Andrew Cheong
Andrew Cheong

Reputation: 30293

Why does this output seem non-deterministic? (Is it the sprintf, printf, or syntax of hexadecimal literals?)

tl;dr

Why do I get different output every time I run this code (Ideone):

#include <iostream>
#include <stdio.h>
using namespace std;

int main() {

    const char* _user = "FOO";
    const char* _password = "BAR";

    char login[21];
    sprintf(login,
        "\x15\x00\x01%-8s%-10s",
        _user,
        _password);

    for (int i = 0; i < 21; i++) {
        printf(" %02x", login[i] & 0xff);
    }

    return 0;
}

But not this code (Ideone):

#include <iostream>
#include <stdio.h>
using namespace std;

int main() {

    const char* _user = "FOO";
    const char* _password = "BAR";
    const char* _session = "ABCDEFGHIJ";
    int _expectedSeq = 123;

    char login[38];
    sprintf(login,
            "L%-6s%-10s%10s%10d\xA",
            _user,
            _password,
            _session,
            _expectedSeq);

    for (int i = 0; i < 38; i++) {
        printf(" %02x", login[i] & 0xff);
    }

    return 0;
}

Question

Deep in our application code, I came across this:

char login[38];
sprintf(login,
        "L%-6s%-10s%10s%10d\xA",
        _user,
        _password,
        _session,
        _expectedSeq);

Now, I need to write a (simpler) variant of this code:

char login[21];
sprintf(login,
       "\x15\x00\x01%-8s%-10s",
       _user,
       _password);

Somehow, this doesn't work! What's weird is that the latter produces different results every time.


Thoughts

  1. The former example only has a hex literal at the end. Is this masking the issue in the former's case?

  2. Or, am I actually messing up my debug output, printf? (By the way, I got the & 0xff thing from Printing hexadecimal characters in C.)

  3. Could it have something to do with using char instead of unsigned char? But then, why does the former case work?

Upvotes: 2

Views: 354

Answers (2)

Dietmar K&#252;hl
Dietmar K&#252;hl

Reputation: 154005

Your issue is that second format string contains a null character (\x00) which terminates it prematurely. Change the string to use %c instead and have a null character printed there.

Upvotes: 2

rodrigo
rodrigo

Reputation: 98486

The problem is that your string literal has an embedded NUL byte, and that marks the end of the string as far as sprintf is concerned. So your call is identical to:

sprintf(login,
       "\x15",
       _user,
       _password);

And that writes into the login array only two bytes: 0x15 0x00.

There are several approaches to solve this mixing of bytes and characters. My choice would be something along the lines of:

memcpy(login, "\x15\x00\x01", 3);
sprintf(login + 3,
   "%-8s%-10s",
   _user,
   _password);    

The call to memcpy takes as parameter the number of bytes, so it is immune to the embedded NUL problem.

But note that sprintf automaticall adds a NUL byte at the end of the output string, so you actually need 22 bytes: 3 + 8 + 10 + 1 = 22:

char login[22];

Upvotes: 6

Related Questions