#c #segmentation-fault
#c #ошибка сегментации
Вопрос:
я не могу понять, почему я получаю ошибку сегментации из этого кода, и каким-то образом, когда я использую массивы вместо указателей, это работает, я был бы рад, если кто-нибудь сможет помочь мне понять это.
void main() {
char *str = "example string";
wrapChrInStr(str, 'a');
}
void wrapChrInStr(char *str, unsigned char chr) {
char *ptr = str;
char c;
while((c = *ptr)) {
if(c != chr) {
*str = c;
str ;
ptr ;
} else {
ptr ;
}
}
*str = '';
}
Комментарии:
1.
str
указывает на память только для чтения,*str = '';
произойдет сбой с segfault. Вы можете объявитьstr
какchar str[] = "..."
или с фиксированным размеромchar str[50] = "...."
2. спасибо за ваш ответ. Почему это доступно только для чтения, можете ли вы объяснить, пожалуйста?
3. Потому что
"example string"
это строковый литерал , и они обычно хранятся в разделе, доступном только для чтения.4. Спасибо. Я много программирую на c, и действительно странно, что я никогда не сталкивался с этим раньше.
Ответ №1:
Спасибо. Я много программирую на c, и действительно странно, что я никогда не сталкивался с этим раньше.
Вероятно, потому, что вы не понимаете, что существуют разные способы хранения C-строки. Возможно, вам повезло, что из-за этого вы никогда не сталкивались с segfault.
Строковые литералы
Строковый литерал объявляется с двойными кавычками, например
"hello world"
Эта строка обычно хранится в разделе, доступном только для чтения. При использовании string
литералы, лучше всего объявлять переменные с const
таким образом:
const char *str = "hello world";
При этом вы знаете, что str
указывает на ячейку памяти, доступную только для чтения, и вы не можете
манипулируйте содержимым строки. На самом деле, если вы сделаете это:
const char *str = "hello world";
str[0] = 'H';
// or the equivalent
*str = 'H'
компилятор вернет ошибку, подобную этой:
a.c:5:5: error: assignment of read-only location ‘*str’
что я нашел очень полезным, потому что вы не можете случайно манипулировать
содержимое, на которое указывает str
.
Массивы
Если вам нужно манипулировать содержимым строки, то вам нужно сохранить строку в массиве, например
char str[] = "hello word";
В этом случае компилятор знает, что строковый литерал содержит 10 символов и резервирует 11 байт (1 байт для ''
— завершающего байта) для str
и инициализирует массив с помощью
содержимое строкового литерала.
Здесь вы можете делать такие вещи, как
str[0] = 'H'
но вы не можете получить доступ дальше 11-го байта.
Вы также можете объявить массив с фиксированным размером. В этом случае размер должен быть по крайней мере таким же, как длина 1 строкового литерала.
char str[11] = "Hello world";
Если вы объявите меньше места ( char str[3] = "hello world";
например),
ваш компилятор предупредит вас чем-то вроде этого
a.c:4:14: warning: initializer-string for array of chars is too long
но я не уверен, что произойдет, если вы все равно выполните код. Я думаю, что это случай неопределенного поведения
и это означает: случиться может все, что угодно.
Лично я обычно объявляю свою строку без фиксированного размера, если только нет причины для фиксированного размера.
Комментарии:
1. «В этом случае размер должен быть по крайней мере таким же, как длина 1 строкового литерала». — 1 не применяется, вы даже показали это в своем примере (в инициализаторе
char str[11]
есть 11 ненулевых символов).2. Да, но для правильной программы это верно. Потому что строка без символа завершения приведет к неопределенному поведению