Функция C — realloc() — защита от повреждения данных

#c #memory #dynamic #allocation #realloc

#c #память #динамический #выделение #перераспределение

Вопрос:

Посмотрите, что я нашел с помощью этого простого кода:

 #include <stdio.h>
#include <stdlib.h>
#include <string.h>

char *string;

int main(){    

string = (char *) malloc(50*sizeof(char));
strcpy(string, "initi1l wording cont2ining forty-nine ch3r4cters.");
printf("BEFORE: %sn", string);
string = (char *) realloc(string, 24*sizeof(char));
printf("AFTER: %sn", string);

system("PAUSE");

return 0;
}
 

Выход:

 BEFORE: initi1l wording cont2ining forty-nine ch3r4cters.
AFTER: initi1l wording cont2inia
 

Обратите внимание на «a» в конце строки! Я понятия не имею, откуда это взялось, может быть, где-то в куче. Это не из исходного блока данных. Изначально я использовал realloc() с массивами структур, и это, очевидно, искажало данные более значительными способами.

Как я могу обойти эту проблему?

Комментарии:

1. Ну, я мог бы спросить позже о том, как я могу обойти свою проблему, см. Мой ответ на ответ aix.

2. Что ж, возможно, вопросов нет, но ответ очевиден: перераспределение не нарушено.

Ответ №1:

Для строк C требуется NUL терминатор.Вы неявно ожидаете realloc() каким-то образом выяснить, что память содержит строку C, и заменить ее последний символ на NUL . Он этого не делает; вы должны сделать это самостоятельно:

 string = (char *) realloc(string, 24*sizeof(char));
string[23] = 0;   // <========= THE FIX
printf("AFTER: %sn", string);
 

Другими словами, это ошибка в вашем коде.

Комментарии:

1. Да, верно. Спасибо, что указали на это, но… Когда я сжимал массив структур до меньшего блока, я ожидал, что последние элементы придут к концу (и не нужно было указывать завершающий символ NULL). То, что произошло, было полным повреждением.

Ответ №2:

Это не так! В C «Строка» — это набор символов, разделенных символом . В этом случае вы пытаетесь напечатать «string», поэтому вы получаете исходные 24 символа и некоторый хвост, пока в памяти не будет найден random 0

Комментарии:

1. Хорошо, ребята. Спасибо. Я вернусь, когда смогу доказать, как realloc добавляет случайные данные в мой массив структур. (Я согласен, что забыл значение NULL в примере со строкой).

2. Если вы используете realloc для увеличения размера, часть за старым содержимым содержит неопределенные байты, как указано стандартом. Если вы используете его для уменьшения размера, память за новым размером — это просто любая случайная ячейка памяти для вашей программы, она может содержать что угодно, доступ к ней недопустим. Что на самом деле происходит (в этом случае другие реализации могут вести себя по-другому), так это то, что realloc перезаписывает первые несколько байтов, чтобы пометить их как свободную память.

Ответ №3:

Строки в C завершаются нулем. Я удивлен, что программа не вылетела.

Комментарии:

1. Почему это должно быть? После выделенного блока может быть, например, размер следующего блока, который, если он короткий, имеет нулевой старший байт 😉

Ответ №4:

Это символ 25, и у вас нет 0-завершения в первых 24.