Вы говорите, что это должен быть код C89, но есть некоторые аспекты этого кода, которые не совместимы с C89. Я включил измененную версию вашего кода ниже, которая исправляет эти вещи, компилируется без предупреждений и, кажется, работает правильно. В качестве заявления об отказе от ответственности я никогда не пишу на C89, так что кто-то другой может что-то решить.
Отказ, о котором вы сообщаете, объясняется двойным объявлением fp
, как указано в @BLUEPIXY в комментариях к вашему вопросу. Вы сначала указываете этот указатель на файл в области файла (то есть fp
является «глобальной» переменной), а затем в области блока в пределах функции initialize()
. Второе объявление скрывает первый в блоке, поэтому вы открываете файл в initialize()
и назначаете полученный указатель на область области fp
(которая имеет автоматический срок хранения). Когда main()
затем вызывает readFile()
, это пропускаемый файл fp
, и этот fp
инициализирован NULL
. Таким образом, fseek(fp, 0, SEEK_END)
вызывается указателем NULL
, и это вызывает segfault. Изменение второго заявления на исправление назначения этой проблемы:
fp = fopen(argv[1], "rb");
Вы не можете смешивать объявления переменных и коду в C89, так что вам нужно переместить объявления в начало функции. Кроме того, C89 не поддерживает спецификатор формата %zu
для переменных size_t
. Вместо этого используйте %lu
и введите значение size_t
bytesRead
в (unsigned long)
.
Я добавил определение: ONEMB 1048576
и удалил определение TENMB
. При вычислении размера файла вы делите количество байтов на 10 МБ вместо 1 МБ, и я полагаю, что вы на самом деле пытались вычислить количество МБ.
Я добавил чек на наличие ошибок на звонок до ftell()
. Поскольку ftell()
возвращает long
, я добавил переменную size_t fileSize
и передал значение size
в size_t
, прежде чем назначать его fileSize
. Я изменил printf()
заявление:
printf("File size: %.2f MB\n", (fileSize * 1.0)/ONEMB);
так, что размер файла сообщается с большей точностью; таким образом, файлы размером менее 1 МБ не будут отображаться как имеющие размер 0 МБ.
Я removed the cast от вызова malloc()
, так как он совершенно не нужен в С.
Присваивание bytesRead
вызовов fread()
, который принимает параметр size_t
для третьего аргумента, а не long
.Первоначально у вас было значение long
size
, и это одна из причин новой переменной fileSize
. Следующая строка имела сравнение между bytesRead
, которое равно size_t
и size
. Это должно генерировать предупреждения компилятора (вы их используете, не так ли?), Так как использование подписанных и неподписанных типов в одном выражении может привести к трудностям, и поэтому здесь я снова использовал переменную fileSize
.
Вам не хватает инструкции return
в конце main()
, и в ваших определениях функций отсутствовали спецификаторы типа возвращаемого типа, поэтому я также добавил их. Я также заметил, что ваша программа была segfault при вызове без аргументов. Это связано с тем, что при ошибках в вашей функции initialize()
вы указали return
на вызывающую функцию, которая затем вызывала readFile()
с указателем NULL
. Вместо этого вы должны указать exit()
из программы. Я внес эти изменения во все ловушки ошибок файла в модифицированном коде.
Есть еще некоторые проблемы с вашим кодом. Я бы предпочел не использовать функцию End()
. Он не проверяет ошибки во время закрытия файлов и будет segfault, если вы передадите указатель NULL
. Кроме того, ошибка "Unexpected number of bytes read"
в readFile()
закончится успешно, так как End()
всегда выходит с EXIT_SUCCESS
.
Я не люблю отливку от long
до size_t
, что и я, и у кого-то другой может быть лучший подход. То, что я пытался справиться здесь, было то, что ftell()
возвращает long
, который вы используете для расчета размера файла, и fread()
берет и возвращает size_t
значения.
Большинство из того, что я сделал, было в функции readFile()
, и я удалил ваши комментарии из этой функции, чтобы было легче увидеть, что было изменено.
Я скомпилирован с:
НКУ -std = c89 -Wall -Wextra -pedantic
#include <stdio.h>
#include <stdlib.h> /*added this to provide a declaration for malloc*/
#define ONEMB 1048576
#define ONEB 1
FILE * fp = NULL;
void End(FILE * fp)/*made END a function */
{
fclose(fp); /* close and free the file */
exit(EXIT_SUCCESS); /* or return 0; */
}
int initialize(int argc, char ** argv)
{
/* Open the file given on the command line */
if(argc != 2)
{
printf("Usage: %s filename.mp3\n", argv[0]);
exit(EXIT_FAILURE);
}
fp = fopen(argv[1], "rb");
if(fp == NULL)
{
printf("Can't open file %s\n", argv[1]);
exit(EXIT_FAILURE);
}
return 0; /*this might need to change */
}
int readFile(FILE * fp)
{
long size = 0;
unsigned char *data;
size_t fileSize, bytesRead;
fseek(fp, 0, SEEK_END);
if ((size = ftell(fp)) == -1) {
fprintf(stderr, "ftell() error\n");
exit(EXIT_FAILURE);
};
rewind(fp);
if(size < ONEB || size > 10 * ONEMB)
{
printf("File size is not within the allowed range\n");
End(fp);
}
fileSize = (size_t) size;
printf("File size: %.2f MB\n", (fileSize * 1.0)/ONEMB);
data = malloc(fileSize);
bytesRead = fread(data, sizeof(unsigned char), fileSize, fp);
free(data);
if(bytesRead != fileSize)
{
printf("Error reading file. Unexpected number of bytes read: %lu\n", (unsigned long) bytesRead);
End(fp);
exit(EXIT_FAILURE);
}
return 0;
}
int main(int argc, char ** argv)
{
initialize(argc, argv);
readFile(fp);
/* We now have a pointer to the first byte of data in a copy of the file, have fun
unsigned char * data <--- this is the pointer */
End(fp);
return 0;
}
Вы использовали отладчик? В самом (очень) наименее отладчике сразу сообщит вам, какая строка кода вызывает ошибку seg. – kaylum
Нет, в случае, когда я получаю segfault, я использую файл размером 5 Мб, который находится между этими значениями. –
'TENMB' на самом деле 1MB .. вам нужно добавить 0 – yano