2016-11-09 4 views
-2

Итак, у меня есть проект, над которым я работаю в классе ANSI C (C89). Я застрял в этом процессе. Я получаюОшибка сегментации: 11 в ANSI C (C89)

ошибку сегментации: 11

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

/* CS315 Lab 3: C data types */ 

#include <stdio.h> 
#include <stdlib.h> /*added this to provide a declaration for malloc*/ 

#define TENMB 1048576 /*1024 kilobytes or 10 megabytes */ 
#define ONEB 1 

FILE * fp = NULL; 

End(FILE * fp)/*made END a function */ 
{ 
    fclose(fp);    /* close and free the file */ 
    exit(EXIT_SUCCESS);  /* or return 0; */ 
} 

initialize(int argc, char ** argv) 
{ 
    /* Open the file given on the command line */ 
    if(argc != 2) 
    { 
     printf("Usage: %s filename.mp3\n", argv[0]); 
     return(EXIT_FAILURE); 
    } 
    FILE * fp = fopen(argv[1], "rb"); 
    if(fp == NULL) 
    { 
     printf("Can't open file %s\n", argv[1]); 
     return(EXIT_FAILURE); 
    } 
    return 0; /*this might need to change */ 
} 

readFile(FILE * fp) 
{ 
    /* How many bytes are there in the file? If you know the OS you're 
    on you can use a system API call to find out. Here we use ANSI 
    standard function calls. */ 
    long size = 0; 
    fseek(fp, 0, SEEK_END);  /* go to 0 bytes from the end */ 
    size = ftell(fp);    /* how far from the beginning? */ 
    rewind(fp);      /* go back to the beginning */ 

    if(size < ONEB || size > TENMB) 
    { 
     printf("File size is not within the allowed range\n"); 
     End(fp); /* switched from goto END:*/ 
    } 

    printf("File size: %.2ld MB\n", size/TENMB); /* change %d to %ld, added .2 to print to 2 decimal places (maybe use f instead) */ 
    /* Allocate memory on the heap for a copy of the file */ 
    unsigned char * data = (unsigned char *)malloc(size); 
    /* Read it into our block of memory */ 
    size_t bytesRead = fread(data, sizeof(unsigned char), size, fp); 
    free(data); /* deallocation */ 
    if(bytesRead != size) 
    { 
     printf("Error reading file. Unexpected number of bytes read:  %zu\n",bytesRead); /* changed from %d to %zu */ 
     End(fp); /* switched from goto END:*/ 
     return 0; 
    } 
    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 */ 
} 

Спасибо за помощь!

+2

Вы использовали отладчик? В самом (очень) наименее отладчике сразу сообщит вам, какая строка кода вызывает ошибку seg. – kaylum

+0

Нет, в случае, когда я получаю segfault, я использую файл размером 5 Мб, который находится между этими значениями. –

+3

'TENMB' на самом деле 1MB .. вам нужно добавить 0 – yano

ответ

0

Вы говорите, что это должен быть код 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_tbytesRead в (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.Первоначально у вас было значение longsize, и это одна из причин новой переменной 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; 
} 
+0

'% lu' не является правильным спецификатором для' size_t'. Перед печатью выведите результат до 'unsigned long' или вы вызовете неопределенное поведение. Например, 'long' на 64-битной Windows не такой же размер, как' size_t' –

+0

@ Lưu Vĩnh Phúc-- Конечно! Я пропустил это, но теперь это исправлено. Я не привык к C89, поэтому, если вы видите что-то еще, что я забыл, прокомментируйте. –

0

В своем коде вы объявили Ф.П. дважды, как на глобальном и местном

FILE * fp = NULL; 

End(FILE * fp)/*made END a function */ 
{ 
    fclose(fp);    /* close and free the file */ 
    exit(EXIT_SUCCESS);  /* or return 0; */ 
} 

initialize(int argc, char ** argv) 
{ 
... 
    FILE * fp = fopen(argv[1], "rb"); 
... 

Несмотря на то, что вы пишете C89, нет необходимости собирать плохие вещи со стандартом. Например. правильно объявлять функции с типом возврата.

Смежные вопросы