C中的神秘sprintf行为:第二个lld参数始终打印为零(0)

时间:2011-04-15 04:21:59

标签: c printf

我在C语言中遇到了一个彻头彻尾的奇怪问题,这似乎超出了我的基本调试能力。基本上,我正在使用一个简单的单元测试框架(CuTest)来为丑陋的(未记录的,无单元测试)代码库添加一些测试。我添加了一种新类型的单元测试,它基本上复制了已经存在的单元测试 - 但是对于代码中使用的64位整数。

此测试通常工作(正确评估相等比较),但是当它失败时,由于sprintf问题,它不会产生正确的错误消息。该函数如下所示:

/* 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);
}

(注意:STRING_MAX是512,所以它应该足够大)。 (注2:在我正在使用的Cygwin系统上,u64是一个“long long int”变量)

当此测试失败时,产生的错误消息是奇怪的部分。无论“实际”的值是什么,它都会在该位置打印0。因此,给定expect = 1和actual = 2,消息将为:

"expected <1> but was <0>"

如果你切换参数的位置,并使其状态如下:

sprintf(buf, "actually <%lld> but expected <%lld>", actual, expected);

你得到输出:

"actually <2> but expected <0>"

毋庸置疑,这没什么意义,似乎表明某种奇怪的堆栈错误可能?说实话,我完全不清楚这样的错误是如何发生的 - 即使在原则上也是如此。我用CuTest代码做了一个小工作示例,它工作正常(没有将第二个设置为零)。这表明既不是CuTest,也不是函数本身就是问题。

但是,当与实际代码库一起使用时,它会遇到此问题。与环境(堆栈,内存或变量)相关的问题就是问题。

有没有人知道为什么会这样?我目前的候选理论是: 1.尝试读取数据时sprintf函数中的下溢/溢出。我不确定如何发生这种情况,因为传递给函数的任何数据都是按值计算的。此外,数据本身显然存在 - 如果我切换订单,我可以看到每个值。

  1. 我以某种方式使用了错误的格式。我非常确定lld对于长期的int来说是正确的(并且在我的最小工作示例中工作)但也许它很脆弱。

  2. 完全有关某种堆栈损坏。当然希望不是这个,因为我每周只工作20个小时。我怀疑我可以调试整个代码库来找出这么大的东西。

  3. 我目前正在cygwin环境中使用gcc-3进行编译,以获得它的价值。任何猜测都会很棒,我知道基本上不可能在没有看到整个代码库的情况下专门诊断它,但即使是一些关于调试这类问题的引导也会很棒。

1 个答案:

答案 0 :(得分:4)

尝试使用“%I64d”作为格式字符串。我不确定Cygwin但我知道Visual Studio标准库的printf函数中的MinGW链接,造成各种各样的破坏。请注意任何新的C99功能和类型,如long double或size_t格式。

在这方面,有什么值得替换printf:

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);
}