C中神秘的sprintf行为:第二个lld参数始终打印为零(0)
我在C语言中使用sprintf时遇到了一个完全奇怪的问题,这似乎超出了我的基本调试能力。基本上,我正在使用一个简单的单元测试框架(CuTest)向一个丑陋的(没有文档记录,没有单元测试)代码库添加一些测试。我添加了一种新类型的单元测试,它基本上复制了已经存在的单元测试,但代码中使用的64位整数除外 此测试通常有效(正确评估相等比较),但如果失败,由于sprintf问题,它不会生成正确的错误消息。函数如下所示:C中神秘的sprintf行为:第二个lld参数始终打印为零(0),c,printf,C,Printf,我在C语言中使用sprintf时遇到了一个完全奇怪的问题,这似乎超出了我的基本调试能力。基本上,我正在使用一个简单的单元测试框架(CuTest)向一个丑陋的(没有文档记录,没有单元测试)代码库添加一些测试。我添加了一种新类型的单元测试,它基本上复制了已经存在的单元测试,但代码中使用的64位整数除外 此测试通常有效(正确评估相等比较),但如果失败,由于sprintf问题,它不会生成正确的错误消息。函数如下所示: /* Comparison Function for U64 Numbers */ v
/* Comparison Function for U64 Numbers */
void CuAssertU64Equals_LineMsg(CuTest* tc, const char* file, int line, const char* message, u64 expected, u64 actual) {
char buf[STRING_MAX];
if (expected == actual) return;
sprintf(buf, "expected <%lld> but was <%lld>", expected, actual);
CuFail_Line(tc, file, line, message, buf);
}
U64数字的比较函数*/
void CuAssertU64Equals_LineMsg(CuTest*tc,常量字符*文件,int行,常量字符*消息,预期为u64,实际为u64){
char buf[STRING_MAX];
如果(预期==实际)返回;
sprintf(buf,“预期的,但过去的”,预期的,实际的);
CUU行(tc、文件、行、消息、buf);
}
(注意:字符串_MAX为512,因此应该足够大)。
(注2:在我目前使用的Cygwin系统上,u64是一个“long-long-int”变量)
当该测试失败时,产生的错误消息是奇怪的部分。不管“实际”的值是多少,它都会在该点打印0。因此,给定expected=1和actual=2,消息将是:
"expected <1> but was <0>"
“应为,但为”
如果切换参数的位置,并使其状态如下:
sprintf(buf, "actually <%lld> but expected <%lld>", actual, expected);
sprintf(buf,“实际但预期”,实际,预期);
您将获得以下输出:
"actually <2> but expected <0>"
“实际但预期”
不用说,这没有什么意义,似乎表明了某种奇怪的堆栈错误?老实说,我只是完全不清楚这种错误是如何发生的——即使在原则上也是如此。我用最可爱的代码制作了一个小的工作示例,它工作正常(没有将第二个设置为零)。这表明,既不是最可爱的,也不是功能本身的问题
但是,当与实际的代码库一起使用时,它会遇到这个问题。与环境(堆栈、内存或变量)相关的内容就是问题所在
有人知道为什么会发生这种情况吗?我目前的候选理论是:
1.尝试读取数据时,sprintf函数中出现下溢/溢出。但我不确定这将如何发生,因为传递到函数中的任何数据都是按值传递的。此外,数据本身显然存在——如果切换顺序,我可以看到每个值
static char *CuPrintU64(char* buffer, u64 value) {
do
*--buffer = value % 10 + '0';
while(value /= 10);
return buffer;
}
/* Comparison Function for U64 Numbers */
void CuAssertU64Equals_LineMsg(CuTest* tc, const char* file, int line, const char* message, u64 expected, u64 actual) {
static const char first[] = "expected ";
static const char second[] = " but was ";
char buf[STRING_MAX], *ptr = &buf[sizeof buf];
if(expected == actual) return;
/* sprintf(buf, "expected <%llu> but was <%llu>", expected, actual); */
*--ptr = '\0';
ptr = CuPrintU64(ptr, actual);
ptr = memcpy(ptr - second, sizeof second - 1);
ptr = CuPrintU64(ptr, expected);
ptr = memcpy(ptr - first, sizeof first - 1);
CuFail_Line(tc, file, line, message, ptr);
}
static char*CuPrintU64(char*buffer,u64值){
做
*--缓冲区=值%10+“0”;
而(值/=10);
返回缓冲区;
}
/*U64数字的比较函数*/
void CuAssertU64Equals_LineMsg(CuTest*tc,常量字符*文件,int行,常量字符*消息,预期为u64,实际为u64){
静态常量char first[]=“应为”;
static const char second[]=“but was”;
char buf[STRING_MAX],*ptr=&buf[sizeof buf];
如果(预期==实际)返回;
/*sprintf(buf,“预期的,但过去的”,预期的,实际的)*/
*--ptr='\0';
ptr=CuPrintU64(ptr,实际值);
ptr=memcpy(ptr-second,sizeof second-1);
ptr=铜64(预期为ptr);
ptr=memcpy(ptr-first,sizeof-first-1);
CuFail_行(tc、文件、行、消息、ptr);
}
这太奇怪了。我建议在valgrind下运行该程序,看看是否会出现内存错误。我应该注意,我已经找到了u64数据类型的定义位置,并且它肯定是一个长整型,至少在Cygwin下编译时是这样的(代码有一组编译器ifdef,可以根据不同的操作系统对其进行调整)。有人会认为它是一个无符号的值,但它实际上是有符号的(考虑到代码中一些非常奇怪的命名选择,这并不奇怪)?如前所述,这个问题只在使用大量代码库时才会出现,我认为没有人想要10页长的编译器日志?相关文件没有显示我能看到的任何值得注意的内容,没有警告或类似的内容。尝试显式地显示,但在此编译器模式下它根本无法识别格式(字面上写为“I64d”)。谢谢你在这里扔东西。至少是一个有趣的解决方案。现在我的解决方法实际上是执行两个单独的sprintf语句来分离缓冲区,然后组合缓冲区(这很好)。不过,如果第一部分开始出现故障,这绝对是一种有用的方法。@Namey我怀疑您会发现%lld
实际上被视为%ld
,并且只读取64位整数的一半,所以这不是一个很好的解决方法(特别是如果您希望打印任何大于LONG_MAX
)的数字。如果您不希望打印任何如此大的值,更好的解决方案可能是使用%lu
并强制转换va