开发者

Mysterious sprintf behavior in C: Second lld param prints as zero (0) always

开发者 https://www.devze.com 2023-02-25 04:23 出处:网络
I\'m running into a downright bizarre issue with sprintf in C that seems beyond my basic abilities to debug.Basically, I\'m using a simple unit test framework (CuTest) to add some tests to an ugly (un

I'm running into a downright bizarre issue with sprintf in C that seems beyond my basic abilities to debug. Basically, I'm using a simple unit test framework (CuTest) to add some tests to an ugly (undocumented, no unit tests) code base. I added a new type of unit test, which basically duplicates the ones that are already present- but for the 64 bit integers used in the code.

This test works in general (correctly evaluates equality comparisons), but when it fails it does not produce the correct error message due to the sprintf issue. The function looks like this:

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

(Note: STRING_MAX is 512, so it should be plenty large). (Note 2: On the Cygwin system I'm currently working with, a u64 is a "long long int" variable)

When this tests fails, the error message produced is the weird part. Regardless of what the value of "actual" is, it prints 0 in that spot. So given expected = 1 and actual = 2, the message would be:

"expected <1> but was <0>"

If you switch the positions of the arguments, and make it state something like:

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

You'd get the output:

"actually <2> but expected <0>"

Needless to say, this makes very little sense and seems to indicate some sort 开发者_如何学运维of weird stack error maybe? To be quite honest, I am just entirely unclear on how such an error could occur- even in principle. I made a small working example with the CuTest code and it worked properly (did not set the second one to zero). This indicates that the neither CuTest, nor the function itself is the issue.

However, when used with the actual code base it encounters this issue. Something related to the environment (stack, memory, or variables) is the issue.

Does anyone have a clue as to why this would happen? My current candidate theories are: 1. Underflow/overflow in the sprintf function when trying to read the data. I'm not sure how this would occur though, since any data passed into the function is by value. Moreover, the data itself clearly exists- I can see each value if I switch the order.

  1. I'm using the incorrect formatting somehow. I was pretty sure lld was correct for a long long int (and works in my minimal working example) but maybe it's fragile.

  2. Full on stack corruption of some sort. Sure hope its not this, because I'm only on this project 20 hours a week. I doubt I could debug the whole code base to figure out something of this magnitude.

I'm currently compiling using gcc-3 in a cygwin environment, for what it's worth. Any guesses would be great, I know it's basically impossible to diagnose it specifically without seeing the whole code base, but even some leads about debugging this sort of issue would be great.


Try "%I64d" as a format string instead. I'm not certain of Cygwin but I know MinGW links in the printf function from Visual Studio's standard library, causing all sorts of havoc. Beware of any new C99 features and types such as long doubles or size_t formats.

For what it's worth here's a replacement for printf in this context:

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

精彩评论

暂无评论...
验证码 换一张
取 消