Explain a macro that checks the integrity of an Integer expression

A macro was discussed on the Linux kernel mailing list to check if its argument is an integer constant expression and is a constant expression itself.

One particularly smart approach that doesn't use the built-in functions suggested by Martin Wacker (taking inspiration from glibc tgmath.h ):

#define ICE_P(x) (sizeof(int) == sizeof(*(1 ? ((void*)((x) * 0l)) : (int*)1))) 

Note that it relies on sizeof(void) allowed (and different from sizeof(int) ), which is an extension of GNU C.

According to Linus Torvalds warning ,

  • it will destroy the minds of everyone who has ever seen this expression.

Therefore, in order to avoid such a fatal outcome, we must explain how and why it works.


For a discussion of different approaches to solving the same problem, see instead: Detecting integer expressions of integers in macros

+5
source share
1 answer

This macro uses conditional statement rules to determine the return type (6.5.15.6) and to determine the null pointer constant (6.3.2.3.3).


Macro body:

 (sizeof(int) == sizeof(*(1 ? ((void*)((x) * 0l)) : (int*)1))) 

Focus on this part:

 ((void*)((x) * 0l)) 

If x is the integer constant expression (6.6.6), then ((x) * 0l) is the integer constant expression of the value 0 . Therefore, (void*)((x) * 0l) is the constant of the null pointer (6.3.2.3.3):

An integer constant expression with a value of 0 or an expression that distinguishes the void * type is called a null pointer constant

If x not an integer constant expression, then (void*)((x) * 0l) not a null pointer constant, regardless of its value.

Knowing this, we can see what happens afterwards:

 1 ? ((void*)((x) * 0l)) : (int*)1 

The key here is that the conditional operator returns a different type depending on whether one of the operands is a null pointer constant (6.5.15.6):

[...] if one operand is a constant of a null pointer, the result is of the type of another operand; otherwise, one operand is a pointer to void or a qualified version of void , in which case the result type is a pointer to the corresponding version of void .

So, if x was an integer constant expression, then the second operand is the constant of the null pointer, and therefore the type of the expression is the type of the third operand, which is a pointer to int .

Otherwise, the second operand is a pointer to void , and therefore the type of the expression is a pointer to void .

Therefore, we get two possibilities:

 sizeof(int) == sizeof(*((int*)NULL)) // if `x` was an integer constant expression sizeof(int) == sizeof(*((void*)(x))) // otherwise 

According to the GNU C extension , sizeof(void) == 1 . Therefore, if x was an integer constant expression, the result of the macro is 1 ; otherwise 0 .

Moreover, since we compare only two sizeof expressions for equality, the result itself is another integer constant expression (6.6.3, 6.6.6):

Constant expressions must not contain assignment, increment, decrement, call, or comma operations, unless they are contained in a subexpression that is not evaluated.

An integer constant expression must be an integer type and must have only operands that are integer constants, enumeration constants, symbolic constants, sizeof expressions, the results of which are integer constants and floating-point constants, which are direct operands of cast. Cast operators in an integer constant expression must convert only arithmetic types to integer types, except that they are part of the operand to the sizeof operator.

+8
source

Source: https://habr.com/ru/post/1276071/


All Articles