C语言中使用glibc的realloc函数时出现“无效指针”错误。

3
这是一个关于学校的项目。 我编写了一个名为file_to_array_d的函数,将所有字符放入一个数组中,从而方便地操作文件。
程序根据用户的读取进行一些文件处理。目前只有“V”和“R”选项可用。“V”将打印文件内容。
在主循环(main()函数中的循环)第一次运行时一切正常。然而,在第二次运行时,我遇到了glibc错误。
*** glibc detected *** ./a.out: realloc(): invalid pointer: 0x00007f01ad4397f8 ***
======= Backtrace: =========
/lib/x86_64-linux-gnu/libc.so.6(+0x7eb96)[0x7f01ad0ffb96]
/lib/x86_64-linux-gnu/libc.so.6(realloc+0x2de)[0x7f01ad10495e]
./a.out[0x400ec9]
./a.out[0x400d3d]
./a.out[0x4009b1]
./a.out[0x40090c]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xed)[0x7f01ad0a276d]
./a.out[0x400819]
======= Memory map: ========
00400000-00402000 r-xp 00000000 08:05 3543576                            /home/hork/Dropbox/FIIT/PPR/projekt/a.out
00601000-00602000 r--p 00001000 08:05 3543576                            /home/hork/Dropbox/FIIT/PPR/projekt/a.out
00602000-00603000 rw-p 00002000 08:05 3543576                            /home/hork/Dropbox/FIIT/PPR/projekt/a.out
01318000-01339000 rw-p 00000000 00:00 0                                  [heap]
7f01ace6b000-7f01ace80000 r-xp 00000000 08:05 4722212                    /lib/x86_64-linux-gnu/libgcc_s.so.1
7f01ace80000-7f01ad07f000 ---p 00015000 08:05 4722212                    /lib/x86_64-linux-gnu/libgcc_s.so.1
7f01ad07f000-7f01ad080000 r--p 00014000 08:05 4722212                    /lib/x86_64-linux-gnu/libgcc_s.so.1
7f01ad080000-7f01ad081000 rw-p 00015000 08:05 4722212                    /lib/x86_64-linux-gnu/libgcc_s.so.1
7f01ad081000-7f01ad236000 r-xp 00000000 08:05 4723475                    /lib/x86_64-linux-gnu/libc-2.15.so
7f01ad236000-7f01ad435000 ---p 001b5000 08:05 4723475                    /lib/x86_64-linux-gnu/libc-2.15.so
7f01ad435000-7f01ad439000 r--p 001b4000 08:05 4723475                    /lib/x86_64-linux-gnu/libc-2.15.so
7f01ad439000-7f01ad43b000 rw-p 001b8000 08:05 4723475                    /lib/x86_64-linux-gnu/libc-2.15.so
7f01ad43b000-7f01ad440000 rw-p 00000000 00:00 0 
7f01ad440000-7f01ad462000 r-xp 00000000 08:05 4723489                    /lib/x86_64-linux-gnu/ld-2.15.so
7f01ad63f000-7f01ad642000 rw-p 00000000 00:00 0 
7f01ad65c000-7f01ad662000 rw-p 00000000 00:00 0 
7f01ad662000-7f01ad663000 r--p 00022000 08:05 4723489                    /lib/x86_64-linux-gnu/ld-2.15.so
7f01ad663000-7f01ad665000 rw-p 00023000 08:05 4723489                    /lib/x86_64-linux-gnu/ld-2.15.so
7fffe5f17000-7fffe5f38000 rw-p 00000000 00:00 0                          [stack]
7fffe5fff000-7fffe6000000 r-xp 00000000 00:00 0                          [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]
Aborted (core dumped)

我正在使用Ubuntu 12.04系统,代码是用gcc编译的。

gcc (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3

以下是完整的源代码,注意其中一些注释为斯洛伐克语,但重要内容为英语。

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <execinfo.h> 

#define DEBUG
#define DETAILED_DEBUG

#define FILE_RELATIVE_PATH "ucet.txt"

void dump(char **p, int n);
char ** output_file();
char ** file_to_array_d(FILE * fd, long * rows);
void print_trace ();
void * xrealloc (void *ptr, size_t size);
void fatal(char *message);
void biggest_credit();


int main(int argc, char const *argv[])
{

    char argument;

    char ** p = NULL;
    double * suma = NULL; 

    int open = 0;
    while( (argument = getchar()) != 'K')
    {
        if (argument == 'V') // vypise na obrazovku obsah suboru
        {
            output_file();
            open = 1;
        }
        else if (argument == 'n') // aktualizuje polia
        {
            //number_of_records = parse_file(cena, pocet_dni);
        }
        else if (argument == 'l') // najlacnejsia cena
        {
            //calculate_lowest_price(cena, pocet_dni, number_of_records);
        }
        else if (argument == 'h') // histogram
        {
            //histogram(cena, pocet_dni, number_of_records);
        }
        else if (argument == 'R') // najvyssia cena za posledny rok
        {
            if (open)
                biggest_credit();
        }
    }

    return 0;
}

char ** output_file()
{
    FILE * f = fopen(FILE_RELATIVE_PATH, "r");
    if (f == 0)
    {
        printf("Neotvoreny subor\n");
        return 0;
    }

    long size = 0;

    char ** p = NULL;
    p = file_to_array_d(f, &size);

    //dump(p, size);

    printf("Size: %li\n", size );
    int i, r = 0;
    for (i = 0, r = 0; r < (int)size; i++, r++)
    {
        switch(i)
        {
            case 0:
                printf("transakcia: %s\n", p[r]);
                break;
            case 1:
                printf("kredit/debet: %s\n", p[r]);
                break;
            case 2:
                printf("cislo uctu kam/odkial idu peniaze: %s\n", p[r]);
                break;
            case 3:
                printf("suma: %s\n", p[r]);
                break;
            case 4:
                printf("datum: %s\n", p[r]);
                break;
            case 5:
                printf("\n");
                i = -1;
                break;
        }   
    }

    fclose(f);

    return p;
}


void biggest_credit()
{
    FILE * f = fopen(FILE_RELATIVE_PATH, "r");
    if (f == 0)
    {
        return;
    }

    long size = 0;

    char ** p = file_to_array_d(f, &size);

    double max = 0;
    int max_index;
    int i;

    // suma je 4. riadok, takze index 3 je prva suma
    for (i = 3; i < size; i += 6)
    {
        if (  (double)atof(p[i]) > max && atoi(p[i-2]) == 1 )
        {
            max = (double)atof(p[i]);
            max_index = i;
        }
    }

    if (max)
    {
        printf("%s\n", p[max_index]);
    }
}


/*
 *  Parameters:
 *      FILE *  - pointer to the file handle
 *      long *  - pointer to adress, where the number of rows is written
 *
 *  Returs:
 *      char *  - pointer to the start of array containing file
 *
 *  Info:
 *      
 */
char ** file_to_array_d(FILE * fd, long * rows)
{

    char c;                 // current char being read from file
    int current_row = 0;    // starting from first row
    int current_pos = 0;


    // whole file will be stored in p[][]
    // allocate first row
    #ifdef DEBUG
                printf("calling (char**) malloc( %i );\n", 1 * (int)sizeof(char *) );
    #endif
    char **p = (char **) malloc(1 * sizeof(char *));


    // until EOF is reached
    for(current_pos = 0; ; current_pos++)   
    {

        if (EOF == fscanf(fd, "%c", &c))
        {
            if (p[current_row] == 0)
            {
                // un-allocate space
                p = (char**) xrealloc(p, ((current_row) * sizeof(char *)) );
            }

            break;
        }

        #ifdef DETAILED_DEBUG
            printf("row: %2i pos: %2i\n",current_row, current_pos );
            printf("  read char: '%c'\n\n", c);
        #endif

        // if char was read and it is not EOF
        // allocate new space for char
        #ifdef DEBUG
            printf("calling (char*) xrealloc(p[%i ], %i);\n",current_row, current_pos+1);
        #endif

        p[current_row] = (char*) xrealloc(p[current_row], current_pos+1);

        if (c != '\n')
        {
            // place read char there
            p[current_row][current_pos] = c;
        }
        else // if new line
        {

            // place end of string there
            p[current_row][current_pos] = '\0';

            #ifdef DATAILED_DEBUG
                printf("\tp[%i] = '%s' \n", current_row, p[current_row]);
            #endif

            // allocate and increment new row
            current_row++;

            #ifdef DEBUG
                printf("calling (char**) xrealloc(p, %i);\n", (current_row + 1) * (int)sizeof(char *) );
            #endif
            p = (char**) xrealloc(p, ((current_row + 1) * sizeof(char *)) );    

            // set to -1, it will be zero in the next run of loop
            current_pos = -1;   


            #ifdef DEBUG
                int i;
                for (i = 0; i < current_row; ++i)
                {
                    printf("p[%2i] is at: %p size: %4li contains: %s\n",i, &p[i], sizeof(*p[i]) ,p[i] );
                }
                printf("\n");
            #endif
        }
    }

    // if EOF is in the position: '\n EOF'
    // right way to end file
    if( p[current_row] == 0)
    {
        #ifdef DEBUG
            printf("%s\n", "Subor je spravne ukonceny n.1 :)");
        #endif
        *rows = current_row;
    }
    // if EOF is right behind line
    // wrong way to end file
    else 
    {
        #ifdef DEBUG
            printf("%s\n", "Subor je nespravne ukonceny !! (ale to nevadi)");
        #endif
        *rows = current_row+1;
    }

    #ifdef DEBUG
        printf("Number of lines: %i\n", current_row);
    #endif

    return p;
}


void * xrealloc (void *ptr, size_t size)
{
    register void *value = realloc (ptr, size);
    if (value == 0)
        fatal ("Virtual memory exhausted");
    return value;
}

void dump(char **p, int n)
{
    int i, s;
    printf("-- DUMP --\n");
    for(i = 0; i < n; i++)
    {
        for(s = 0; p[i][s] != '\0'; s++)
            printf("%c", p[i][s]);
        printf("\n");
    }
    printf("-- END OF DUMP --\n");
}

// A function to display an error message and then exit
void fatal(char *message) 
{
   char error_message[100];

   strcpy(error_message, "[!!] Fatal Error ");
   strncat(error_message, message, 83);
   perror(error_message);
   exit(-1);
}

感谢您的支持,这让我为了解决这个问题劳累了数小时。

2
p[current_row] = (char*) xrealloc(p[current_row], current_pos+1); 我看不到你曾经将 p[some_index] 初始化为 NULL - Daniel Fischer
我不知道,但是该函数第一次成功运行。但是当第二次调用它时,glibc出现了。 - Horkyze
初始的char **p = (char **) malloc(1 * sizeof(char *))同上。 - alk
没有初始化,内容是不确定的,所以是否将空指针传递给 realloc 是随机的。第一次,你很可能会得到由操作系统清零的新内存,通常所有位都为 0 是一个空指针表示。后续的情况下,它更可能包含非空垃圾数据。 - Daniel Fischer
1个回答

2

Valgrind下运行您的程序,它将会准确告诉您问题所在。


4
抱歉,但这条留言更适合在评论区发布。 - alk
我通过valgrind运行它,没有错误,程序正常工作。这怎么可能? - Horkyze
@Horkyze:无论如何,我会遵循Employed Russian的建议。 - alk
@Horkyze:我得到了一个“有条件的跳转或移动取决于未初始化的值”和一个“大小为8的无效读取”。后者是致命的。前者则是混乱。 - alk
所以我在xrealloc之后添加了这行代码p[current_row] = 0;。现在它运行良好。谢谢你的帮助 :) - Horkyze

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接