简体   繁体   中英

Why do we cast constant literals

I am wondering why do we cast constants when using #define preprocessor?

For example:

#define WIDTH 128

Would it be equal to -128 in an 8bit platform?

What if I change it like this:

#define WIDTH 128U

What would it be equal to, on 8bit platform?

What is the default sizeof a constant integers like the above? Do its length/type depends on the platform architecture, or it depends on the type of the literal value they hold?

Sorry about my bad English.

Defining WIDTH as 128 poses no problems, int is at least 16 bit wide on all conforming platforms.

Defining WIDTH as 128U would make it an unsigned integer constant literal. Since the value fits in an unsigned int (mandated to be at least 16 bit wide), it has type unsigned int . sizeof(WIDTH) evaluates to sizeof(unsigned int) , which is entirely platform specific.

Using this suffix is not recommended . It would have surprising side effects:

 if (WIDTH > -1) {
    printf("This will never print\n");
 }

Since WIDTH expands to 128U , an unsigned constant, the comparison is performed as an unsigned comparison, -1 is converted to unsigned and becomes UINT_MAX , a value much larger than 128 . Don't do this.

If you subsequently store WIDTH into a char variable, you may have a problem. It would actually not make a difference whether you define it as 128 or 128U , you would still have an overflow if the char type is 8 bit and signed, leading to undefined behavior. On most platforms, the value stored would indeed be -128 but you cannot even rely on that.

More importantly, you should use all the help the compiler can give you by enabling all compiler warnings and making them errors:

gcc -Wall -Wextra -Werror

or

clang -Weverything -Werror

Very few of these warnings are annoying, most of them are very useful and point to silly mistakes, typos and oversights.

First of all 128 is not equal to -128 on a 8-bit platform.

Second this has nothing to do with the preprocessor. What the preprocessor does is to replace WIDTH with whatever it's defined as. That is the question is why you write 128 or 128u in your source.

The suffix u is not about type casting, it's about to indicate the type of the literal. In this example 128 is an literal with value 128 of type int while 128u is a literal with value 128 of type unsigned int . It's not a problem immediately here, but if you start to use them and end up larger than 32767 you could run into problems. For example:

#define WIDTH   256u
#define HEIGHT  192u

unsigned npixels = WIDTH * HEIGHT;

it should be noted that the suffices are required to make it portable (what could happen is that the platform only uses 16-bit int s and with int the multiplication would overflow which means undefined behavior).

Also note that in newer C standards (but not the antique ones) will extend the literal to become as large as necessary if possible. For example the literal 32768 means a signed integral type with value 32768 , if int isn't large enough to hold that signed number then larger types would be used.

The sizeof these integers are the same as sizeof(int) as the type of the literals are int and unsigned int . The actual value of sizeof(int) could be any positive integer.

Giving C99 chapters because I don't have the C11 document at hand.


ISO/IEC 9899:1999, 6.4.4.1 Integer constants

The type of an integer constant is the first of the corresponding list in which its value can be represented.

For decimal constants without suffix:

  • int
  • long int
  • long long int

For decimal constants with u or U suffix:

  • unsigned int
  • unsigned long int
  • unsigned long long int

ISO/IEC 9899:1999, 5.2.4.2.1 Sizes of integer types

The width of integer types is implementation-defined, as is the binary representation.

INT_MAX -- the largest value an int can take -- is guaranteed to be at least +32767 .

UINT_MAX -- the largest value an unsigned int can take -- is guaranteed to be at least 65535 .


ISO/IEC 9899:1999, 6.3.1.8 Usual arithmetic conversions

If you compare an int with an unsigned int , the int will be implicitly converted to unsigned int for the comparison. Similar for the short / long / long long types. As @chqrlie pointed out, this can be a problem; your compiler should give you a warning if this happens (you are always compiling with -Wall -Wextra / /W3 enabled, aren't you?).


Summary

Your constants will fit into an int / unsigned int even on an 8-bit machine. Assuming they would not, the compiler would use the next largest type for them (instead of casting the value).

As for why we do it...

If you, for example, intend to use WIDTH for comparisons with the result of sizeof() , or the return code of strlen() , or anything else that is unsigned by nature, you would want WIDTH to have the same value domain, ie being able to hold all possible values.

That is why you would want WIDTH to be unsigned as well.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM