Почему это простое строковое назначение segfault?

StackOverflow https://stackoverflow.com/questions/614094

  •  03-07-2019
  •  | 
  •  

Вопрос

Я & # 8217; у меня есть следующий код:

#include <iostream>
using namespace std;
int main()
{
    char* a = "foo";
    char* b = "bar";
    a = b;
    cout << a << ", " << b << endl;
    return 0;
}

Это компилирует и работает, т.е. печатает bar, bar. Теперь я хотел бы продемонстрировать, что здесь происходит не копирование строки. Я хотел бы изменить b и показать, что a также меняется. Я придумал этот простой код:

#include <iostream>
using namespace std;
int main()
{
    char* a = "foo";
    char* b = "bar";
    a = b;
    b[1] = 'u'; // ← just this line added
    cout << a << ", " << b << endl;
    return 0;
}

& # 8230; но это ошибка. Зачем? Интересно, что следующая модификация работает нормально:

#include <iostream>
using namespace std;
int main()
{
    char* a = "foo";
    char b[] = "bar"; // ← declaration changed here
    a = b;
    b[1] = 'u';
    cout << a << ", " << b << endl;
    return 0;
}

Почему не & # 8217; это не так, как в предыдущем? Я предполагаю, что пропускаю какое-то важное различие между стилем указателя и инициализацией строки в стиле массива.

Это было полезно?

Решение

Вы не можете изменять строковые константы, что вы получаете, когда используете синтаксис указателя на литерал, как в первых примерах кода.

См. также этот вопрос: является строковым литералом в c ++ создан в статической памяти? .

Другие советы

Когда вы пишете это:

char *b = "bar";

компилятор выделяет анонимную (безымянную) область памяти для хранения строкового литерала " bar " ;. Строковые литералы не могут быть изменены, поэтому компилятор (с помощью компоновщика и операционной системы) помещает строковый литерал в часть пространства памяти работающей программы, которая защищена от записи. Когда вы пытаетесь изменить его, операционная система ловит его и вызывает ошибку сегментации вашей программы.

(Ваш код - C ++, а не C, но это не имеет отношения к этому вопросу.)

Когда вы пишете:

char *foo = "bar";

На самом деле происходит то, что " bar " хранится в сегменте памяти только для чтения. Следовательно, он неизменен. Вы получаете segfault, потому что вы пытаетесь изменить сегмент только для чтения.

Вы также можете показать, что «а» было изменено, напечатав значение указателя.

#include <iostream>
using namespace std;
int main()
{
    char* a = "foo";
    char* b = "bar";
    a = b;

    cout << (void*)a << ", " << (void*)b << endl;
}

Это напечатает адрес, на который указывают 'a' и 'b'.
Вы должны привести к void *, потому что оператор & Lt; & Lt; перегружен для 'char *', чтобы распечатать строку, любой другой указатель напечатает адрес.

Теоретически, строковый литерал не должен быть назначен на char *, только на const char *. Тогда компилятор остановит вас, прежде чем вы напишете код ошибки seg.

Эта разница, возможно, зависит от компилятора. Чтобы продемонстрировать свою точку зрения, используйте malloc для выделения буфера, затем скопируйте строку в этот буфер и не забудьте использовать free, когда строка больше не нужна.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top