gpt4 book ai didi

c - Malloc 返回相同的值 - 没有双重释放错误

转载 作者:行者123 更新时间:2023-11-30 15:14:45 25 4
gpt4 key购买 nike

查看最新更新

给定以下函数,请记下调用 free(tmp) 的位置:

int *power_arr(int *n, int nlength, int exp, int *res_length)
{
int *tmp, *rt, *bufp;
int bufp_length, i, dbg_i;

rt = malloc(sizeof(int) * 1000);
bufp = malloc(sizeof(int) * 1000);

if (!rt || !bufp)
{
return NULL;
}

copy(rt, n, nlength);
copy(bufp, n, nlength);

*res_length = bufp_length = nlength;

while (--exp > 0)
{
for (i = *n - 1; i > 0; i--)
{
tmp = sum(rt, *res_length, bufp, bufp_length, res_length);

if (!tmp)
{
exit(-1);
}

copy(rt, tmp, *res_length);
//free(tmp); // produces undefined output?
}

copy(bufp, rt, *res_length);
bufp_length = *res_length;
}

free(tmp);

free(bufp);

return rt;
}

以下主函数的结果:

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

int main(void)
{
int b[] = { 3 };
int r, i;
int *rlength, *res;

r = 0;

rlength = &r;

res = power_arr(b, 1, 3, rlength);

printf("Length = %d\n", *rlength);

for (i = 0; i < *rlength; i++)
{
printf("i=");
printf("%d\n", res[i]);
}

printf("\n");



exit(0);
}

是:

Length = 2
i=2
i=7

我对第一种情况的理解是,每次后续的 tmp = sum(rt, *res_length, bufp, bufp_length, res_length); 调用都会发生内存泄漏。此时,我决定将调用移至 for 循环内的 free(tmp)。移动后,我注意到输出发生了变化,如下所示:

Length = 4
i=1018670
i=4
i=2
i=7

我可以看到答案从 i[3] 开始。为什么 free(tmp) 调用的移动会导致这种效果?

我的理解是,在 free() 调用之后,tmp 变成了一个悬空指针。然后,它被重新分配一个由函数 sum() 返回的值 - 该值是通过调用 malloc() 检索的。此时,如果将对 free() 的调用放在其原始位置,就会发生内存泄漏。由于 tmp 的值会发生变化,因此仅释放分配给它的最后一个指针。

<小时/>

编辑:

下面是支持功能的代码。

int *pad(int *n, int nlength, int new_length, enum SIDE side)
{
int i, j;
int *padded;

if (nlength < 1 || new_length <= nlength)
{
return NULL;
}

padded = calloc(new_length, sizeof(int));

if (!padded)
{
return NULL;
}

if (side == LOW)
{
j = new_length - 1;

for (i = (nlength - 1); i >= 0; i--)
{
padded[j--] = n[i];
}
}
else
{
j = 0;

for (i = 0; i < nlength; i++)
{
padded[j++] = n[i];
}
}

return padded;
}

int *trim(int *n, int nlength, int *res_length)
{
int i, j;
int *res;

for (i = 0; i < nlength; i++)
{
if (n[i] > 0)
{
break;
}
}

*res_length = (nlength - i);

res = malloc(sizeof(int) * (*res_length));

if (!res)
{
return NULL;
}

j = 0;

while (i < nlength)
{
res[j++] = n[i++];
}

return res;
}

int *sum(int *n, int nlength, int *m, int mlength, int *sum_length)
{
int i, tmp, carry;
int *result, *trimmed, *op1, *op2;
enum SIDE side = LOW;

if (nlength == mlength)
{
op1 = n;
op2 = m;
}
else if (nlength > mlength)
{
op1 = n;
op2 = pad(m, mlength, nlength, side);
}
else
{
op1 = m;
op2 = pad(n, nlength, mlength, side);
}

result = malloc(sizeof(int) * (MAX(nlength, mlength) + 1));

if (!op1 || !op2 || !result)
{
return NULL;
}

carry = 0;

for (i = (MAX(nlength, mlength)) - 1; i >= 0; i--)
{
tmp = op1[i] + op2[i] + carry;

if (carry > 0)
{
carry = 0;
}

if (tmp >= 10)
{
carry = tmp / 10;
tmp = tmp % 10;
}

result[i + 1] = tmp;
}

if (carry > 0)
{
result[0] = carry--;
}

*sum_length = (MAX(nlength, mlength)) + 1;

trimmed = trim(result, *sum_length, sum_length);

free(result);

if (!trimmed)
{
return NULL;
}

return trimmed;
}

void copy(int *to, int *from, int length)
{
int i;

for (i = 0; i < length; i++)
{
to[i] = from[i];
}
}
<小时/>

更新:

实现第一篇文章中建议的更改后,开始出现双重释放错误,为了进行调试,我将以下打印语句添加到power_arr()中。以下输出显示 tmpsum() 分配的值与初始调用时收到的值相同。为什么?

更新了显示调试 printf 语句的代码:

    for (i = *n - 1; i > 0; i--)
{
tmp = sum(rt, *res_length, bufp, bufp_length, res_length);

printf("first tmp = %d\n", tmp);

if (!tmp)
{
printf("tmp was null\n");
exit(-1);
}

copy(rt, tmp, *res_length);

printf("second tmp = %d\n", tmp);\

if (tmp != NULL)
{
printf("freeing tmp\n");
free(tmp);
tmp = NULL;
}

printf("tmp = %d\n", tmp);
}

输出:

first tmp = 11227072
second tmp = 11227072
freeing tmp
tmp = 0
first tmp = 11227072 <-- Why has the pointer value not changed?
second tmp = 11227072
freeing tmp <-- Double free now occuring.
*** Error in `./a.out': double free or corruption (fasttop): 0x0000000000ab4fc0 ***
Aborted
<小时/>

更新:

我相信我已经将该错误追溯到 trim() 函数。我已经发布了带有循环的函数来连续执行它10次。正如您在输出中看到的,trim() - 调用 malloc(),在后续调用中返回相同的指针值。然而,每次连续调用 free 都不会触发双重释放错误。为什么会这样?

int main()
{
int i, j, length;
int n[] = { 4, 5, 6 };
int m[] = { 0, 3, 5 };
int *num;
int *trimmed, *trimmed_length;

trimmed_length = &length;

for (i = 0; i < 10; i++)
{
num = (i % 2 == 0) ? n : m;

trimmed = trim(num, 3, trimmed_length);

if (!trimmed)
{
printf("trimmed was null\n");
exit(-1);
}

for (j = 0; j < *trimmed_length; j++)
{
printf("%d", trimmed[j]);
}

printf("\n");

free(trimmed);
}

exit(0);
}

int *trim(int *n, int nlength, int *res_length)
{
int i, j;
int *res;

for (i = 0; i < nlength; i++)
{
if (n[i] > 0)
{
break;
}
}

*res_length = (nlength - i);

res = malloc(sizeof(int) * (*res_length));

if (!res)
{
return NULL;
}

j = 0;

while (i < nlength)
{
res[j++] = n[i++];
}

printf("Returned pointer from trim() %d\n", res);

return res;
}

输出:

Returned pointer from trim() 39534608
456
Returned pointer from trim() 39534608
35
Returned pointer from trim() 39534608
456
Returned pointer from trim() 39534608
35
Returned pointer from trim() 39534608
456
Returned pointer from trim() 39534608
35
Returned pointer from trim() 39534608
456
Returned pointer from trim() 39534608
35
Returned pointer from trim() 39534608
456
Returned pointer from trim() 39534608
35

这似乎也是我原来问题中的行为 - 它触发了双重释放错误。为什么在这种特殊情况下没有遇到双重释放错误?

最佳答案

您有许多内存泄漏点和一个完整的错误,这就是释放失败的原因[由于同一指针的双重释放]。

注意:我已对此答案进行了更新,但它太大,无法放在这里,因此我将其发布为第二个答案

我已经将所有文件合并为一个,这样我就可以编译它[请原谅无谓的风格清理],修复了错误,并注释了所有热点[这可以编译,但我没有测试它]:

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

enum SIDE {
LOW
};

#define MAX(_x,_y) (((_x) > (_y)) ? (_x) : (_y))

// perform free if pointer is non-null -- set to null afterwards to prevent
// "double free"
#define FREEME(_ptr) \
do { \
if (_ptr != NULL) \
free(_ptr); \
_ptr = NULL; \
} while (0)

int *
pad(int *n, int nlength, int new_length, enum SIDE side)
{
int i,
j;
int *padded;

if (nlength < 1 || new_length <= nlength) {
return NULL;
}

padded = calloc(new_length, sizeof(int));

if (!padded) {
return NULL;
}

if (side == LOW) {
j = new_length - 1;

for (i = (nlength - 1); i >= 0; i--) {
padded[j--] = n[i];
}
}
else {
j = 0;

for (i = 0; i < nlength; i++) {
padded[j++] = n[i];
}
}

return padded;
}

int *
trim(int *n, int nlength, int *res_length)
{
int i,
j;
int *res;

for (i = 0; i < nlength; i++) {
if (n[i] > 0) {
break;
}
}

*res_length = (nlength - i);

res = malloc(sizeof(int) * (*res_length));

if (!res) {
return NULL;
}

j = 0;

while (i < nlength) {
res[j++] = n[i++];
}

return res;
}

int *
sum(int *n, int nlength, int *m, int mlength, int *sum_length)
{
int i,
tmp,
carry;
int *result,
*trimmed,
*op1,
*op2;
int padflg;
enum SIDE side = LOW;

// NOTE: this helps us remember whether to free op2 or not
padflg = 1;

// NOTE: here op2 comes from _caller_ -- so do _not_ free it in this
// function -- _this_ is the cause of the bug
// case (1)
if (nlength == mlength) {
op1 = n;
op2 = m;
padflg = 0;
}

// NOTE: here op2 comes from _pad_ -- so we do _want_ to free it so it
// doesn't leak
// case (2)
else if (nlength > mlength) {
op1 = n;
op2 = pad(m, mlength, nlength, side);
}

// case (3)
else {
op1 = m;
op2 = pad(n, nlength, mlength, side);
}

result = malloc(sizeof(int) * (MAX(nlength, mlength) + 1));

if (!op1 || !op2 || !result) {
if (padflg)
FREEME(op2);
FREEME(result);
return NULL;
}

carry = 0;

for (i = (MAX(nlength, mlength)) - 1; i >= 0; i--) {
tmp = op1[i] + op2[i] + carry;

if (carry > 0) {
carry = 0;
}

if (tmp >= 10) {
carry = tmp / 10;
tmp = tmp % 10;
}

result[i + 1] = tmp;
}

// NOTE: we want to free op2 for case (2)/(3) but we didn't remember
// how we got it: (1) means no free, (2)/(3) means free
// only free if this if we called pad, and _not_ if this pointer belongs
// to caller
if (padflg)
FREEME(op2);

if (carry > 0) {
result[0] = carry--;
}

*sum_length = (MAX(nlength, mlength)) + 1;

trimmed = trim(result, *sum_length, sum_length);

free(result);

return trimmed;
}

void
copy(int *to, int *from, int length)
{
int i;

for (i = 0; i < length; i++) {
to[i] = from[i];
}
}

int *
power_arr(int *n, int nlength, int exp, int *res_length)
{
int *tmp,
*rt,
*bufp;
int bufp_length,
i;

// NOTE: rt/bufp are memory leaks -- they are never freed
rt = malloc(sizeof(int) * 1000);
bufp = malloc(sizeof(int) * 1000);

// NOTE: this is a memory leak -- if one is null, but the other is non-null,
// you must free the non-null one or it leaks
if (!rt || !bufp) {
FREEME(rt);
FREEME(bufp);
return NULL;
}

copy(rt, n, nlength);
copy(bufp, n, nlength);

*res_length = bufp_length = nlength;

while (--exp > 0) {
for (i = *n - 1; i > 0; i--) {
tmp = sum(rt, *res_length, bufp, bufp_length, res_length);

if (!tmp) {
exit(-1);
}

copy(rt, tmp, *res_length);

// NOTE: this will now work because of the padflg changes in
// sum
#if 0
// free(tmp); // produces undefined output?
#else
FREEME(tmp);
#endif
}

copy(bufp, rt, *res_length);
bufp_length = *res_length;
}

FREEME(bufp);

return rt;
}

int
main(void)
{
int b[] = { 3 };
int r,
i;
int *rlength,
*res;

r = 0;

rlength = &r;

res = power_arr(b, 1, 3, rlength);

printf("Length = %d\n", *rlength);

for (i = 0; i < *rlength; i++) {
printf("i=");
printf("%d\n", res[i]);
}

printf("\n");

exit(0);
}

关于c - Malloc 返回相同的值 - 没有双重释放错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33932203/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com