Are both of these volatile qualifier usages redundant? - c

Consider
volatile int volatile * volatile vip; // (1)
and
volatile int volatile * volatile vipa[10]; // (2)
Both lines of code trigger -Wduplicate-decl-specifier (see rev 236142
and gcc7 release notes). I'd like to know if I can remove some volatile specifiers from the given code without changing the semantics of the code, and understand the reasons behind it, too.
Thus, the following questions:
a. In (1), do the 1st and 2nd volatile qualifiers both refer to int, thus being "duplicate" in gcc terms? (I'm looking at C99 6.7.3.4 here.)
b. In (2), does one of the volatile qualifiers refer to the type of the array, and not the int or the pointer itself, so that C99 6.7.3.8 holds:
If the specification of an array type includes any type qualifiers, the element type is so-qualified, not the array type.
or do the volatile specifiers in (2) only affect the int and the pointer type, and not the type of the array?
c. If the answer to b is negative, how do I declare a volatile array type that is described in C99 6.7.3.8? Is it the syntax described at https://en.cppreference.com/w/c/language/array (quote follows)?
qualifiers - any combination of const, restrict, or volatile qualifiers, only allowed in function parameter lists; this qualifies the pointer type to which this array parameter is transformed
Let's consider this a question about C99. If there are any differences in C11 in this regard, please make a note.

TL;DR:
In (1), do the 1st and 2nd volatile qualifiers both refer to int, thus being "duplicate" in gcc terms? (I'm looking at C99 6.7.3.4 here.)
yes, they both qualify the int and they're the duplicate.
In (2), does one of the volatile qualifiers refer to the type of the array, and not the int or the pointer itself, so that C99 6.7.3.8 holds:
C99 6.7.3.8 does not hold here. The qualifier already applies to the element type. It is possible to apply a qualifier to an array with a typedef, but that also qualifies the element type (see below)
c. If the answer to b is negative, how do I declare a volatile array type that is described in C99 6.7.3.8?
With a typedef for example.
The C standard explicitly allows qualifiers to occur more than once. C11 n1570 6.7.3p5:
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.
i.e. what -Wduplicate-decl-specifier is not an error as such, but such code verbatim is suspicious - should it be volatile int *volatile that was misspelt as volatile int volatile * causing the pointer to be unqualified...
The qualifiers apply to type of the left left of the qualifier, except if the qualifier itself is the leftmost one, in which case it is as if it was right of the base type i.e.
volatile int *
and
int volatile *
mean the same thing. Therefore in volatile int volatile you can remove one of these. Thus the thing what you need is
volatile int *volatile vipa[10];
Meaning that vipais an array of 10volatile-qualified pointers tovolatileint`s.
What the C99 6.7.3p8/C11 6.7.3p9 means is that an array as itself cannot be volatile - its address is constant, only its elements can be qualified. hence if an array type is qualified, it only applies to its elements. This is even so if a typedef is qualified:
typedef int intarray[5];
const intarray myarray;
will declare myarray as if by
const int myarray[5];
whereas if you'd use typedef for a pointer:
typedef int *intptr;
const intptr myptr;
this qualifier would not affect the pointed-to type but be equivalent to
int *const myptr;
While both volatile int and int volatile are strictly allowed, the C standard prefers the former. C11 n1570 6.7.6.1p3:
EXAMPLE The following pair of declarations demonstrates the difference between a ''variable pointer to a constant value'' and a ''constant pointer to a variable value''.
const int *ptr_to_constant;
int *const constant_ptr;
The contents of any object pointed to by ptr_to_constant shall not be modified through that pointer, but ptr_to_constant itself may be changed to point to another object. Similarly, the contents of the int pointed to by constant_ptr may be modified, but constant_ptr itself shall always point to the same location.
It is further possible to add a type qualifier for an array within the brackets, but only in function parameters, so you can write
void foo(int array[volatile])
which means almost the same and the parameter decays to a qualified pointer
void foo(int *volatile array)
but you can use the static specifier only with the former style.

the explanation is simple.
volatile int * == int volatile *
in this case order does not matter.
So volatile int * volatile x; == int volatile * volatile x;
if you have volatile int volatile * you have already declared it as volatile do the second one is not needed

Related

char* func_name VS char *func_name (what are the differences) [duplicate]

I've recently decided that I just have to finally learn C/C++, and there is one thing I do not really understand about pointers or more precisely, their definition.
How about these examples:
int* test;
int *test;
int * test;
int* test,test2;
int *test,test2;
int * test,test2;
Now, to my understanding, the first three cases are all doing the same: Test is not an int, but a pointer to one.
The second set of examples is a bit more tricky. In case 4, both test and test2 will be pointers to an int, whereas in case 5, only test is a pointer, whereas test2 is a "real" int. What about case 6? Same as case 5?
4, 5, and 6 are the same thing, only test is a pointer. If you want two pointers, you should use:
int *test, *test2;
Or, even better (to make everything clear):
int* test;
int* test2;
White space around asterisks have no significance. All three mean the same thing:
int* test;
int *test;
int * test;
The "int *var1, var2" is an evil syntax that is just meant to confuse people and should be avoided. It expands to:
int *var1;
int var2;
Many coding guidelines recommend that you only declare one variable per line. This avoids any confusion of the sort you had before asking this question. Most C++ programmers I've worked with seem to stick to this.
A bit of an aside I know, but something I found useful is to read declarations backwards.
int* test; // test is a pointer to an int
This starts to work very well, especially when you start declaring const pointers and it gets tricky to know whether it's the pointer that's const, or whether its the thing the pointer is pointing at that is const.
int* const test; // test is a const pointer to an int
int const * test; // test is a pointer to a const int ... but many people write this as
const int * test; // test is a pointer to an int that's const
Use the "Clockwise Spiral Rule" to help parse C/C++ declarations;
There are three simple steps to follow:
Starting with the unknown element, move in a spiral/clockwise
direction; when encountering the following elements replace them with
the corresponding english statements:
[X] or []: Array X size of... or Array undefined size of...
(type1, type2): function passing type1 and type2 returning...
*: pointer(s) to...
Keep doing this in a spiral/clockwise direction until all tokens have been covered.
Always resolve anything in parenthesis first!
Also, declarations should be in separate statements when possible (which is true the vast majority of times).
There are three pieces to this puzzle.
The first piece is that whitespace in C and C++ is normally not significant beyond separating adjacent tokens that are otherwise indistinguishable.
During the preprocessing stage, the source text is broken up into a sequence of tokens - identifiers, punctuators, numeric literals, string literals, etc. That sequence of tokens is later analyzed for syntax and meaning. The tokenizer is "greedy" and will build the longest valid token that's possible. If you write something like
inttest;
the tokenizer only sees two tokens - the identifier inttest followed by the punctuator ;. It doesn't recognize int as a separate keyword at this stage (that happens later in the process). So, for the line to be read as a declaration of an integer named test, we have to use whitespace to separate the identifier tokens:
int test;
The * character is not part of any identifier; it's a separate token (punctuator) on its own. So if you write
int*test;
the compiler sees 4 separate tokens - int, *, test, and ;. Thus, whitespace is not significant in pointer declarations, and all of
int *test;
int* test;
int*test;
int * test;
are interpreted the same way.
The second piece to the puzzle is how declarations actually work in C and C++1. Declarations are broken up into two main pieces - a sequence of declaration specifiers (storage class specifiers, type specifiers, type qualifiers, etc.) followed by a comma-separated list of (possibly initialized) declarators. In the declaration
unsigned long int a[10]={0}, *p=NULL, f(void);
the declaration specifiers are unsigned long int and the declarators are a[10]={0}, *p=NULL, and f(void). The declarator introduces the name of the thing being declared (a, p, and f) along with information about that thing's array-ness, pointer-ness, and function-ness. A declarator may also have an associated initializer.
The type of a is "10-element array of unsigned long int". That type is fully specified by the combination of the declaration specifiers and the declarator, and the initial value is specified with the initializer ={0}. Similarly, the type of p is "pointer to unsigned long int", and again that type is specified by the combination of the declaration specifiers and the declarator, and is initialized to NULL. And the type of f is "function returning unsigned long int" by the same reasoning.
This is key - there is no "pointer-to" type specifier, just like there is no "array-of" type specifier, just like there is no "function-returning" type specifier. We can't declare an array as
int[10] a;
because the operand of the [] operator is a, not int. Similarly, in the declaration
int* p;
the operand of * is p, not int. But because the indirection operator is unary and whitespace is not significant, the compiler won't complain if we write it this way. However, it is always interpreted as int (*p);.
Therefore, if you write
int* p, q;
the operand of * is p, so it will be interpreted as
int (*p), q;
Thus, all of
int *test1, test2;
int* test1, test2;
int * test1, test2;
do the same thing - in all three cases, test1 is the operand of * and thus has type "pointer to int", while test2 has type int.
Declarators can get arbitrarily complex. You can have arrays of pointers:
T *a[N];
you can have pointers to arrays:
T (*a)[N];
you can have functions returning pointers:
T *f(void);
you can have pointers to functions:
T (*f)(void);
you can have arrays of pointers to functions:
T (*a[N])(void);
you can have functions returning pointers to arrays:
T (*f(void))[N];
you can have functions returning pointers to arrays of pointers to functions returning pointers to T:
T *(*(*f(void))[N])(void); // yes, it's eye-stabby. Welcome to C and C++.
and then you have signal:
void (*signal(int, void (*)(int)))(int);
which reads as
signal -- signal
signal( ) -- is a function taking
signal( ) -- unnamed parameter
signal(int ) -- is an int
signal(int, ) -- unnamed parameter
signal(int, (*) ) -- is a pointer to
signal(int, (*)( )) -- a function taking
signal(int, (*)( )) -- unnamed parameter
signal(int, (*)(int)) -- is an int
signal(int, void (*)(int)) -- returning void
(*signal(int, void (*)(int))) -- returning a pointer to
(*signal(int, void (*)(int)))( ) -- a function taking
(*signal(int, void (*)(int)))( ) -- unnamed parameter
(*signal(int, void (*)(int)))(int) -- is an int
void (*signal(int, void (*)(int)))(int); -- returning void
and this just barely scratches the surface of what's possible. But notice that array-ness, pointer-ness, and function-ness are always part of the declarator, not the type specifier.
One thing to watch out for - const can modify both the pointer type and the pointed-to type:
const int *p;
int const *p;
Both of the above declare p as a pointer to a const int object. You can write a new value to p setting it to point to a different object:
const int x = 1;
const int y = 2;
const int *p = &x;
p = &y;
but you cannot write to the pointed-to object:
*p = 3; // constraint violation, the pointed-to object is const
However,
int * const p;
declares p as a const pointer to a non-const int; you can write to the thing p points to
int x = 1;
int y = 2;
int * const p = &x;
*p = 3;
but you can't set p to point to a different object:
p = &y; // constraint violation, p is const
Which brings us to the third piece of the puzzle - why declarations are structured this way.
The intent is that the structure of a declaration should closely mirror the structure of an expression in the code ("declaration mimics use"). For example, let's suppose we have an array of pointers to int named ap, and we want to access the int value pointed to by the i'th element. We would access that value as follows:
printf( "%d", *ap[i] );
The expression *ap[i] has type int; thus, the declaration of ap is written as
int *ap[N]; // ap is an array of pointer to int, fully specified by the combination
// of the type specifier and declarator
The declarator *ap[N] has the same structure as the expression *ap[i]. The operators * and [] behave the same way in a declaration that they do in an expression - [] has higher precedence than unary *, so the operand of * is ap[N] (it's parsed as *(ap[N])).
As another example, suppose we have a pointer to an array of int named pa and we want to access the value of the i'th element. We'd write that as
printf( "%d", (*pa)[i] );
The type of the expression (*pa)[i] is int, so the declaration is written as
int (*pa)[N];
Again, the same rules of precedence and associativity apply. In this case, we don't want to dereference the i'th element of pa, we want to access the i'th element of what pa points to, so we have to explicitly group the * operator with pa.
The *, [] and () operators are all part of the expression in the code, so they are all part of the declarator in the declaration. The declarator tells you how to use the object in an expression. If you have a declaration like int *p;, that tells you that the expression *p in your code will yield an int value. By extension, it tells you that the expression p yields a value of type "pointer to int", or int *.
So, what about things like cast and sizeof expressions, where we use things like (int *) or sizeof (int [10]) or things like that? How do I read something like
void foo( int *, int (*)[10] );
There's no declarator, aren't the * and [] operators modifying the type directly?
Well, no - there is still a declarator, just with an empty identifier (known as an abstract declarator). If we represent an empty identifier with the symbol λ, then we can read those things as (int *λ), sizeof (int λ[10]), and
void foo( int *λ, int (*λ)[10] );
and they behave exactly like any other declaration. int *[10] represents an array of 10 pointers, while int (*)[10] represents a pointer to an array.
And now the opinionated portion of this answer. I am not fond of the C++ convention of declaring simple pointers as
T* p;
and consider it bad practice for the following reasons:
It's not consistent with the syntax;
It introduces confusion (as evidenced by this question, all the duplicates to this question, questions about the meaning of T* p, q;, all the duplicates to those questions, etc.);
It's not internally consistent - declaring an array of pointers as T* a[N] is asymmetrical with use (unless you're in the habit of writing * a[i]);
It cannot be applied to pointer-to-array or pointer-to-function types (unless you create a typedef just so you can apply the T* p convention cleanly, which...no);
The reason for doing so - "it emphasizes the pointer-ness of the object" - is spurious. It cannot be applied to array or function types, and I would think those qualities are just as important to emphasize.
In the end, it just indicates confused thinking about how the two languages' type systems work.
There are good reasons to declare items separately; working around a bad practice (T* p, q;) isn't one of them. If you write your declarators correctly (T *p, q;) you are less likely to cause confusion.
I consider it akin to deliberately writing all your simple for loops as
i = 0;
for( ; i < N; )
{
...
i++;
}
Syntactically valid, but confusing, and the intent is likely to be misinterpreted. However, the T* p; convention is entrenched in the C++ community, and I use it in my own C++ code because consistency across the code base is a good thing, but it makes me itch every time I do it.
I will be using C terminology - the C++ terminology is a little different, but the concepts are largely the same.
As others mentioned, 4, 5, and 6 are the same. Often, people use these examples to make the argument that the * belongs with the variable instead of the type. While it's an issue of style, there is some debate as to whether you should think of and write it this way:
int* x; // "x is a pointer to int"
or this way:
int *x; // "*x is an int"
FWIW I'm in the first camp, but the reason others make the argument for the second form is that it (mostly) solves this particular problem:
int* x,y; // "x is a pointer to int, y is an int"
which is potentially misleading; instead you would write either
int *x,y; // it's a little clearer what is going on here
or if you really want two pointers,
int *x, *y; // two pointers
Personally, I say keep it to one variable per line, then it doesn't matter which style you prefer.
#include <type_traits>
std::add_pointer<int>::type test, test2;
In 4, 5 and 6, test is always a pointer and test2 is not a pointer. White space is (almost) never significant in C++.
The rationale in C is that you declare the variables the way you use them. For example
char *a[100];
says that *a[42] will be a char. And a[42] a char pointer. And thus a is an array of char pointers.
This because the original compiler writers wanted to use the same parser for expressions and declarations. (Not a very sensible reason for a langage design choice)
I would say that the initial convention was to put the star on the pointer name side (right side of the declaration
in the c programming language by Dennis M. Ritchie the stars are on the right side of the declaration.
by looking at the linux source code at https://github.com/torvalds/linux/blob/master/init/main.c
we can see that the star is also on the right side.
You can follow the same rules, but it's not a big deal if you put stars on the type side.
Remember that consistency is important, so always but the star on the same side regardless of which side you have choose.
In my opinion, the answer is BOTH, depending on the situation.
Generally, IMO, it is better to put the asterisk next to the pointer name, rather than the type. Compare e.g.:
int *pointer1, *pointer2; // Fully consistent, two pointers
int* pointer1, pointer2; // Inconsistent -- because only the first one is a pointer, the second one is an int variable
// The second case is unexpected, and thus prone to errors
Why is the second case inconsistent? Because e.g. int x,y; declares two variables of the same type but the type is mentioned only once in the declaration. This creates a precedent and expected behavior. And int* pointer1, pointer2; is inconsistent with that because it declares pointer1 as a pointer, but pointer2 is an integer variable. Clearly prone to errors and, thus, should be avoided (by putting the asterisk next to the pointer name, rather than the type).
However, there are some exceptions where you might not be able to put the asterisk next to an object name (and where it matters where you put it) without getting undesired outcome — for example:
MyClass *volatile MyObjName
void test (const char *const p) // const value pointed to by a const pointer
Finally, in some cases, it might be arguably clearer to put the asterisk next to the type name, e.g.:
void* ClassName::getItemPtr () {return &item;} // Clear at first sight
The pointer is a modifier to the type. It's best to read them right to left in order to better understand how the asterisk modifies the type. 'int *' can be read as "pointer to int'. In multiple declarations you must specify that each variable is a pointer or it will be created as a standard variable.
1,2 and 3) Test is of type (int *). Whitespace doesn't matter.
4,5 and 6) Test is of type (int *). Test2 is of type int. Again whitespace is inconsequential.
I have always preferred to declare pointers like this:
int* i;
I read this to say "i is of type int-pointer". You can get away with this interpretation if you only declare one variable per declaration.
It is an uncomfortable truth, however, that this reading is wrong. The C Programming Language, 2nd Ed. (p. 94) explains the opposite paradigm, which is the one used in the C standards:
The declaration of the pointer ip,
int *ip;
is intended as a mnemonic; it says that the expression *ip is an
int. The syntax of the declaration for a variable mimics the syntax
of expressions in which the variable might appear. This reasoning
applies to function declarations as well. For example,
double *dp, atof(char *);
says that in an expression *dp and atof(s) have values of type
double, and that the argument of atof is a pointer to char.
So, by the reasoning of the C language, when you declare
int* test, test2;
you are not declaring two variables of type int*, you are introducing two expressions that evaluate to an int type, with no attachment to the allocation of an int in memory.
A compiler is perfectly happy to accept the following:
int *ip, i;
i = *ip;
because in the C paradigm, the compiler is only expected to keep track of the type of *ip and i. The programmer is expected to keep track of the meaning of *ip and i. In this case, ip is uninitialized, so it is the programmer's responsibility to point it at something meaningful before dereferencing it.
A good rule of thumb, a lot of people seem to grasp these concepts by: In C++ a lot of semantic meaning is derived by the left-binding of keywords or identifiers.
Take for example:
int const bla;
The const applies to the "int" word. The same is with pointers' asterisks, they apply to the keyword left of them. And the actual variable name? Yup, that's declared by what's left of it.

What is the order of field assignment when assigning compatible aggregate types

I've been looking for standardese on this all evening, with no luck. Perhaps I'm missing something!
In the following code example, is the copy into uint32_t a sequenced with the the copy into uint32_t b during the assignment *one = *two, or is this implementation defined?
typedef struct __attribute__((packed)) {
uint32_t a;
uint32_t b;
} x_t;
extern x_t volatile* get_volatile_x();
extern x_t const* get_const_x();
void foo()
{
x_t volatile* one = get_volatile_x();
x_t const* two = get_const_x();
*one = *two;
}
godbolt: https://godbolt.org/z/Vl0fdM
Here's and example where, with certain ARM flags, the load is done in the order [a, b], but the store is done in the order [b, a].
#include <stddef.h>
#include <stdint.h>
typedef struct __attribute__((packed)) {
volatile uint32_t a;
volatile uint32_t b;
} x_t;
extern x_t* get_x();
extern x_t volatile* get_volatile_x();
extern x_t const* get_const_x();
void foo()
{
x_t* one = get_x();
x_t const* two = get_const_x();
*one = *two;
}
godbolt: https://godbolt.org/z/FcRbhm
As a fun fact (I'm much more interested in the C question above than this detail) that this code will not compile with a C++ compiler. You'll get an error something like:
<source>:4:9: note: candidate function (the implicit move assignment operator) not viable: 'this' argument has type 'volatile x_t', but method is not marked volatile
because it looks like C++ is expecting an operator=() which takes this as a volatile. Something like: https://godbolt.org/z/lt30dJ, although this isn't really quite right in C++ either.
The C standard does not define ordering for assignment of members within a structure when assigning a structure.
About assignment, C 2018 6.5.16.1 says only:
2 In simple assignment (=), the value of the right operand is converted to the type of the assignment expression and replaces the value stored in the object designated by the left operand.
3 If the value being stored in an object is read from another object that overlaps in any way the storage of the first object, then the overlap shall be exact and the two objects shall have qualified or unqualified versions of a compatible type; otherwise, the behavior is undefined.
Examining the standard for all mentions of “member”, “struct”, or “structure” does not reveal anything that would impose any chronological ordering on the members within the assignment. (There is not even an explicit definition of what the value of a structure is; we are left to presume it is effectively an ordered tuple of the value of its members, or perhaps, from “a structure is a type consisting of a sequence of members” in 6.7.2.1 6, that its value is a sequence of values of its members.)

Array declared without size and const volatile

I found this line in my work project, but I can't understand this:
extern const volatile uint8 * const volatile array[];
Could you please help me explain the line?
First, leaving out the qualifiers:
uint8 *array[];
array is an array of unspecified size whose elements are of type uint8 *. In other words, an array of pointers to uint8.
If this declaration appears as a parameter to a function, the array syntax is actually shorthand for a pointer. If it is not a function parameter and is declared at file scope then this acts as a tentative definition. A complete definition may occur elsewhere in the code that specifies the size and optionally an initializer. If no other complete definition exists, then the array is defined to have 1 element.
Before talking about what the declaration means with the qualifiers, let's first talk about exactly what those qualifiers mean.
The const qualifier prevents code from modifying the named object. Given this declaration:
const int x;
It means that x can't be modified using for example x = 1. With pointers involved it's a little more tricky:
const int *x;
This defines x as a pointer to a const int. That means that you can modify x but not what it points to. So x = &y is legal but not *x = 1.
int * const x;
This defines x as a const pointer to an int, which means you can't modify x but you can modify what it points to. So x = &y is not legal but *x = 1 is.
const int * const x;
This defines x as a const pointer to a const int. So in this case, neither x nor what it points to can be modified.
const int * const x[];
Here, x is an array whose elements are const pointers to a const int. As in the prior example, for each array element, neither the array element nor what it points to can be modified.
Now let's talk about volatile. This qualifier tells the compiler that the variable in question might change unpredictably. From section 6.7.3p7 of the C standard:
An object that has volatile-qualified type may be modified in
ways unknown to the implementation or have other unknown side
effects. Therefore any expression referring to such an object shall
be evaluated strictly according to the rules of the abstract machine,
as described in 5.1.2.3. Furthermore, at every sequence point the
value last stored in the object shall agree with that prescribed by
the abstract machine, except as modified by the unknown factors
mentioned previously. 134) What constitutes an access to an
object that has volatile-qualified type is implementation-defined
134) A volatile declaration may be used to describe an object
corresponding to a memory-mapped input/output port or an object
accessed by an asynchronously interrupting function. Actions
on objects so declared shall not be "optimized out" by an
implementation or reordered except as permitted by the rules for
evaluating expressions
What this means is that a volatile object could change in ways not known to the compiler, and thus the compiler should not perform any optimizations on this variable and in fact should assume the value was changed externally.
Now moving on the your full declaration:
const volatile uint8 * const volatile array[];
This declares array as an array of unspecified size whose elements are of type uint8 *, where the elements of the array cannot be modified by the program (i.e. are const) but could be modified externally (i.e. volatile), and what those array elements point to also cannot be changed by the program but could be modified externally.

Is there a difference between the _Atomic type qualifier and type specifier?

Why the standard make that difference?
It seems as both designate, in the same way, an atomic type.
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.
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))
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:
foo to be atomic, so that you can, for example, atomically change
foo's value to be 2 instead of 1.
p to be atomic, so that you can, for example, change atomically what p is pointing to, and point to bar instead of foo.
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'.

Is the "volatile" property of pointers inherited?

Suppose I have the following code:
my_struct_member_type *foo() {
volatile my_struct *s = (my_struct *)SOME_ADDRESS;
return &(s->struct_member);
}
Is the pointer returned by foo also volatile?
EDIT: A followup: is the pointer &(s->struct_member) volatile inside of foo?
Yes.
For volatile my_struct *s says that the object you see through it is volatile and all members that you access through this pointer inherit the volatile qualification.
Having the address of a pointer to volatile returned if the return type doesn't say so is a constraint violation and your compiler must have given you a diagnostic for that.
Edit: There also seems to be confusion to where the volatile keyword applies. In your example it applies to the object pointed to, not to the pointer itself. As a rule of thumb always write the qualifiers (const or volatile) as far to the right as possible without a type change. Your example then would read:
my_struct volatile*s = (my_struct *)SOME_ADDRESS;
which is entirely different from
my_struct *volatile s = (my_struct *)SOME_ADDRESS;
where the pointer itself is volatile but not the object behind it.
Edit 2: Since you ask for my sources, the actual C standard, C11, 6.8.6.4 it says about the return statement:
If the expression has a type different from the return type of the
function in which it appears, the value is converted as if by
assignment to an object having the return type of the function
So 6.15.16.1 for the assignment operator what is expected from the conversion:
the left operand has atomic, qualified, or unqualified pointer type,
and (considering the type the left operand would have after lvalue
conversion) both operands are pointers to qualified or unqualified
versions of compatible types, and the type pointed to by the left has
all the qualifiers of the type pointed to by the right;
Here the pointed type on the right has the volatile qualifier in addtion to the one on the left.
No. volatile is a compiler hint, and as such is only respected as long as the compiler can see it. That function could be called from another translation unit, which would have no idea about you declaring the pointer volatile initially.
Volatile has no sense in runtime, it is used to tell compiler to not optimize access to a variable.
Returning volatile pointer is nonsense. You mark some storage (lvalue) as volatile, not rvalue.
http://www.barrgroup.com/Embedded-Systems/How-To/C-Volatile-Keyword

Resources