malloc:使用printf和NULL wchar_t*双错误

Vin*_*ent 1 c macos printf gcc

我正在尝试将应用程序从Linux移植到Mac Os X(leopard),但是当我执行它时,我有这样的错误消息:malloc: *** error for object 0x100160 : double free.

我用下面的代码重现了这个问题:

//main.cpp 
#include <stdio.h>
#include <wchar.h>

int main(int argc, char*argv[])
{
    wchar_t *b=NULL;
    printf("a=%ls, b=%ls \n", L"a", b);
}
Run Code Online (Sandbox Code Playgroud)

用gcc编译:

gcc main.cpp -o test
Run Code Online (Sandbox Code Playgroud)

执行的输出:

a=a, b=(null)
test (5337) malloc: *** error for object 0x100160 : double free
*** set a breakpoint in malloc_error_break to debug
Run Code Online (Sandbox Code Playgroud)

这很奇怪,因为如果我使用这一行:printf("a=%ls, b=%ls", b, b),不会打印错误.而且,我不能用wprintf(L"a=%ls, b=%ls", a, b).在Fedora 13上,该程序不会输出任何错误.

这是一个printf bug吗?如何删除此错误?

Ada*_*eld 6

您不能将NULL指针打印为字符串,这是未定义的行为.根据C99标准,§7.19.6.1/ 8

转换说明符及其含义为:
...
s      如果不存在l长度修饰符,则参数应为指向字符类型数组的初始元素的指针....

如果存在l长度修饰符,则参数应该是指向wchar_t类型数组的初始元素的指针.

由于未明确允许NULL指针,因此不允许使用它们.您应该将代码更改为:

printf("a=%ls, b=%ls \n", L"a", b ? b : L"(null)");
Run Code Online (Sandbox Code Playgroud)

  • @Al Kepp未定义的行为被允许做任何事情.包括"似乎工作",崩溃,损坏内存,使您的打印机着火.其他函数,例如strlen,strcpy,如果以未定义的方式使用它们,通常只会导致段错误.printf碰巧破坏了内存 (4认同)
  • 当代码导致未定义的行为时,所有的赌注都被关闭 - 没有人会列出无效代码可能在所有不同平台上造成的所有影响.腐败堆栈或堆只是可能的. (2认同)

小智 5

这只是一个错误vprintf_l,处理printf(可能是它的所有朋友).

严格来说,图书馆有权通过做任何它喜欢的事情来处理这种情况,包括堆腐败,但是根据代码来判断意图 - 对于任何非垃圾printf实现应该是这种情况- 是NULL明智地处理字符串.代码中只有一个错误就是这样做.这些事情发生了.

如果在打印了NULL前一个非宽字符指针后尝试打印宽字符指针NULL,vprintf_l则会在下一个字符串参数或vprintf_l退出时启动.(可能还有其他方法可以实现这一点,我想 - 我没有检查.)

违规代码在这里:

    case 's':
        if (flags & LONGINT) {
            wchar_t *wcp;

            if (convbuf != NULL)
                free(convbuf);
            if ((wcp = GETARG(wchar_t *)) == NULL)
                cp = "(null)";
            else {
                convbuf = __wcsconv(wcp, prec, loc);
                if (convbuf == NULL) {
                    fp->_flags |= __SERR;
                    goto error;
                }
                cp = convbuf;
            }
        } else if ((cp = GETARG(char *)) == NULL)
Run Code Online (Sandbox Code Playgroud)

如果GETARG(wchar_t *)返回NULL,convbuf将指向旧的(现在释放的)缓冲区.然后,当函数退出时,有一个双重释放:

if (convbuf != NULL)
    free(convbuf);
Run Code Online (Sandbox Code Playgroud)

如果有另一个字符串参数,则同样适用,但在这种情况下,case 's'上面的代码中会发生双重自由:

printf("a=%ls, b=%ls c=%ls\n", L"a", b, L"c");
Run Code Online (Sandbox Code Playgroud)

当然,解决办法是设置convbufNULL它被释放之后.

printf代码是在这里:

http://www.opensource.apple.com/source/Libc/Libc-594.1.4/stdio/vfprintf-fbsd.c

通过反汇编来判断,它是用于Snow Leopard中默认运行时的代码.