Intereting Posts
Как создать компилятор C для пользовательского ЦП? Как обрабатывать исключение, когда scanf integer получает символ Освобождение строк в C Какова эпоха CLOCK_TAI? Изменить строковый литерал в C-указателе? Как правильно делить десятичное значение тонального изгиба MIDI на 2 разделенных 7-битных значения? использование бесплатных для хоста результатов в результате сегментации Что происходит с внешней встроенной функцией? Повреждение кучи – ошибка отладки не удалась. в выражении dbgheap.c 1322 выражение _crtIsValidHeapPointer (pUserData) Что должно было бы сделать strlen () действительно в этом коде? Есть ли способ предсказать неопределенное поведение или поведение, определяемое реализацией? Использование crypt_r в OS X многомерные массивы и переносные буферы Получить максимальное значение переменной в C Использует ли strlen () условие цикла медленнее, чем просто проверка нулевого символа?

C fread () магически читает динамически распределенные члены структуры, как?

Это тестовая программа, которую я написал для более крупного проекта, над которым я работаю. Это связано с записью данных структуры на диск с помощью функции fwrite (), а затем чтения этих данных с помощью fread (). Один член структуры динамически распределяется.

Во-первых, вот мой код

#include  #include  #include  #define STRING_LEN 128 struct Person { int age; char *name; }; int main(int argc, const char *argv[]) { struct Person *person = calloc(1, sizeof(struct Person)); person->age = 22; person->name = calloc(STRING_LEN, sizeof(char)); char *name = "Name that is really, really, really, really, really, really, long."; strncpy(person->name, name, STRING_LEN); FILE *out_file = fopen("rw.out", "w"); fwrite(person, sizeof(struct Person), 1, out_file); fclose(out_file); FILE *in_file = fopen("rw.out", "r"); struct Person *person_read = calloc(1, sizeof(struct Person)); fread(person_read, sizeof(struct Person), 1, in_file); fclose(in_file); printf("%d %s\n", person_read->age, person_read->name); free(person->name); free(person); free(person_read); return 0; } 

И изгибы

 22 Name that is really, really, really, really, really, really, long. 

Мой вопрос: почему это работает? Не следует ли fwrite () записывать только адрес, который содержит «имя» (т. Е. Адрес начала строки)? То есть, я передаю sizeof (struct Person) в fwrite (), и все же он пишет строку, на которую указывает ‘name’.

Еще более запутанным для меня является поведение fread (). Опять же, если я передаю sizeof (struct Person), как читается фактическая ценность «имени»? Как память для него выделяется?

Мое предыдущее понимание того, как использовать fwrite () + fread (), состояло в том, что мне пришлось бы «вручную» записывать данные, на которые указывала «имя», «вручную» читать эти данные, а затем копировать эту строку после выделения памяти для как структура, так и член «имя». Другими словами, мне нужно было бы пройти любые указатели, записать данные, а затем прочитать эти данные в том же порядке.

EDIT : Дэн и другие верны. Я просмотрел выходной файл с помощью xxd:

 0000000: 1600 0000 0000 0000 30a0 d900 0000 0000 ........0....... 

Если я распечатаю адрес, который «имя» содержит перед записью, и после прочтения он будет таким же (0xd9a030), который соответствует выходу из xxd.

Вы записываете данные в struct, который является int, за которым следует указатель на строку. Это всего лишь данные, как и все остальное, и вы знаете, как долго это происходит, потому что struct – фиксированная длина – int плюс указатель. Вы читаете тот же указатель на ту же строку имени, что и оригинал. Само название не написано и не читается.

person->name person_read->name person->name и person_read->name указывая на то же место памяти. Поскольку вы не person_read->name прежде чем читать файл, значение указателя в имени person_read->name остается в силе.

Если вы person->name или прочитали файл из другой программы, значение указателя больше не будет действительным, и попытка ссылаться на него вызовет неопределенное поведение – вы либо распечатали тарабарщину, либо получили segfault.

Указатель * остается действительным в течение всех вызовов fwrite и fread, которые кажутся вам случайными. Если вы выберете (person-> name) перед printf, вы получите результат или ошибку, ожидающие вас.