I am currently looking at the stdatomic.h
header file in ubuntu linux.
What is the use of (void)0
and comma in __typeof__ ((void)0, *__atomic_store_ptr)
?
Doesn't this need nothing?
#define atomic_store_explicit(PTR, VAL, MO) \
__extension__ \
({ \
__auto_type __atomic_store_ptr = (PTR); \
__typeof__ ((void)0, *__atomic_store_ptr) __atomic_store_tmp = (VAL); \
__atomic_store (__atomic_store_ptr, &__atomic_store_tmp, (MO)); \
})
CodePudding user response:
It will help when PTR
happens to be a pointer to an array or a function or a pointer to a qualified type. The comma operator in ((void)0,x)
will force L-value conversion. It basically means transforming an object into a value. In a case of arrays it transforms an array in to pointer to its first element. Functions that are transformed to function pointers. The qualified types looses qualifiers like volatile
, const
or _Atomic
.
The l-value conversion happens in pretty much every case except address operator &
, sizeof
, _Alignof
and gcc's __typeof__
(typeof
in upcoming C23).
As it was said in the comments the cast to void
is used to silence warning about unused value of 0
.
- Pointer to an array
See:
int arr[3];
int (*ptr)[3];
atomic_store_explicit(ptr, &arr, XXX);
If no L-value conversion was forced then the macro would expand to:
__auto_type __atomic_store_ptr = (ptr);
which is
int (*__atomic_store_ptr)(ptr);
and
__typeof__ (*__atomic_store_ptr) __atomic_store_tmp = (VAL);
which becomes:
int __atomic_store_tmp[3] = arr;
because dereferencing int(*)[3]
produces int[3]
.
It will cause compilation failure due to initialization of an array with a pointer.
With the trick the code above will become:
int *__atomic_store_tmp = arr;
because int[3]
is transformed to int*
by doing l-value conversion.
- Function pointer.
For function pointers the situation is analogous to a pointer to an array.
void foo(void);
void (*ptr)(void);
atomic_store_explicit(ptr, foo, X);
Expands to:
...
__auto_type __atomic_store_ptr = (PTR);
==>
void (*__atomic_store_ptr)(void) = ptr;
and
__typeof__ (*__atomic_store_ptr) __atomic_store_tmp = (VAL);
==>
void __atomic_store_tmp(void) = foo;
because dereferencing (void(*)(void)
produces a function type void(void)
. The compilation fails due to initialization of a function with is not allowed in C.
With the trick the above declaration becomes:
void (*__atomic_store_tmp)(void) = foo;
which compiles fine.
- A pointer to a qualified type.
Assume:
volatile int *ptr;
atomic_store_explicit(ptr, 42, XXX);
Now:
__auto_type __atomic_store_ptr = (ptr);
==>
volatile int *__atomic_store_ptr = ptr;
and:
__typeof__ (*__atomic_store_ptr) __atomic_store_tmp = (val);
==>
volatile int __atomic_store_tmp = val;
Now the value becomes volatile
causing subtle issues like worse performance. With l-value conversion the type of __atomic_store_tmp
becomes unqualified int
.