Странная ошибка сегментации (разница между массивами и указателями?) - PullRequest
0 голосов
/ 22 марта 2019

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

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 = '\0';
}

1 Ответ

1 голос
/ 22 марта 2019

Спасибо.Я много занимаюсь программированием на С, и это действительно странно, что я никогда с этим не сталкивался.

Возможно, потому что вы не понимаете, что существуют разные способы хранения C-String.Возможно, вам повезло, что вы никогда не сталкивались с segfault из-за этого.

Строковые литералы

Строковый литерал объявляется с двойными кавычками, например

"hello world"

Эта строка обычно хранится в разделе только для чтения.При использовании строковых литералов лучше всего объявлять переменные с 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’

, что я нашел очень полезным, потому что вы не можете случайно манипулировать содержимым, на которое указываетby str.

Массивы

Если вам нужно манипулировать содержимым строки, вам нужно сохранить строку в массиве, например,

char str[] = "hello word";

В этом случае компилятор знает, что строковый литерал имеет 10 символов, и резервирует 11 байтов (1 байт для '\0' - завершающий байт) для 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

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

Лично я обычно объявляю свою строку без фиксированного размера, если только нет причины иметь фиксированный размер.

...