Reputation: 21364
Why the standard make that difference?
It seems as both designate, in the same way, an atomic type.
Upvotes: 19
Views: 8162
Reputation: 1346
Atomic type specifiers :-:)
Syntax: _Atomic ( type-name );
You can declare an atomic integer like this:
_Atomic(int) counter;
The _Atomic
keyword can be used in the form _Atomic(T)
, where T is a type, as a type specifier equivalent to _Atomic
T. Thus, _Atomic(T) x, y;
declares x and y with the same type, even if T is a pointer type. This allows for trivial C++0x compatibility with a C++ only _Atomic(T)
macro definition as atomic<T>
.
Atomic type specifiers shall not be used if the implementation does not support atomic types. The type name in an atomic type specifier shall not refer to an array type, a function type, an atomic type, or a qualified type.
The properties associated with atomic types are meaningful only for expressions that are lvalues.
If the _Atomic keyword is immediately followed by a left parenthesis, it is interpreted as a type specifier (with a type name), not as a type qualifier.
Atomic type qualifiers :-:)
_Atomic volatile int *p;
It specifies that p has the type ‘‘pointer to volatile atomic int’’, a pointer to a volatile-qualified atomic type.
Types other than pointer types whose referenced type is an object type shall not be restrict-qualified.
The type modified by the _Atomic
qualifier shall not be an array type or a function type.
The properties associated with qualified types are meaningful only for expressions that are lvalues.
If the same qualifier appears more than once in the same specifier-qualifier-list, either directly or via one or more typedefs, the behavior is the same as if it appeared only once. If other qualifiers appear along with the _Atomic
qualifier in a specifier-qualifier-list, the resulting type is the so-qualified atomic type.
The keyword _Atomic
is used, alone, as a type qualifier. An implementation is allowed to relax the requirement of having the same representation and alignment of the corresponding non-atomic type, as long as appropriate conversions are made, including via the cast operator.
Upvotes: 10
Reputation: 988
After many attempts, I have found why this is needed: pointers!
Let's suppose you have:
int foo = 1;
int bar = 2;
int *p = &foo;
Picture that as memory locations, first two holding an int, the last one holding a pointer to the first int. _Atomic makes it so that those memory locations are suited for atomic operations.
For reasons that concern your program, you might want:
In the first case, to make foo atomic is easy, there is no ambiguity when reading it:
_Atomic int foo;
atomic_store_explicit(&foo , 2, memory_order_release); /* valid atomic op. */
But now you want to make p atomic, if you write:
_Atomic int *p;
... that is not what you want!
That is, as explained above, a non atomic pointer to an atomic int. Strictly speaking, there is no guarantee that this pointer will be correctly aligned to be able to do atomic operations on it (although you'll have hard time to force a compiler to misalign a pointer!). This means that, if you managed to make the pointer misaligned, the atomic operations on it will have a chance to fail. What you want is, on the other hand, an atomic pointer to a int that is non necessary atomic.
So you have to write:
int bar = 2;
_Atomic (int *) p;
atomic_store(&p , &bar); /* now valid atomic operation */
Now you have your atomic pointer!
Note that for the very simple case of making the foo int atomic, you could also have written, any of these 3 declarations, the last one uses the convenience typedef defined in stdatomic.h:
typedef _Atomic int atomic_int;
_Atomic int foo;
_Atomic (int) foo;
atomic_int foo;
I made it "easy to understand" with an int and a pointer to and int, but when you have to deal with
_Atomic (struct foobar *) *q;
You will now know that q itself is not an atomic pointer, but it points to an atomic pointer to a foobar struct!
And so the demonstration:
#include <stdatomic.h>
void test()
{
_Atomic int foo = 1; /* Atomic */
_Atomic int *pf = &foo; /* Non Atomic */
_Atomic int **ppf = &pf; /* Non Atomic */
int bar = 2; /* Non Atomic */
_Atomic (int *) pb = &bar; /* Atomic */
_Atomic (int *) *ppb = &pb; /* Non Atomic */
int *res;
res = atomic_load(ppf); /* Not OK, yields a warning */
res = atomic_load(ppb); /* This is correct */
}
In function ‘test’:
test.c:13:6: warning: assignment from incompatible pointer type [-Wincompatible-pointer-types]
res = atomic_load(ppf);
Indeed, the first atomic_load tries to return a non atomic pointer to an int: the int pointed to is atomic, not the pointer. It could also fail, because there is no guarantee that &pf (the content of ppf) is properly aligned for an atomic operation (although practically here it is, you would have to cast pf to a misaligned int to make it fail).
The second atomic_load correctly works with an atomic pointer and returns it to 'res'.
Upvotes: 3
Reputation: 106022
Yes. There is a difference. When it is used as type specifier then standard restrict it as (6.7.2.4 p(3)):
The type name in an atomic type specifier shall not refer to an array type, a function type, an atomic type, or a qualified type.
For example
typedef int arr[5];
arr
can be a type name when _Atomic
is used as qualifier but can't be used as type name if _Atomic
is used as type specifier (like _Atomic (arr)
)
Upvotes: 7