【问题标题】:Strange Behavior From realloc()来自 realloc() 的奇怪行为
【发布时间】:2014-03-29 22:53:57
【问题描述】:
  • 编译器:GCC 4.7.2 (Debian 4.7.2-5)
  • 平台:Linux 3.2.0 x86 (Debian Wheezy)

编辑:请参阅下面的答案代码已更新并经过测试。它再次正确运行,感谢 WhozCraig。

我正在编写一个简单的头文件,其中包含动态操作宽字符串的函数。例如,我的标头包含一个复制功能,如果需要,它会扩展目标缓冲区。我正在编写的当前函数仅将提供的缓冲区的一部分复制到提供的目标缓冲区中。最初,我使用的目标缓冲区对于我的测试用例来说足够大,并且该函数完全没有问题。但我想测试我的 realloc() 逻辑,所以我将目标缓冲区的大小设置为 1,以便函数必须 realloc() 目标缓冲区。但在我看来 realloc() 正在改变目标缓冲区内容的值。我已将错误跟踪到第三个 realloc() 调用。这是我一直在使用的函数和测试用例。

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

#define DWS_DEF_SIZ 16

#define DWS_ENOMEM -1
#define DWS_EINVAL -2

int scpydws(wchar_t* *des, size_t *siz, int argc, wchar_t *src, size_t num, ...)
{   
    size_t i = 0;
    size_t j = 0;
    va_list argv;
    wchar_t *tmp = NULL;
    size_t x = 0;

    if(des == NULL) return DWS_EINVAL;
    if(siz == NULL) return DWS_EINVAL;
    if(*siz == 0 && *des != NULL) return DWS_EINVAL;

    if(*des == NULL)
    {
        if(*siz == 0)
        {
            if((*des = malloc(DWS_DEF_SIZ * sizeof(wchar_t))) == NULL) return DWS_ENOMEM;

            *siz = DWS_DEF_SIZ;
        }
        else if((*des = malloc(*siz * sizeof(wchar_t)) == NULL) return DWS_ENOMEM;
    }

    for(va_start(argv, num); argc > 0; argc--, src = va_arg(argv, wchar_t*), num = va_arg(argv, size_t))
    {
        if(src == NULL || src[0] == 0) continue;

        for(j = 0; j < num; j++, i++)
        {
            for(x = 0; x < i; x++) putwchar((*des)[x]);
            wprintf(L" | %i == %i\n", i, *siz - 1);

            if(i == *siz - 1)
            {
                if((tmp = realloc(*des, *siz * 2 * sizeof(wchar_t))) == NULL)
                {
                    va_end(argv);
                    return DWS_ENOMEM;
                }

                *siz *= 2;
                *des = tmp;
            }

            (*des)[i] = src[j];
        }
    }

    (*des)[i] = 0;
    va_end(argv);
    return 0;
}

int main()
{
    int ret = 0;
    size_t siz = 1;
    wchar_t *des = NULL;
    wchar_t src1[] = L"123456789";
    wchar_t src2[] = L"abcdefghijklmnopqrstuvwxyz";
    wchar_t src3[] = L"blahblah";

    //The syntax is fairly straight forward the numbers following the
    //buffers represent how many characters to copy into the destination buffer
    ret = scpydws(&des, &siz, 3, src1, 2, src2, 3, src3, wcslen(src3));
    wprintf(L"ret = %i\n", ret);

    return 0;
}

这是测试用例的输出。一切正常,直到第三个 realloc() "|" 之前的字符表示函数操作期间的目标缓冲区。 “|”后面是什么表示目标缓冲区的函数索引变量(“==”左侧的整数)和目标缓冲区的最后一个有效索引(“==”右侧的整数)之间的比较,如果语句为真,则函数将重新分配()目标缓冲区。所以我的问题基本上是“Y”到底是从哪里来的(见下文)?另外,正如我旁注的那样,我在 Windows Vista SP3 系统上使用 MinGW-GCC 编译了代码(我不知道 MinGW-GCC 版本是什么),而不是随机的“Y”字符,我从 realloc() 得到了一个 ENOMEM 错误,具有讽刺意味的是在第三次 realloc() 调用中,但我并不关心为什么会发生这种情况。

 | 0 == 0
1 | 1 == 1
12 | 2 == 3
12a | 3 == 3
12ab | 4 == 7
12abc | 5 == 7
12abcb | 6 == 7
12abcbl | 7 == 7 Note: The function realloc()s here
12abcYla | 8 == 15 Note: Notice how the 'b' changes to a 'Y'?
12abcYlah | 9 == 15
12abcYlahb | 10 == 15
12abcYlahbl | 11 == 15
12abcYlahbla | 12 == 15
ret = 0

【问题讨论】:

  • 我会首先根据wchar_ts 的计数来分配字节,而不仅仅是一些计数。您意识到在 Windows 上 wchar_t 的宽度是 2 字节,对吧?一旦你越过num/2wchar_t 屏障,你就会写入未分配的内存(可能是堆链)并调用UB。
  • @WhozCraig 你是我太习惯于字符的人了,以至于我忘记了 malloc() 和 realloc() 接受字节数的参数。我更新了我的代码,问题解决了,非常感谢。
  • 没问题。发表一个答案,我会加分的。这是你的代码,我懒得做=P
  • “而不是随机的 'Y' 字符,我得到了一个 ENOMEM 错误 [..] 但我并不关心为什么会发生这种情况”。这应该是一个线索。在我打字时解决了,但下次使用 valgrind 之类的工具。
  • @JohnVulconshinz 一个很棒的工具,不幸的是你不能在 Windows 上使用它。 There are alternatives,包括内置的 CRT 内存管理功能,如果你是一个真正的惩罚者。

标签: c linux glibc


【解决方案1】:

malloc()realloc() 的大小参数需要乘以 sizeof(wchar_t)

我改变了这个:

*des = malloc(DWS_DEF_SIZ))

到这里:

*des = malloc(DWS_DEF_SIZ * sizeof(wchar_t)))

还有这个:

tmp = realloc(*des, *siz * 2)

到这里:

tmp = realloc(*des, *siz * 2 * sizeof(wchar_t))

感谢WhozCraig

【讨论】:

    猜你喜欢
    • 2016-07-30
    • 2018-10-22
    • 1970-01-01
    • 1970-01-01
    • 2020-02-10
    • 2014-01-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多