stdarg.h功能似乎错误地连接了参数

时间:2014-04-24 16:36:03

标签: c variadic-functions

TL; DR:mprintf("%s and %s", arg1, arg2)似乎使用"arg1arg2 and arg2" 中定义的"arg1 and arg2"宏打印va_*而不是stdarg.h

大家好。我正在使用vsprintf提供的功能,如下所示:

exits.c

#include "../../utils/printutil.c"

...

char dir[4][5];
char* format;

...

switch(bitcount) {

    ...

    case 2: format = "%s and %s";
        mprintf(format, dir[0], dir[1]);
    break;

    ...
}

注意:dirstrcpy(dir[bitcount], names[dircount]);获取其值,其中names只是一个char指针数组,dir[0] = "North"dir[1] = "East",{ {1}}和dir[2] = South"


printutil.c

dir[3] = "West"

注意:/* Utils file to provide common utilities not tied specifically to any aspect of the software */ #include "printutil.h" #include <stdio.h> #include <stdlib.h> #include <stdarg.h> char* mprintf(const char * format, ...) { /* Prints a statement containing a format and then multiple arguments. */ char *buf; va_list args; va_start (args, format); buf = malloc(sizeof(format) + sizeof(args)); vsprintf (buf, format, args); va_end (args); return buf; } 只包含函数原型


这就是代码的结构。在switch语句的情况下,指定格式字符串,然后使用格式和args dir [0]和dir [1]调用printutil.h(在printutil.c中定义),两个变量是成功写入exits.c。

的早期版本

使用gdb,我能够辨别传递给mprintf()的值是否符合预期:

mprintf()

当我进入Breakpoint 1, exitstr (exits=5 '\005') at exits.c:33 33 switch(bitcount) { (gdb) s 38 case 2: format = "%s and %s"; (gdb) s 39 mprintf(format, dir[0], dir[1]); (gdb) p format $1 = 0x403115 "%s and %s" (gdb) p dir[0] $2 = "North" (gdb) p dir[1] $3 = "South" 函数时,gdb显示格式的内容与它们应该完全一样,并显示mprintf()的内容如下:

va_list args

我根据c {{{}}和vprintf的cplusplus.com参考中的示例构建了此代码,它们都表明我已正确使用17 vsprintf (buf, format, args); (gdb) p format $4 = 0x403115 "%s and %s" (gdb) p args $5 = {{gp_offset = 8, fp_offset = 48, overflow_arg_area = 0x7fffffffe710, reg_save_area = 0x7fffffffe650}} 中定义的函数和宏

然而,在踩到stdarg.h行后,打印vsprintf()的内容会产生问题的根源。即第二个参数似乎与第一个参数连接,然后第二个参数重新用于第二个参数。

buf

奇怪的是,这似乎只发生在北方和北方的情况下。或者&#39; South&#39;是第一个论点。如果&#39; East&#39;或者&#39; West&#39;是第一个参数,参数正确打印到(gdb) print buf $7 = 0x63ca50 "NorthSouth and South"


提前感谢大家的时间和耐心。

2 个答案:

答案 0 :(得分:2)

buf = malloc(sizeof(format) + sizeof(args));

这应该做什么? sizeof (format)只是指针的大小,32位系统为4个字节,64位为8个字节。 sizeof (args)只是sizeof (va_list)的另一个名称,它是一个实现定义的类型。但是,您将其用作字符串的预期大小。

可能你会溢出这个缓冲区并遇到未定义的行为。

使用snprintf的变体总是更好,它们采用指定的输出缓冲区大小。

编辑:此外,正如@Mahonri注意到的那样,您已将字符串"North"放入一个空格中仅包含5个字符的数组,这会丢弃终止的NUL字节。这导致sprintf超出字符串的预期结束。我原以为它会打印NorthEast,但它仍然只是未定义的行为。

答案 1 :(得分:0)

在最近的系统上,snprintf()会告诉您所需的字节数,即使它没有其他任何内容。这使您的代码像

一样简单
char* mprintfv(const char * format, va_list ap) {
    // This version should always be given for the case someone wants to build on it.

    va_list ap2;

    va_copy(ap2, ap);
    size_t length = vsprintf(NULL, format, ap2);
    va_end(ap2);
    if (size_t < 0) return NULL;
    char *buf = malloc(length + 1);
    if (!buf) return NULL;
    vsprintf(buf, format, ap2);
    return buf;
}

char* mprintf(const char * format, ...) {
    /* Prints a statement containing a format and then multiple arguments. */

    va_list args;

    va_start (args, format);
    char *buf = mprintfv(format, args);
    va_end(args);

    return buf;
}

但是,在较旧的系统上,sprintf()可能会在不知道需要多少空间的情况下返回。在这种情况下,您必须创建一个循环,该循环会连续增长您的内存块直到它工作,并再次调整它。