【问题标题】:Malloc returning same value - no double free errorMalloc 返回相同的值 - 没有双重释放错误
【发布时间】:2015-11-26 06:48:00
【问题描述】:

查看最新更新

给定以下函数,记下调用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)调用的移动会造成这种效果?

我的理解是tmpfree() 调用之后变成了一个悬空指针。然后,它被重新分配一个由函数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];
    }
}

更新:

在实施第一篇文章中建议的更改后,double free 错误开始发生,为了调试,我将以下打印语句添加到 power_arr()。下面的输出显示tmp 被分配了与sum() 相同的值,就像它在初始调用时收到的一样。为什么?

显示调试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() 函数。我已经发布了带有 loop 的函数以连续执行 10 次。正如您在输出中看到的那样,trim() - 调用 malloc() 在后续调用中返回相同的指针值。然而,每次连续调用 free 都不会触发 double 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

这似乎也是我最初问题中的行为 - 它触发了 double free 错误。为什么在这种特殊情况下没有遇到 double free 错误?

【问题讨论】:

  • 当您的程序无法正常运行时,请创建一个Minimal, Complete, and Verifiable example 并发布它。
  • sumcopy的定义在哪里?错误可能就在那里。请创建一个最小、完整且可验证的示例...
  • @RSahu 该函数是利用整数数组计算 2^1000 的一系列函数的一部分。我可以发布支持功能的代码。请注意,我已将代码测试为正确高达 2^1000。当我决定应该移动 free(tmp) 调用时,我注意到了未定义的行为。
  • @cryptic,当您尝试创建一个最小、完整和可验证的示例时,您通常最终会自己看到问题。如果你不这样做,其他人会更容易看到给出这样一个例子的问题。
  • @R 萨胡。我已经单独测试了支持功能,并获得了成功的结果。正是当我相信我发现了内存泄漏,并决定在为其分配一个新值之前free(tmp)——反过来,释放 malloc 返回的前一个值——我遇到了未定义的输出。

标签: c free undefined-behavior


【解决方案1】:

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

注意:我已经对这个答案进行了更新,但它太大了,不适合这里,所以我把它作为第二个答案发布了

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

#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);
}

【讨论】:

  • 使用 NULL 指针调用 free() 是可以的,free() 会检查该特殊情况并立即返回。
  • 实施这些更改后,似乎出现了一个新错误。对FREEME(tmp) 的调用现在导致双倍免费。如果 tmp 被分配了 NULL 的值,然后从对 sum 的调用中重新分配了一个新值,为什么会发生这种情况?
  • 我在power_arr()for 循环中添加了printf 语句 - 以显示tmp 的值(它也指向的位置)。在随后调用sum() 后,似乎分配了相同的值。通过执行 2^2,可以看到 - 正如我发布的输出所示。
  • 注意,通过在trim() 中放置printf(res),我可以看到malloc() 返回的值在后续调用中是相同的。我将放置一段显示结果的代码。
  • 我添加了一个可以复制和运行的sn-p代码。它表明trim() 返回的值在后续调用中是相同的——即使输入参数不同。现在的问题是,为什么在这种情况下没有观察到 double free 错误?不应该每次调用free(trimmed) 都会触发 double free 错误,因为 trimmed 的值没有改变?
【解决方案2】:

更新:我在这里添加第二个答案,因为新的代码示例太大,不适合作为我之前的更新。

我发现了更多问题。如果您重复运行原始测试程序,它会在每次运行时给出不同的 [不正确] 答案。也就是说,从脚本循环它。

我无法使用现有函数和参数修复剩余的错误。纯粹的复杂性增加了问题。

一旦我意识到你真正想要做的是什么(例如多精度数学),我就能够应用一些简化:

我没有到处传递int *vals, int *len,而是创建了一个“大数字”struct
- 包含指向数据和长度的指针
- 数据缓冲区的长度可以动态增长/收缩
- 有引用计数

您在“大端”模式下进行数学运算。这使得有必要经常重新分配(例如padcopy)。这也增加了复杂性。我将其切换为使用“little endian”, 更易于使用。而且,它也有点[相当]快。

我还在你的幂函数中注意到你使用sum 来做本质上是rt += bufp; 的事情。所以,我创建了一个版本sumeq,直接对power的rt值进行操作,进一步简化了事情。

注意:您仍然可以使用大端,但我见过的大多数包都很少使用。尽管如此,创建/增长结构的基本例程与字节序无关,您可以创建我创建的小字节序函数的大字节序版本[来自您的大字节序函数]


这是实际代码。它是可构建和可运行的。它将测试所有版本,包括您的原始代码 [有问题]。

// badfree -- test program

#define TSTDEFALL
// badfree/badfree.h -- badfree control

#ifndef _badfree_badfree_h_
#define _badfree_badfree_h_

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

#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

#ifdef DEBUG
#define dbgprtf(_fmt...)        printf(_fmt)
#define dbgexec(_exec)          _exec
#else
#define dbgprtf(_fmt...)        /**/
#define dbgexec(_exec)          /**/
#endif

#define dbgleshow(_num,_who)    dbgexec(leshow(_num,0,_who))

#define sysfault(_fmt...) \
    do { \
        printf(_fmt); \
        fflush(stdout); \
        _sysfault(); \
    } while (0)

typedef unsigned int u32;

#if BIGDIG == 1
typedef char bigdig_t;
#elif BIGDIG == 2
typedef short bigdig_t;
#else
typedef int bigdig_t;
#endif
typedef bigdig_t *bigdig_p;
typedef const bigdig_t *bigdig_pc;

// multiprecision number control
struct bignum {
    u32 num_opts;                       // options
    const char *num_tag;                // symbol name
    int num_refcnt;                     // reference count
    int num_curlen;                     // current length
    int num_maxlen;                     // maximum length
    bigdig_p num_base;                  // pointer to vector
};
typedef struct bignum bgn_t;
typedef bgn_t *bgn_p;
typedef const bgn_t *bgn_pc;

// num_opts definitions
#define BGNASTRUCT      (1 << 0)    // struct was allocated
#define BGNABASE        (1 << 1)    // num_base was allocated
#define BGNINITAV       (1 << 2)    // bgninit -- init values
#define BGNINITDUP      (1 << 3)    // bgninit -- init values
#define BGNTRIM         (1 << 4)    // trim number
#define BGNRAW          (1 << 5)    // output number in raw order

#define BGNACQUIRE(_num) \
    bgnacquire(_num)
#define BGNRELEASE(_num) \
    _num = bgnrelease(_num)

#define BGNINIT(_sym,_opt,_len...) \
    _sym = bgninit(#_sym,_opt,_len)

#define BGNASSERT(_num,_opt) \
    do { \
        if (((_num)->num_opts & _opt) == (_opt)) \
            break; \
        sysfault("BGNASSERT: failure -- num=%p num_opts=%8.8X opt=%8.8X\n", \
            num,num->num_opts,_opt); \
    } while (0)

#define BGNASSERT_NOT(_num,_opt) \
    do { \
        if (((_num)->num_opts & _opt) != (_opt)) \
            break; \
        sysfault("BGNASSERT_NOT: failure -- num=%p num_opts=%8.8X opt=%8.8X\n", \
            num,num->num_opts,_opt); \
    } while (0)

enum SIDE {
    LOW
};

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

// perform free if pointer is non-null -- set to null afterwards to prevent
// "double free" and use of pointer after it has been freed
#define FREEME(_ptr) \
    do { \
        if (_ptr != NULL) \
            free(_ptr); \
        _ptr = NULL; \
    } while (0)

// test control
struct tstctl {
    int tst_allow;                      // allow test to be performed
    const char *tst_tag;                // name of test
    void (*tst_fnc)(void);              // test function
};
typedef struct tstctl tstctl_t;
typedef tstctl_t *tstctl_p;
typedef const tstctl_t *tstctl_pc;

#define FORTSTALL(_tst) \
    _tst = tstlist;  _tst->tst_tag != NULL;  ++_tst

    int
    main(int argc, char **argv);

    void
    dotests(void);

    void
    dotest(tstctl_p tst);

    void
    usage(void);

    void
    becopy(bigdig_t *to, const bigdig_t *from, int length);

    bigdig_t *
    bepad(const bigdig_t *n, int nlength, int new_length, enum SIDE side);

    bigdig_t *
    betrim(const bigdig_t *n, int *res_length);

    bigdig_t *
    besum(bigdig_t *n, int nlength, bigdig_t *m, int mlength,
        int *sum_length);

    bigdig_t *
    be_power_arr(const bigdig_t *n, int nlength, int exp, int *res_length);

    void
    betest_orig(void);

    void
    betest_trim(void);

    void
    betest_show(bigdig_pc num,int len,const char *sym);

    bgn_p
    bgninit(const char *tag,u32 opt,int maxlen,...);

    bgn_p
    bgnacquire(bgn_p num);

    bgn_p
    bgnrelease(bgn_p num);

    void
    bgngrow(bgn_p num,int newlen);

    void
    bgnshrink(bgn_p num);

    void
    lecopy(bgn_p to,bgn_pc from);

    bgn_p
    lepad(bgn_pc num,int newlen);

    void
    letrim(bgn_p num);

    int
    _letrim(bgn_pc num);

    void
    leshow(bgn_pc num,u32 opt,const char *who);

    void
    lesumeq(bgn_p result,bgn_p op2);

    bgn_p
    le_power_arr_eq(bgn_pc num,int exp);

    bgn_p
    lesumrt(bgn_p n,bgn_p m);

    bgn_p
    le_power_arr_rt(bgn_pc n, int exp);

    void
    letest_lesumrt(void);

    void
    letest_lesumeq(void);

    void
    _sysfault(void);

    void
    reverse(bigdig_p arr, int length);

#define TSTDEF(_on,_fnc) \
    { .tst_allow = _on, .tst_tag = #_fnc, .tst_fnc = _fnc }

#ifdef TSTDEFALL
tstctl_t tstlist[] = {
    TSTDEF(1,betest_orig),
    TSTDEF(0,betest_trim),
    TSTDEF(1,letest_lesumrt),
    TSTDEF(1,letest_lesumeq),
    { .tst_tag = NULL }
};
#endif

#endif

// badfree/bemath -- big endian math

void
becopy(bigdig_t *to, const bigdig_t *from, int length)
{
    int i;

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

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

    if (nlength < 1 || new_length <= nlength) {
        sysfault("bepad: length fault -- nlength=%d new_length=%d\n",
            nlength,new_length);
        return NULL;
    }

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

    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;
}

bigdig_t *
betrim(const bigdig_t *n, int *res_length)
{
    int i;
    int j;
    int nlength;
    bigdig_t *res;

    nlength = *res_length;

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

    nlength -= i;
    *res_length = nlength;

    res = malloc(sizeof(bigdig_t) * nlength);
    if (!res) {
        sysfault("betrim: null malloc\n");
        return NULL;
    }

    j = 0;
    for (;  i < nlength;  ++i, ++j)
        res[j] = n[i];

    return res;
}

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

    // 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 = bepad(m, mlength, nlength, side);
        padflg = 1;
    }

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

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

    if (!op1 || !op2 || !result) {
        sysfault("besum: null fault -- padflg=%d op1=%p op2=%p result=%p\n",
            padflg,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 bepad, 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 = betrim(result, sum_length);

    FREEME(result);

    return trimmed;
}

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

    // NOTE: rt/bufp are memory leaks -- they are never freed
    rt = malloc(sizeof(bigdig_t) * 1000);
    bufp = malloc(sizeof(bigdig_t) * 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;
    }

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

    *res_length = bufp_length = nlength;

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

            if (tmp == NULL)
                sysfault("be_power_arr: null besum return\n");

            nlength = *res_length;
            becopy(rt, tmp, nlength);

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

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

    FREEME(bufp);

    return rt;
}
// badfree/betest -- big endian tests

void
betest_orig(void)
{
#if 1
    bigdig_t b[] = { 3 };
#else
    const bigdig_t b[] = { 3 };
#endif
    int rlength;
    int exp;
    bigdig_t *res;

    exp = 3;
    printf("\n");
    printf("betest_orig: exp=%d\n",exp);

    betest_show(b,1,"b");

    rlength = 0;
    res = be_power_arr(b, 1, exp, &rlength);

    betest_show(res,rlength,"res");

    FREEME(res);
}

void
betest_trim(void)
{
    int i;
    int j;
    int length;
    const bigdig_t n[] = { 4, 5, 6 };
    const bigdig_t m[] = { 0, 3, 5 };
    const bigdig_t *num;
    bigdig_t *trimmed;

    printf("\n");
    printf("betest_trim:\n");

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

        length = 3;
        trimmed = betrim(num, &length);

        if (!trimmed)
            sysfault("betest_trim: trimmed was null\n");

        printf("pass %d: num=%p trimmed=%p\n",i,num,trimmed);
        for (j = 0; j < length; j++)
            printf(" %d", trimmed[j]);
        printf("\n");

        FREEME(trimmed);
    }
}

// betest_show -- show number
void
betest_show(bigdig_pc num,int len,const char *sym)
{
    int i;

    printf("  sym %s length %d --",sym,len);
    for (i = 0; i < len; i++)
        printf(" %d",num[i]);
    printf("\n");
}
// badfree/bgn -- big number control

// bgninit -- create new number
bgn_p
bgninit(const char *tag,u32 opt,int maxlen,...)
// opt -- options (BGNINIT* -- has initializer data)
// maxlen -- length of number
{
    va_list ap;
    int i;
    bgn_pc from;
    bgn_p num;

    va_start(ap,maxlen);

    num = calloc(1,sizeof(bgn_t));

    opt |= BGNASTRUCT;
    opt |= BGNABASE;
    num->num_opts = opt;

    num->num_tag = tag;

    num->num_refcnt = 1;

    if (maxlen <= 0)
        maxlen = 1;

    from = NULL;
    if (opt & BGNINITDUP) {
        from = va_arg(ap,bgn_pc);
        maxlen = MAX(maxlen,from->num_curlen);
    }

    num->num_maxlen = maxlen;
    num->num_base = calloc(maxlen,sizeof(bigdig_t));

    // initialize from varargs
    if (opt & BGNINITAV) {
        for (i = 0;  i < maxlen;  ++i)
            num->num_base[i] = va_arg(ap,int);
        num->num_curlen = maxlen;
    }

    // initialize by cloning data
    if (opt & BGNINITDUP) {
        maxlen = from->num_curlen;
        for (i = 0;  i < maxlen;  ++i)
            num->num_base[i] = from->num_base[i];
        num->num_curlen = maxlen;
    }

    va_end(ap);

    return num;
}

// bgnacquire -- increment reference count
bgn_p
bgnacquire(bgn_p num)
{

    num->num_refcnt += 1;

    return num;
}

// bgnrelease -- decrement reference count and deallocate
bgn_p
bgnrelease(bgn_p num)
{

    if (--num->num_refcnt == 0) {
        if (num->num_opts & BGNABASE)
            FREEME(num->num_base);

        if (num->num_opts & BGNASTRUCT)
            FREEME(num);

        // this zaps caller's pointer
        num = NULL;
    }

    return num;
}

// bgngrow -- grow allocated number to given length
void
bgngrow(bgn_p num,int newlen)
{
    int growlen;
    int maxlen;
    int i;

    BGNASSERT(num,BGNABASE);

    maxlen = num->num_maxlen;
    growlen = newlen - maxlen;

    if (growlen > 0) {
        maxlen += growlen;

        num->num_base = realloc(num->num_base,sizeof(bigdig_t) * maxlen);

        // zero extend the new area
        for (i = num->num_maxlen;  i < maxlen;  ++i)
            num->num_base[i] = 0;

        num->num_maxlen = maxlen;
    }
}

// bgnshrink -- shrink allocated number to current length
void
bgnshrink(bgn_p num)
{
    int curlen;

    BGNASSERT(num,BGNABASE);

    curlen = num->num_curlen;

    if (num->num_maxlen > curlen) {
        num->num_base = realloc(num->num_base,sizeof(bigdig_t) * curlen);
        num->num_maxlen = curlen;
    }
}
// badfree/lecom -- little endian math common

// lecopy -- copy big number
void
lecopy(bgn_p to,bgn_pc from)
{
    int newlen;
    int i;

    dbgprtf("lecopy: ENTER\n");

    dbgleshow(to,"lecopy");
    dbgleshow(from,"lecopy");

    newlen = from->num_curlen;

    bgngrow(to,newlen);

    for (i = 0; i < newlen; ++i)
        to->num_base[i] = from->num_base[i];

    to->num_curlen = newlen;

    dbgleshow(to,"lecopy");

    dbgprtf("lecopy: EXIT\n");
}

// lepad -- clone and pad number
bgn_p
lepad(bgn_pc num,int newlen)
{
    int i;
    int curlen;
    bgn_p padded;

    curlen = num->num_curlen;

#if 0
    if ((curlen < 1) || (newlen <= curlen)) {
        sysfault("lepad: length fault -- curlen=%d newlen=%d\n",curlen,newlen);
        return NULL;
    }
#endif

    BGNINIT(padded,0,newlen);
    if (!padded) {
        sysfault("lepad: bgninit returned null\n");
        return NULL;
    }

    // copy existing digits
    for (i = 0;  i < curlen;  ++i)
        padded->num_base[i] = num->num_base[i];

    // zero extend the larger number
    for (;  i < newlen;  ++i)
        padded->num_base[i] = 0;

    return padded;
}

// letrim -- get rid of leading zeroes by adjusting current length
void
letrim(bgn_p num)
{

    num->num_curlen = _letrim(num);
}

// _letrim -- get rid of leading zeroes by adjusting current length
// RETURNS: trimmed length
int
_letrim(bgn_pc num)
{
    int i;
    int curlen;

    curlen = num->num_curlen;

    for (i = curlen - 1;  i >= 0;  --i) {
        if (num->num_base[i] > 0)
            break;
    }

    if (i <= 0)
        i = 1;

    return i;
}

// leshow -- show number
void
leshow(bgn_pc num,u32 opt,const char *who)
{
    int curlen;
    int i;

    if (opt & BGNTRIM)
        curlen = _letrim(num);
    else
        curlen = num->num_curlen;

    if (who != NULL)
        printf("%s: ",who);

    printf("sym=%s ref=%d len=%d/%d",
        num->num_tag,num->num_refcnt,curlen,num->num_maxlen);

    printf(" trim=%s order=%s",
        (opt & BGNTRIM) ? "yes" : "no",
        (opt & BGNRAW) ? "raw" : "flip");

    printf("\n");

    if (who != NULL)
        printf("%s:",who);
    printf("  ");

    if (opt & BGNRAW) {
        for (i = 0; i < curlen; ++i)
            printf(" %d",num->num_base[i]);
    }

    else {
        for (i = curlen - 1;  i >= 0;  --i)
            printf(" %d",num->num_base[i]);
    }

    printf("\n");
}
// badfree/lesumeq -- little endian sum / power (in-place)

// lesumeq -- do x += y
void
lesumeq(bgn_p result,bgn_p op2)
{
    int op2len;
    int i;
    int tmp;
    int carry;
    int maxlen;
    int minlen;

    op2len = op2->num_curlen;
    tmp = result->num_curlen;
    maxlen = MAX(tmp,op2len);
    minlen = MIN(tmp,op2len);

    bgngrow(result,maxlen + 1);

    carry = 0;
    i = 0;

    for (;  i < minlen;  ++i) {
        tmp = result->num_base[i] + op2->num_base[i] + carry;

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

        result->num_base[i] = tmp;
    }

    ++maxlen;

    for (;  i < maxlen;  ++i) {
        if (! carry)
            break;

        tmp = result->num_base[i] + carry;

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

        result->num_base[i] = tmp;
    }

    result->num_curlen = maxlen;
}

// le_power_arr_eq -- raise number to power
bgn_p
le_power_arr_eq(bgn_pc num,int exp)
{
    bgn_p rtpwr;
    bgn_p bufp;
    int icur;
    int ilim;

    BGNINIT(rtpwr,BGNINITDUP,1000,num);
    BGNINIT(bufp,BGNINITDUP,1000,num);

    ilim = num->num_base[0];
    ilim -= 1;

    while (--exp > 0) {
        for (icur = 0;  icur < ilim;  ++icur)
            lesumeq(rtpwr,bufp);
        lecopy(bufp,rtpwr);
    }

    BGNRELEASE(bufp);

    return rtpwr;
}
// badfree/lesumrt -- little endian sum / power (alloc/return mode)

bgn_p
lesumrt(bgn_p n,bgn_p m)
{
    int i;
    int tmp;
    int carry;
    int maxlen;
    bgn_p rtsum;
    bgn_p op1;
    bgn_p op2;

    dbgprtf("lesumrt: ENTER\n");

    dbgleshow(n,"lesumrt");
    dbgleshow(m,"lesumrt");

    // case (1)
    if (n->num_curlen == m->num_curlen) {
        op1 = BGNACQUIRE(n);
        op2 = BGNACQUIRE(m);
    }

    // case (2)
    else if (n->num_curlen > m->num_curlen) {
        op1 = BGNACQUIRE(n);
        op2 = lepad(m,n->num_curlen);
    }

    // case (3)
    else {
        op1 = BGNACQUIRE(m);
        op2 = lepad(n,m->num_curlen);
    }

    maxlen = MAX(n->num_curlen,m->num_curlen);
    dbgprtf("lesumrt: PAD maxlen=%d\n",maxlen);

    BGNINIT(rtsum,0,maxlen + 1);

    carry = 0;

    for (i = 0;  i < maxlen;  ++i) {
        tmp = op1->num_base[i] + op2->num_base[i] + carry;

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

        rtsum->num_base[i] = tmp;
    }

    rtsum->num_base[i] += carry;
    rtsum->num_curlen = maxlen + 1;

    BGNRELEASE(op1);
    BGNRELEASE(op2);

    dbgleshow(rtsum,"lesumrt");

    dbgprtf("lesumrt: EXIT\n");

    return rtsum;
}

bgn_p
le_power_arr_rt(bgn_pc n, int exp)
{
    bgn_p tmp;
    bgn_p rtpwr;
    bgn_p bufp;
    int icur;
    int ilim;

    dbgprtf("le_power_arr_rt: ENTER\n");

    BGNINIT(rtpwr,BGNINITDUP,1000,n);
    BGNINIT(bufp,BGNINITDUP,1000,n);

    ilim = n->num_base[0];
    ilim -= 1;

    while (--exp > 0) {
        for (icur = 0;  icur < ilim;  ++icur) {
            tmp = lesumrt(rtpwr,bufp);
            if (tmp == NULL)
                sysfault("le_power_arr_rt: null lesumrt return\n");

            lecopy(rtpwr,tmp);

            BGNRELEASE(tmp);
        }

        lecopy(bufp,rtpwr);
    }

    BGNRELEASE(bufp);

    dbgprtf("le_power_arr_rt: EXIT\n");

    return rtpwr;
}
// badfree/letest -- little endian tests

void
letest_lesumrt(void)
{
    bgn_p b;
    int exp;
    bgn_p res;

    exp = 3;
    printf("\n");
    printf("letest_lesumrt: exp=%d\n",exp);

    BGNINIT(b,BGNINITAV,1,3);
    leshow(b,0,"letest_lesumrt");

    res = le_power_arr_rt(b,exp);
    leshow(res,0,"letest_lesumrt");

    BGNRELEASE(res);
    BGNRELEASE(b);
}

void
letest_lesumeq(void)
{
    bgn_p b;
    int exp;
    bgn_p res;

    exp = 3;
    printf("\n");
    printf("letest_lesumeq: exp=%d\n",exp);

    BGNINIT(b,BGNINITAV,1,3);
    leshow(b,0,"letest_lesumeq");

    res = le_power_arr_eq(b,exp);
    leshow(res,0,"letest_lesumeq");

    BGNRELEASE(res);
    BGNRELEASE(b);
}
// badfree/util -- utility functions

void
_sysfault(void)
{

    exit(1);
}

void
reverse(bigdig_p arr, int length)
{
    int lhs;
    int rhs;
    bigdig_t tmp;

    for (lhs = 0, rhs = length - 1;  lhs < rhs;  ++lhs, --rhs) {
        tmp = arr[lhs];
        arr[lhs] = arr[rhs];
        arr[rhs] = tmp;
    }
}

int opt_fork;
int opt_T;

// main -- main program
int
main(int argc, char **argv)
{
    char *cp;
    tstctl_p tst;
    int tstno;

    --argc;
    ++argv;

    for (; argc > 0; --argc, ++argv) {
        cp = *argv;
        if (*cp != '-')
            break;

        switch (cp[1]) {
        case 'f':
            opt_fork = 1;
            break;

        case 'h':
            usage();
            break;

        case 'T':
            opt_T = atoi(cp + 2);
            break;

        default:
            usage();
            break;
        }
    }

    if (opt_T <= 0)
        opt_T = 1;

    for (FORTSTALL(tst))
        tst->tst_allow = (argc <= 0);

    for (; argc > 0; --argc, ++argv) {
        cp = *argv;
        for (FORTSTALL(tst)) {
            if (strcmp(tst->tst_tag, cp) == 0)
                tst->tst_allow = 1;
        }
    }

    for (tstno = 0; tstno < opt_T; ++tstno)
        dotests();

    return 0;
}

// dotests -- perform tests
void
dotests(void)
{
    tstctl_p tst;
    pid_t pid;
    int status;

    for (FORTSTALL(tst)) {
        if (!tst->tst_allow)
            continue;

        if (!opt_fork) {
            dotest(tst);
            continue;
        }

        pid = fork();
        if (pid) {
            waitpid(pid, &status, 0);
            continue;
        }

        dotest(tst);
        exit(0);
    }
}

// dotest -- perform test
void
dotest(tstctl_p tst)
{

    tst->tst_fnc();
}

// usage -- show usage
void
usage(void)
{
    tstctl_pc tst;

    printf("usage: [options] [test names]\n");

    printf("\n");
    printf("options:\n");
    printf("  -f -- run tests in forked child\n");
    printf("  -T<repeat> -- test repeat count\n");

    printf("\n");
    printf("tests:\n");

    for (FORTSTALL(tst))
        printf("  %s\n", tst->tst_tag);

    exit(1);
}

【讨论】:

  • 如果您喜欢该代码,但希望将其作为单独的文件,我可以将其作为自扩展存档发布到 pastebin
猜你喜欢
  • 1970-01-01
  • 2010-11-01
  • 2020-12-24
  • 2014-11-10
  • 2013-05-06
  • 2017-02-26
  • 1970-01-01
  • 2023-02-23
  • 2013-12-04
相关资源
最近更新 更多