2015-12-01 2 views
0

Я новичок в C и хочу выполнить эту задачу: объявить и инициализировать строку, а затем переназначить каждый элемент строки на новое значение.Назначение строки C дает ошибку сегментации

Написание кода таким способом:

char *str = "geeksforgeeks\0"; 

for (int i = 0; str[i] != '\0'; ++i) { 
    str[i] = 'a'; 
} 

бросает ошибку сегментации.

Но если я пишу код таким образом:

char string[] = "geeksforgeeks\0"; 
char *str = string; 

for (int i = 0; str[i] != '\0'; ++i) { 
    str[i] = 'a'; 
} 

программа ведет себя правильно.

Кроме того, этот код:

char str[] = "geeksforgeeks\0"; 

for (int i = 0; str[i] != '\0'; ++i) { 
    str[i] = 'a'; 
} 

ведет себя правильно.

В чем разница между этими двумя? Не должно быть эквивалентно?

+0

Почему они должны быть эквивалентными? Что означает 'str' в первой программе? На что он указывает во втором? – immibis

+0

Больше, чем в прошлом месяце, чем у Джона Скита. –

+0

Вам не нужно добавлять ответ на вопрос - вот что такое раздел ответа ... – immibis

ответ

3

char * str = "geeksforgeeks \ 0";

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

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

char *str = string; 

вы указываете на первый элемент массива - который, как упоминалось, является изменяемым (а также все элементы массива).

* Возможно, они хранятся не в памяти только для чтения, в зависимости от платформы. Но в любом случае вам не разрешается их модифицировать.

+0

Но из того, что я знаю, это должна быть строка только для чтения: "const char * str =" geeksforgeeks \ 0 "; – Alex

+0

@Alex: Это только копируется в массив, и вы можете изменить массив (второй случай). –

+0

Я думаю, что начинаю понимать. – Alex

1

Причина проста.

В первом примере у вас есть указатель на статическую строку. поэтому возникает ошибка сегментации.

char *str = "Test"; 

Это практически постоянная строка. Но во втором примере это переменная, которую вы меняете.

// You have a variable here 
char str_array[] = "Test"; 
// Now you have a pointer to str_array 
char *str = str_array; 
5

Если у вас есть:

char *str = "geeksforgeeks\0"; 

строка (обычно), которые хранятся в памяти только для чтения, и вы получите ошибку сегментации при попытке изменить его. (\0 действительно не требуется, у вас есть два нулевых байта в конце строки.)

Самым простым способом исправить это использовать массив вместо постоянной строки (которая в основном то, что вы делаете во втором рабочем случае):

char str[] = "geeksforgeeks"; 

Обратите внимание, что вы действительно должны использовать это для строки, так как строка не может быть изменена:

const char *str = "geeksforgeeks"; 
+0

Нит-струнные литералы не имеют * * для хранения в памяти только для чтения. Они находятся на общих платформах, но стандарт не требует этого. Попытка изменить строковый литерал вызывает неопределенное поведение. –

1

Вы попали в уродливый унаследованный багаж. Когда вы пишете литерал "geeksforgeeks\0", компилятор превращает это в указатель на массив символов. Если позже вы снова используете строку "geeksforgeeks\0", ей разрешено указывать обе ссылки на один и тот же массив. Это работает только в том случае, если вы не можете изменить массив; в противном случае fputs(stdout, "geeksforgeeks\0"); будет печатать aeeksforgeeks. (Fortran может возглавить это: по крайней мере, на одном компиляторе вы можете передать константу 1 по имени функции, установить ее равной -1, и все ваши петли затем будут работать назад.) С другой стороны, стандарт C не " t сказать, что модификация строковых литералов не будет работать, и есть какой-то старый код. Это неопределенное поведение.

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

Так почему же не строковые литералы const char * вместо char *? Ранние версии C не имели ключевого слова const, и комитет по стандартам не хотел нарушать этот старый код. Тем не менее, вы можете и должны указывать указатели на строковые литералы как const char* s = "geeksforgeeks\0";, поэтому компилятор остановит вас от стрельбы в ногу.

+0

поэтому они пытаются сохранить память? Если я создаю 10000 из «geeksforgeeks», в основном есть только одна копия в памяти, а переменные 10000 указывают на одно и то же? Я прихожу с Java и там у вас что-то похожее: строки являются неизменяемыми объектами, однако, если вы попытаетесь изменить один из них, вы не получите ошибку сегментации, но вы получите новую новую неизменяемую строку. – Alex

+1

Компилятор разрешен, но не требуется, чтобы каждая копия строкового литерала указывала на то же место. C - слишком низкоуровневый язык, чтобы сделать такую ​​копию при модификации. ОС может сделать это для страниц нулей на аппаратном уровне.Вы в значительной степени получаете все недостатки обоих без каких-либо преимуществ: вы не можете быть уверены, что память будет разделяться или нет, или что сравнение «Hello» == «Hello» будет работать или нет, или что оно может или не могут быть изменены. Но вам также не гарантировано получить предупреждение о том, что вы делаете что-то небезопасное. – Davislor

+0

Вот где C становится действительно сумасшедшим ... если вы внесете изменения, а не ожидаете двух простых результатов: работает/не работает, есть три результата: работает/не работает/не определено поведение. – Alex

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