Valgrind错误大小为4的无效读/写-调试后未找到问题-C

时间:2019-11-29 17:42:14

标签: c arrays pointers debugging valgrind

这是我的代码,该代码使用c中的数组实现Queue数据结构。请注意,gMyQueue是我之前定义的Queue类型的全局变量。在此程序上运行Valgrind时-会大喊无效大小为4的写入和读取错误。我已经调试了一段时间,试图抓住问题,仔细检查指针地址,索引值等。但是似乎没有什么错。

typedef struct Queue
{
    size_t numOfElems;
    size_t queueCapacity;
    int *data;
} Queue;

Queue gMyQueue;

void printQueue()
{
    for(size_t i = 0; i < gMyQueue.numOfElems; ++i)
    {
      printf("%d, ", gMyQueue.data[i]);
    }

    printf("\n");

    printf("numOfElems: %lu, queueCapacity: %lu\n",
           gMyQueue.numOfElems, gMyQueue.queueCapacity);
}

void cleanQueue()
{
    if (gMyQueue.data != NULL)
    {
        free(gMyQueue.data);
    }
}

void init()
{
    gMyQueue.data = (int *) malloc(sizeof(int));
    gMyQueue.queueCapacity = 1;
    gMyQueue.numOfElems = 0;
}


void enqueue(int n) {
    if (gMyQueue.numOfElems == 0 && gMyQueue.data == NULL) {
        init();
        gMyQueue.data[gMyQueue.numOfElems] = n;
        gMyQueue.numOfElems++;
    }
    else {
        gMyQueue.numOfElems++;
        if (gMyQueue.numOfElems == gMyQueue.queueCapacity) {
            gMyQueue.queueCapacity *= 2;
            gMyQueue.data = (int*)realloc(gMyQueue.data, gMyQueue.queueCapacity);
        }

        gMyQueue.data[gMyQueue.numOfElems - 1] = n;
    }
}

int dequeue(void) {
    int dequeue = 0;
    if (gMyQueue.numOfElems == 0) {
        return dequeue;;
    }
    else {
        dequeue = *(gMyQueue.data);
        for (int i = 0; i < gMyQueue.numOfElems - 1; i++)
        {
            gMyQueue.data[i] = gMyQueue.data[i + 1];
        }
        gMyQueue.numOfElems--;
    }
    return dequeue;
}
int main()
{
    init();
    enqueue(1);
    enqueue(2);
    dequeue();
    printQueue();
    cleanQueue();
    return 0;
}

我已经调试了一段时间,看来一切正常,也可以使用指针和索引...但是运行valgrind会产生以下结果:

==12905== Invalid write of size 4
==12905==    at 0x108CC9: enqueue (main.c:200)
==12905==    by 0x109071: main (main.c:309)
==12905==  Address 0x522d090 is 0 bytes inside a block of size 2 alloc'd
==12905==    at 0x4C31D2F: realloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==12905==    by 0x108CA5: enqueue (main.c:197)
==12905==    by 0x109071: main (main.c:309)
==12905== 
==12905== Invalid write of size 4
==12905==    at 0x108CC9: enqueue (main.c:200)
==12905==    by 0x10907B: main (main.c:310)
==12905==  Address 0x522d0e4 is 0 bytes after a block of size 4 alloc'd
==12905==    at 0x4C31D2F: realloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==12905==    by 0x108CA5: enqueue (main.c:197)
==12905==    by 0x10907B: main (main.c:310)
==12905== 
==12905== Invalid read of size 4
==12905==    at 0x108D2B: dequeue (main.c:213)
==12905==    by 0x109080: main (main.c:311)
==12905==  Address 0x522d0e4 is 0 bytes after a block of size 4 alloc'd
==12905==    at 0x4C31D2F: realloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==12905==    by 0x108CA5: enqueue (main.c:197)
==12905==    by 0x10907B: main (main.c:310)

有人知道出什么事了吗? 谢谢。

2 个答案:

答案 0 :(得分:2)

是的,我在评论中提到的是问题。您没有在realloc() call中分配足够的内存:

void enqueue(int n) {
    if (gMyQueue.numOfElems == 0 && gMyQueue.data == NULL) {
        init();
        gMyQueue.data[gMyQueue.numOfElems] = n;
        gMyQueue.numOfElems++;
    }
    else {
        gMyQueue.numOfElems++;
        if (gMyQueue.numOfElems == gMyQueue.queueCapacity) {
            gMyQueue.queueCapacity *= 2;
            gMyQueue.data = (int*)realloc(gMyQueue.data, gMyQueue.queueCapacity); // HERE
        }

        gMyQueue.data[gMyQueue.numOfElems - 1] = n;
    }
}

您的代码正确扩展了容量-最大数量的项目-但是在realloc()期间,它没有将项目转换为字节。您可以在这里这样做:

gMyQueue.data = (int*)realloc(gMyQueue.data, gMyQueue.queueCapacity * sizeof(int));
                                                                    ^^^^^^^^^^^^^^

现在,Valgrind似乎对此很满意。

我建议您实际上从变量而不是类型的名称中得出大小。使用sizeof(* gMyQueue.data)而不是sizeof(int)可以从变量指向的对象中提取类型,如果更新代码,则只需更改即可。

例如,如果您希望datalong而不是int的数组,那么您当然必须更新结构以反映正确的类型,但是您不会必须更新sizeof。建议同样在原始malloc()中进行相同的操作。

这不是必需的,但是这是一个很好的习惯,可以避免以后遇到的问题。

答案 1 :(得分:2)

至少函数enqueue是错误的。

else语句不得以numOfElems的增量开头

//...
else {
    gMyQueue.numOfElems++;
    //…

因为该数据成员已经在if语句中的函数的第一次调用中增加了

if (gMyQueue.numOfElems == 0 && gMyQueue.data == NULL) {
    init();
    gMyQueue.data[gMyQueue.numOfElems] = n;
    gMyQueue.numOfElems++;
}

此函数realloc

的调用
    gMyQueue.data = (int*)realloc(gMyQueue.data, gMyQueue.queueCapacity);

不正确。第二个参数应指定新分配的内存的大小。因此通话应该至少看起来像

    gMyQueue.data = (int*)realloc(gMyQueue.data, gMyQueue.queueCapacity * sizeof( int ) );

并且您应该使用中间指针,因为该函数可以返回NULL。在这种情况下,先前分配的内存的地址将丢失。

函数看起来像

void enqueue( int n ) 
{
    if ( gMyQueue.data == NULL ) 
    {
        init();
        gMyQueue.data[gMyQueue.numOfElems++] = n;
    }
    else 
    {
        if ( gMyQueue.numOfElems == gMyQueue.queueCapacity ) 
        {
            gMyQueue.queueCapacity *= 2;
            int *tmp = realloc( gMyQueue.data, gMyQueue.queueCapacity * sizeof( int ) );
            if ( tmp != NULL )
            {
                gMyQueue.data = tmp; 
                gMyQueue.data[gMyQueue.numOfElems++] = n;
            }
        }
    }
}