Я изначально неверно истолковал вывод valgrind, поэтому @ chux заслуживает одобрения.Я постараюсь составить лучший ответ, какой только смогу.
Проверка ошибок
Первая ошибка (которую я не сразу учел) - это проверка значения, возвращенного fopen(3)
с ferror(3)
.Вызов fopen(3)
возвращает NULL
при ошибке (и устанавливает errno
), поэтому проверка NULL
с помощью ferror(3)
неверна.
Сериализация структуры файла.
При инициализации вы пишете все поля вашей структуры, но не инициализируете всю память, которую она покрывает.Ваш компилятор может, например, оставить некоторые отступы в структуре, чтобы повысить производительность при доступе к данным.Когда вы пишете всю структуру файла, вы фактически передаете неинициализированные данные в функцию fwrite(3)
.
Изменяя размер массива, вы изменяете поведение Valgrind.Вероятно, это связано с тем, что компилятор изменяет расположение структуры в памяти и использует другой отступ.
Попробуйте стереть переменную rec
с memset(&rec, 0, sizeof(rec));
, и Valgrind должен прекратить жаловаться.Это только исправит симптом : поскольку вы сериализуете двоичные данные, вы должны пометить struct record
знаком __attribute__((packed))
.
Инициализация памяти
Ваша первоначальная инициализация в порядке.
Альтернативный способ инициализации данных - использовать strncpy(3)
.Strncpy примет в качестве параметров указатель на место назначения для записи, указатель на блок памяти источника (откуда данные должны быть взяты) и доступный размер записи.
Используя strncpy(&rec.text, "hello world", sizeof(rec.text)
, вы пишете "hello world"msgstr "над буфером rec.text
.Но вы должны обратить внимание на завершение строки: strncpy
не будет писать за пределами заданного размера, а если длина исходной строки больше этого, никакого строкового терминатора не будет.
Strncpyможно безопасно использовать следующим образом
strncpy(&rec.text, "hello world", sizeof(rec.text) - 1);
rec.text[sizeof(rec.text) - 1] = '\0';
Первая строка копирует «hello world» в целевую строку.sizeof(rec.text) - 1
передается как размер, так что мы оставляем место для терминатора \0
, который записывается явно как последний символ, чтобы охватить случай, в котором sizeof(rec.text)
короче, чем "hello world".
Nitpicks
Наконец, уведомления об ошибках должны идти на stderr
, тогда как stdout
для результатов.