1

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
8
  • 2
    Why are you using %d format specifier for long and uint64_t types? And why do you think that members of a union that you didn't explicitly set will have a meaningful value? Commented Jan 6, 2017 at 13:40
  • i've set them explicitly, length, value.uint and value.sint. and the output should be the same anyway, right? even if i didn't initialize them and used the wrong format. the only difference is that in first case I printf everything with one printf, and in the second i call three printf's Commented Jan 6, 2017 at 13:46
  • 2
    If you set sint after you set uint then uint no longer has a meaningful value. Commented Jan 6, 2017 at 13:47
  • 1
    Enable warnings on your compiler and you should get a warning about using %d for a long and uint64_t as Weather Vane mentioned (I think gcc warns about these by default actually). Commented Jan 6, 2017 at 13:48
  • sint is set first, and anyway it shouldn't matter for question (as far as I understand it) Commented Jan 6, 2017 at 13:48

2 Answers 2

4

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.

Sign up to request clarification or add additional context in comments.

2 Comments

%lld for uint64_t and %ld for long solved the problem, so i accepted this answer. Still don't get it why if I print everything in one printf it works differently from when i print in several printf's, but I guess it doesn't really matter.
You should use the macro PRIu64 as the format specifyer for uint64_t (i.e. "%" PRIu64 as seen in my example). %lld is for long long (signed, not unsigned).
0

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.