2009-03-05 4 views
4

У меня есть следующий код:Почему это простое назначение строк segfault?

#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; 
} 

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

#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; 
} 

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

ответ

9

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

См. Также этот вопрос: Is a string literal in c++ created in static memory?.

+0

Также обратите внимание, что ваш код дает предупреждения о компиляторе, которые объясняют эту проблему: «предупреждение: устаревшее преобразование из константы строки в« char * »» – schnaader

+0

g ++ -Wall -pedantic 4.01 не было, что я включил, чтобы получить предупреждение? – zoul

+0

Использование g ++ 4.3.2 здесь, на Debian, параметры не имеют значения, достаточно просто вызвать «g ++ test.cpp», добавив -Wall, -pedantic или оба ничего не изменили. Кажется, это предупреждение было добавлено где-то после 4.1/4.2. – schnaader

6

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

char *b = "bar"; 

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

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

-1

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

2

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

char *foo = "bar"; 

Что происходит на самом деле является то, что «бар» хранится в режиме только для чтения сегмента памяти. Поэтому он неизменен. Вы получаете segfault, потому что пытаетесь изменить сегмент, доступный только для чтения.

2

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

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

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

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

1

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

 Смежные вопросы

  • Нет связанных вопросов^_^