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;
...
}
注意:dir
从strcpy(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"
。
提前感谢大家的时间和耐心。
答案 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()
可能会在不知道需要多少空间的情况下返回。在这种情况下,您必须创建一个循环,该循环会连续增长您的内存块直到它工作,并再次调整它。