Попытка отладить ошибку сегментации программы c, используя вывод из valgrind - PullRequest
4 голосов
/ 19 августа 2011

Моя CLI-программа компилируется и отлично работает на Windows. Прекрасно компилируется в Linux, но вызывает ошибку сегментации при запуске.

Я обратился за помощью к stackoverflow и обнаружил несколько вопросов, похожих на то, что я собираюсь задать, с предложенным предложением valgrind, который я только что установил (woo!).

Итак, я запустил свою программу через valgrind и получил удручающе большое количество выходных данных, но я начну с первого сообщения об ошибке:

==11951== Command: ./vt
==11951== 
Loading...
Load default database? (y/n)y
Opened input file vtdb.~sv, reading contents...
==11951== Invalid write of size 1
==11951==    at 0x400FA9: readnumberfromfile (in /home/rob/Documents/programming/c/vocabtest/vt)
==11951==    by 0x400C21: getrecordsfromfile (in /home/rob/Documents/programming/c/vocabtest/vt)
==11951==    by 0x401FFD: main (in /home/rob/Documents/programming/c/vocabtest/vt)
==11951==  Address 0x53b05bb is 0 bytes after a block of size 11 alloc'd
==11951==    at 0x4C28FAC: malloc (vg_replace_malloc.c:236)
==11951==    by 0x400EAC: readnumberfromfile (in /home/rob/Documents/programming/c/vocabtest/vt)
==11951==    by 0x400C21: getrecordsfromfile (in /home/rob/Documents/programming/c/vocabtest/vt)
==11951==    by 0x401FFD: main (in /home/rob/Documents/programming/c/vocabtest/vt)
==11951== 
...finished.
1180 entries read from vtdb.~sv.

Проблема, кажется, в readnumberfromfile, и я просмотрел ее, и я не могу найти, что с ней не так!

Может кто-нибудь пролить свет?

int readnumberfromfile (int maxvalue,char separator)
{
    int number, i=0;
    char ch;
    char * buff = (char *)malloc(11);//allocate enough space for an 10-digit number and a terminating null
    if (!buff) {printf("Memory allocation failed!\n");return 0;}//return 0 and print error if alloc failed
    if (!maxvalue) maxvalue=MAXINTVALUE;

    ch=getc(inputfile);
    while (!isdigit(ch))
    {
        if (ch == separator||ch=='\n'||ch==EOF) {fprintf(stderr,"Format error in file\n");return 0;}//if no number found(reached separator before digit), print error and return 0
        ch = getc(inputfile);//cycle forward until you reach a digit
    }
    while (i<11 && ch!=separator && ch!='\n')//stop when you reach '~', end of line, or when number too long
    {
        buff[i++]=ch;
        ch = getc(inputfile); //copy number from file to buff, one char at a time
    }
    buff[i] = '\0';//terminate string
    number = atoi(buff)<=maxvalue ? atoi(buff) : maxvalue;//convert string to number and make sure it's in range
    free(buff);
    return number;
}

Вызывается из getrecordsfromfile, если это имеет какое-либо значение:

void getrecordsfromfile(char * inputfilename,char separator)
{
    int counter = 0;
    struct vocab * newvocab;
    struct listinfo * newvocablist;
    if (!(inputfile = fopen(inputfilename, "r")))
    {
        printf("Unable to read input file. File does not exist or is in use.\n");
    }    
    else
    {
        printf("Opened input file %s, reading contents...\n",inputfilename);
        while (!feof(inputfile))
        {
            newvocab = (struct vocab *)malloc(sizeof(struct vocab));
            if (!newvocab)
            {
                printf("Memory allocation failed!\n");
                return;
            }
            else
            {
                newvocab->question=readtextfromfile(MAXTEXTLENGTH,separator);
                newvocab->answer=readtextfromfile(MAXTEXTLENGTH,separator);
                newvocab->info=readtextfromfile(MAXTEXTLENGTH,separator);
                newvocab->hint=readtextfromfile(MAXTEXTLENGTH,separator);
                newvocab->right=readnumberfromfile(1,separator);
                newvocab->counter=readnumberfromfile(0,separator);
                newvocab->known=readnumberfromfile(3,separator);

                switch (newvocab->known)
                {
                    case 0: newvocablist = &n2l;break;
                    case 1: newvocablist = &norm;break;
                    case 2: newvocablist = &known;break;
                    case 3: newvocablist = &old;break;
                }

                addtolist(newvocab,newvocablist);
                if (newvocab->question==NULL||newvocab->answer==NULL)
                {
                    printf("Removing empty vocab record created from faulty input file...\n");
                    removefromlist(newvocab,newvocablist,1);
                }
                else counter++;
            }
        }
        fclose(inputfile);
        printf("...finished.\n%i entries read from %s.\n\n",counter,inputfilename);
    }
    return;
}

Полный источник можно получить с https://github.com/megamasha/Vocab-Tester

Пара замечаний: я пытаюсь помочь себе, я провел свое исследование, посмотрел на похожие вопросы и сам узнал о Вальгринде.

Я все еще относительный новичок, и хотя я ценю решения (ЧТО делать, чтобы исправить это), но еще полезнее знание (КАК исправить это или избежать этого в следующий раз). Я здесь (и очень хочу) учиться.

Ответы [ 4 ]

7 голосов
/ 19 августа 2011

buff[i] = '\0';//terminate string

здесь i == 11, так как вы выделили только 11 символов, а условие заканчивается, когда i = 11.
Итак, вы получаете доступ к памяти, которую вы не выделяли.

поведение в этой ситуации не определено.

Вы можете решить эту проблему, выделив один дополнительный символ на malloc.

3 голосов
/ 19 августа 2011
int number, i=0;
...
while (i<11 ...

Вы читаете до одиннадцати цифр для i = 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 и 10. А затем пытаетесь засунуть \0 в двенадцатый слотbuff[11].

Это называется " отключено одной ошибкой ".

Таким образом, исправление зависит от того, что вы хотите изменить.Если вы хотите принять 11 символов, измените malloc баффа.Если вы хотите принять только 10, измените условие while.

2 голосов
/ 19 августа 2011

Недопустимая запись размером 1

Вы, вероятно, пишете символ

Адрес 0x53b05bb равен 0 байтов после блока размера 11 alloc'd

Вы только что переполнили что-то размером 11

Оба в readnumberfromfile

Это подозрительно связано (по размерам):

char * buff = (char *)malloc(11);

Это будет сделано с i = 11 после цикла, который находится после окончания выделения:

buff[i] = '\0'

Как говорит wormsparty, вы можете сделать Valgrind более полезным, получив отладкусимволы в вашем двоичном файле.

1 голос
/ 19 августа 2011

В дальнейшем, если вы скомпилируете с -g, valgrind точно покажет вам, на какой строке произошла ошибка.

...