Invader Zim
Invader Zim

Reputation: 838

Same code for printf, different output?

I have a struct:

typedef struct cbor {
  cbor_type type;

  uint64_t length; //x2 for maps

  struct cbor* child;
  struct cbor* next;
  struct cbor* parent;

  union {
    const uint8_t* bytes;
    long sint;
    uint64_t uint;
    double real;
  } value;
} cbor;

I initialize a pointer to cbor with malloc, and set length to 0, and value uint and sint to 1, and type to CBOR_INTEGER.

I have a function that should print info about cbor struct:

void cbor_print_item(cbor* item){
    os_printf("c2 = %d %d %d\n", item->value.sint, item->value.uint, item->length);

    switch(item->type){
        case CBOR_INTEGER:
            os_printf("type:integer\nsval:%d\nuval:%d\nlength:%d\n", item->value.sint, item->value.uint, item->length);
            os_printf("type:integer\n");
            os_printf("sval:%d\n", item->value.sint);
            os_printf("uval:%d\n", item->value.uint);
            os_printf("length:%d\n", item->length);
            break;
        case CBOR_STRING:
            os_printf("type:\tstring\n");
            os_printf("val:\t%s\n", item->value.bytes);
            os_printf("length:\t%d\n", item->length);
            break;
        case CBOR_REAL:
            os_printf("type:\treal\n");
            os_printf("length:\t%d\n", item->length);
            os_printf("val:\t%f\n", item->value.real);
            break;
        default:
            os_printf("error!\n");
    }
}

However, I don't get the expected output. The output for printf's in switch should be the same, right? My output is:

c2 = 1 1 0
type:integer
sval:1
uval:1
length:0
type:integer
sval:1
uval:144
length:144

I'm writing code for espressif, hence the "os_printf", as far as I know, it work as "printf".

I'm really baffled and cant find out why this is happening.

EDIT 1: I know that I shouldn't use "%d" for long and uint64. The question is why are the outputs different? I don't change the values between printing, so the same values should be printed.

EDIT 2: This question isn't about unions, their initialization, best way of printing uint64 or long.

The question is why does

os_printf("type:integer\nsval:%d\nuval:%d\nlength:%d\n", item->value.sint, item->value.uint, item->length);

print

type:integer
sval:1
uval:1
length:0

and

    os_printf("type:integer\n");
    os_printf("sval:%d\n", item->value.sint);
    os_printf("uval:%d\n", item->value.uint);
    os_printf("length:%d\n", item->length);

prints

type:integer
sval:1
uval:144
length:144

Upvotes: 1

Views: 232

Answers (2)

Kevin
Kevin

Reputation: 7324

You have undefined behavior because you are trying to print a uint64_t using the %d format. printf (and other functions that take a variable number of arguments) needs to know the type (and size) of the arguments. It gets this information from the format specifiers. Because you gave it %d instead of the correct format it's trying to read an int instead of a uint64_t. See this as an example.

Upvotes: 4

Peter Miehle
Peter Miehle

Reputation: 6070

with sprintf(format, val1, val2) you put val1 and val2 on the paramter stack to sprintf, and then sprintf will fetch values from that stack according to the format string, which is blindly running over the values, not knowing the actual sizes. so in quintessenz: undefined behaviour

Upvotes: 0

Related Questions