免责声明,这是学校作业的帮助也就是说,我的问题只发生在大约50%的时间也就是说,如果我编译和运行我的代码而不进行编辑,有时它会一直运行到最后,有时它不会通过使用多个print语句,我知道问题发生时的确切位置这个问题发生在我第二次打给hugeDestroyer的电话中(就在打印35491354687951984351984354894353513179部分之后),更确切地说是在空闲(p->数字)部分。
我已经尝试了这里的建议(free a pointer to dynamic array in c),并在不走运地释放指针后将其设置为空。
通过一些挖掘和自我反省,我了解到更多关于free是如何工作的(How do malloc() and free() work?),我想知道我的问题是否源于用户Juergen在他的回答中提到的内容,以及我正在“覆盖”free列表中的管理数据。
要说清楚,我的问题有两个方面。
free(p->digits)在语法上是正确的吗?如果是,为什么我在运行代码时一半时间都会遇到问题?
其次,在我的职能中,我如何防范这种行为?

#include <stdio.h>
#include <stdlib.h>
#include <limits.h>


typedef struct HugeInteger
{
    // a dynamically allocated array to hold the digits of a huge integer
    int *digits;

    // the number of digits in the huge integer (approx. equal to array length)
    int length;
} HugeInteger;


// Functional Prototypes

int str2int(char str) //converts single digit numbers contained in strings to their int value
{
    return str - 48;
}

HugeInteger *parseInt(unsigned int n)
{
    int i = 0, j = 0;
    int *a = (int *)calloc(10, sizeof(int));
    HugeInteger *p = (HugeInteger *)calloc(1, sizeof(HugeInteger));

    if(n == 0)
    {
        p->digits = (int *)calloc(1, sizeof(int));
        p->length = 1;
        return p;
    }

    while(n != 0)
    {
        a[i] = n % 10;
        n = n / 10;
        i++;
    }

    p->length = i;
    p->digits = (int *)calloc(p->length, sizeof(int));

    for(i = 0; i <= p->length; i++, j++)
        p->digits[j] = a[i];

    return p;
}

HugeInteger *parseString(char *str)  //notice datatype is char (as in char array), so a simple for loop should convert to huge int array
{
    int i = 0, j = 0;
    HugeInteger *p = (HugeInteger *)calloc(1, sizeof(HugeInteger));

    if(str == NULL)
        {
        free(p);
        p = NULL;
        return p;
        }

    else
    {
        for(i=0; str[i] != '\0'; i++)
            ;
        p->length = i;
        p->digits = (int *)calloc(p->length, sizeof(int));

        for(; i >= 0; i--)
            p->digits[j++] = str2int(str[i - 1]);
    }

    return p;
} //end of HugeInteger *parseString(char *str)


HugeInteger *hugeDestroyer(HugeInteger *p)
{
//printf("No problem as we enter the function\n");
    if(p == NULL)
        return p;
//printf("No problem after checking for p = NULL\n");
    if(p->digits == NULL)
    {
        free(p);
        p = NULL;
        return p;
    }
//printf("No Problem after checking if p->digits = NULL\n");
    //else
    //{
        free(p->digits);
printf("We made it through free(p->digits)\n");
        p->digits = NULL;
printf("We made it through p->digits = NULL\n");
        free(p);
printf("We made it through free(p)\n");
        p = NULL;
printf("We made it through p = NULL\n");
        return p;
    //}

    //return NULL;
}//end of HugeInteger *hugeDestroyer(HugeInteger *p)

// print a HugeInteger (followed by a newline character)
void hugePrint(HugeInteger *p)
{
    int i;

    if (p == NULL || p->digits == NULL)
    {
        printf("(null pointer)\n");
        return;
    }

    for (i = p->length - 1; i >= 0; i--)
        printf("%d", p->digits[i]);
    printf("\n");
}

int main(void)
{
    HugeInteger *p;

    hugePrint(p = parseString("12345"));
    hugeDestroyer(p);

    hugePrint(p = parseString("354913546879519843519843548943513179"));
    hugeDestroyer(p);

    hugePrint(p = parseString(NULL));
    hugeDestroyer(p);

    hugePrint(p = parseInt(246810));
    hugeDestroyer(p);

    hugePrint(p = parseInt(0));
    hugeDestroyer(p);

    hugePrint(p = parseInt(INT_MAX));
    hugeDestroyer(p);

    //hugePrint(p = parseInt(UINT_MAX));
    //hugeDestroyer(p);

    return 0;
}

最佳答案

首先,非常突出的问题你做了很多关于这个话题的研究,总的来说,这个问题是你自己解决的,我来这里主要是为了证实你的发现。
free(p->digits)在语法上是正确的吗?如果是,为什么我在运行代码时一半时间都会遇到问题?
语法正确@Shihab在评论中建议不要只发布p->digits和releasep,但这样的建议是错误的,它会导致内存泄漏有一个简单的规则:对于每个calloc,您必须最终调用free,所以您当前的释放p->digits然后p的方法是完全正确的。
但是,程序在有效行上失败怎么可能快速回答:由于负责跟踪已分配/空闲块列表的元信息损坏,free无法执行其工作在某些情况下,程序损坏了元信息,但这只是在试图使用它时才显示出来的。
正如您已经发现的,在大多数实现中,内存例程(如calloc)都会导致缓冲区的分配,其中包含预先设置好的元信息您接收到指向缓冲区本身的指针,但在此指针之前的一小段信息对于进一步的缓冲区管理(例如释放)至关重要将11个整数写入10的缓冲区,可能会损坏缓冲区后面块的元数据信息腐败是否真的发生,其后果如何,在很大程度上取决于具体的实现和当前的内存对齐方式(缓冲区后面是什么块,什么元数据被破坏)这并不奇怪,你看到每两次执行一次崩溃,也不奇怪我观察到100%崩溃复制在我的系统上。
其次,在我的职能中,我如何防范这种行为?
让我们从固定溢出开始。其中有两个:
parseString:循环for(; i >= 0; i--)执行length+1次,因此p->digits溢出
parseInt:循环for (i = 0; i <= p->length; i++, j++)执行length+1次,因此p->digits溢出
在C++中直接访问内存管理是容易出错和调试麻烦的。内存泄漏和缓冲区溢出是程序员生活中最糟糕的噩梦,通常最好是简化/减少动态内存的直接使用,当然,除非您正在研究如何处理它如果您需要坚持大量的直接内存管理,请看一下valgrind,它旨在检测所有这些东西。
顺便说一下,程序中也有内存泄漏:每次调用parseInt都会为a分配缓冲区,但永远不会释放它。

关于c - Calloc/Malloc和释放到经常还是太大的空间?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/42615496/

10-12 07:27