In linux kernel 2.6.26, i found "#define atomic_read(v) ((v)->counter + 0)", why "+0"?

If + 0 is not used, it would be an lvalue that you could assign to by accident, i.e.

if (atomic_read(v) = 42) {
    ...
}

would "work"... Instead of + 0 you could just use unary +, i.e.

(+(v)->counter)

However + 0 has one good advantage over + in generic case: + requires that the argument be an arithmetic type - but pointers are not of arithmetic type. Yet + 0 would work for pointers alike (and for pointers alone, you can use &* to convert lvalue to a value of expression; this is guaranteed to work for even null pointers)


It is possible that the + 0 was added for the compiler to issue a diagnostic in case there was a redefinition of the function-like macros atomic_read and atomic64_read.

As per the C standard it is possible to redefine an identifier which is a function-like macro if the second definition is a also function-like macro that has the same number and spelling of parameters, and the two replacement lists are identical.

From C11 standard (n1570), section 6.10.3/2:

... Likewise, an identifier currently defined as a function-like macro shall not be redefined by another #define preprocessing directive unless the second definition is a function-like macro definition that has the same number and spelling of parameters, and the two replacement lists are identical.

The kernel version (2.6.26) is quite old but similar prohibition on such redefinition can be found in older standards upto the C89 standard.

Currently the macros atomic_read and atomic64_read are defined in the file atomic.h.

If the user would redefine them in some source file as below:

#define atomic_read(v)      (v)->counter 

The compiler would issue a diagnostic about the redefinition. This warning is issued because there is a + 0 in the definition atomic_read of in the atomic.h file.

If it were not for the + 0, the compiler would not have issued a diagnostic.

A minimal example to demonstrate this issue:

//atomic.h
#define atomic_read(v)      ((v)->counter + 0)
#define atomic64_read(v)    ((v)->counter)

//some source file that includes atomic.h
#define atomic_read(v)      ((v)->counter) //redefinition error 
#define atomic64_read(v)    ((v)->counter) //no redefinition error 

See Demo


It prevents the result from being an lvalue, so you can't wrongly assign to it or take its address.

Tags:

Linux

C