Declaring 64-bit variables in C

后端 未结 5 1918
生来不讨喜
生来不讨喜 2020-12-01 17:41

I have a question.

uint64_t var = 1; // this is 000000...00001 right?

And in my code this works:

var ^ (1 << 43)
         


        
5条回答
  •  温柔的废话
    2020-12-01 18:26

    Your compiler doesn't know that the shift should be done in 64 bits. However, with this particular version of the compiler in this particular configuration for this particular code, two wrongs happen to make a right. Don't count on it.

    Assuming that int is a 32-bit type on your platform (which is very likely), the two wrongs in 1 << 43 are:

    • If the shift amount is greater than or equal to the width of the type of the left operand, the behavior is undefined. This means that if x is of type int or unsigned int, then x << 43 has undefined behavior, as does x << 32 or any other x << n where n ≥ 32. For example 1u << 43 would have undefined behavior too.
    • If the left operand has a signed type, and the result of the operation overflows that type, then the behavior is undefined. For example 0x12345 << 16 has undefined behavior, because the type of the left operand is the signed type int but the result value doesn't fit in int. On the other hand, 0x12345u << 16 is well-defined and has the value 0x23450000u.

    “Undefined behavior” means that the compiler is free to generate code that crashes or returns a wrong result. It so happens that you got the desired result in this case — this is not forbidden, however Murphy's law dictates that one day the generated code won't do what you want.

    To guarantee that the operation takes place on a 64-bit type, you need to ensure that the left operand is a 64-bit type — the type of the variable that you're assigning the result to doesn't matter. It's the same issue as float x = 1 / 2 resulting in x containing 0 and not 0.5: only the types of the operands matter to determine the behavior of the arithmetic operator. Any of (uint64)1 << 43 or (long long)1 << 43 or (unsigned long long)1 << 43 or 1ll << 43 or 1ull << 43 will do. If you use a signed type, then the behavior is only defined if there is no overflow, so if you're expecting truncation on overflow, be sure to use an unsigned type. An unsigned type is generally recommended even if overflow isn't supposed to happen because the behavior is reproducible — if you use a signed type, then the mere act of printing out values for debugging purposes could change the behavior (because compilers like to take advantage of undefined behavior to generate whatever code is most efficient on a micro level, which can be very sensitive to things like pressure on register allocation).

    Since you intend the result to be of type uint64_t, it is clearer to perform all computations with that type. Thus:

    uint64_t var = 1;
    … var ^ ((uint64_t)1 << 43) …
    

提交回复
热议问题