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
Related
Let's say that I have a static global variable a, that is cast to an int in a function call during init(). After init(), is a still volatile when used in the function my_function_2?
static volatile int a;
init(void)
{
my_function_1((int)a);
}
my_function_2(void)
{
/* After init(), is a still volatile? */
}
Does a need to be re-qualified as volatile in my_function_2()? I'm using c99.
The variable a will be volatile for it's entire lifetime. If you cast it to some different type and assign it to a new variable, then this new variable will not be volatile unless you specify it so.
volatile means that the compiler doesn't rely on a previously known value and should not apply appropriate optimizations, so if you cast that away and still reference it , than it is undefined behavior.
Here you don't need to declare b as volatile
volatile int a;
void func(int b)
{
// This doesn't need to be volatile as it will never be changed from the outside, since it was passed by value.
printf("%d\n", b);
}
init(void)
{
// Access a and pass the value to func
// This is fine, because volatile applies only to access of 'a'.
func(a);
}
Another example:
void func(volatile int *p)
{
// 'p' needs to be volatile as it will reference 'a' which is volatile
printf("%d\n", *p);
}
init(void)
{
func(&a);
}
It's undefined behavior to refer to the value through a non-qualified type. If you "cast away" volatile you aren't allowed to access that memory location through a non-volatile qualified type.
C17 6.7.3/6:
If an attempt is made to refer to an object defined with a volatile-qualified type through use of an lvalue with non-volatile-qualified type, the behavior is undefined
You can however still do a copy of it:
static volatile int a;
int b = a; // ok, lvalue conversion, drops qualifiers during copy
*(int*)&a = ... // undefined behavior
What volatile means is basically that it can be modified externally. This can never change. If you have declared a variable as volatile, then it is volatile.
A cast can only convert a value; it cannot affect an object (the memory of a variable). And values do not have qualifiers such as volatile.
When an object’s value is used in an expression, qualifiers are removed, per C 2018 6.3.2.1 2:
Except when it is the operand of the sizeof operator, the unary & operator, the ++ operator, the -- operator, or the left operand of the . operator or an assignment operator, an lvalue that does not have array type is converted to the value stored in the designated object (and is no longer an lvalue); this is called lvalue conversion. If the lvalue has qualified type, the value has the unqualified version of the type of the lvalue;…
So, in (int)a, the a is converted from a volatile int lvalue to an int value before the cast occurs. The cast has no effect; it converts an int to an int. Also note that the value is cast—your title asks about “casting a volatile variable,” but it is impossible to cast a variable, because it is automatically converted to its value before the cast.
So the cast has no effect on a. The object a remains volatile. Its value is passed to my_function_1 as a value without the volatile qualifier.
This is a question from a test.
int main() {
const int i=1;
const int *p=&i;
int j=2;
const int *q=&j;
j=3;
printf("%d", *p+*q);
int k = 0;
return 0;
}
I understand that both p and q are pointers to a const int.
I thought that it would give a compile error when I declare the pointer to a non-const value, but it works fine; why is that?
I thought that it would give a compile error when I declare the q pointer to a non-const value, but it works fine; why is that??
It would not really make sense to make this non-valid code. If you point with a regular pointer, you can read and write. With a const you can only read, but it's the same underlying type so there's not really anything that can go wrong if you're reading a variable via a regular pointer or a pointer to const. It would be like not being able to read a file from a file system that you have mounted read-only, just because the file itself is not write protected.
Using pointer to const to point at a non-const variable is effectively creating a read-only interface, which makes sense to do. It's commonly used for functions that takes pointers as arguments, but you want to show that the function will not change the content. For instance
char *strcpy(char *dest, const char *src);
This library function reads from src and writes to dest.
However, the opposite will give a warning:
const int i=2;
int *p = &i;
This yields this:
warning: initialization discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers]
which makes perfect sense, since otherwise you could very easily be able to modify a constant.
const int *q=&j; is OK because it adds const. Your pointer q is like a const view to the non-const j. That pointer can be passed around giving others const access to i but no write access.
Of course the other way round would not be possible. If you have a const int k=...; you cannot take away the const by writing int* pp=&k;.
First, some standard language:
6.3.2.1 Lvalues, arrays, and function designators
...
2 Except when it is the operand of the sizeof operator, the _Alignof operator, the
unary & operator, the ++ operator, the -- operator, or the left operand of the . operator
or an assignment operator, an lvalue that does not have array type is converted to the
value stored in the designated object (and is no longer an lvalue); this is called lvalue
conversion. If the lvalue has qualified type, the value has the unqualified version of the
type of the lvalue; additionally, if the lvalue has atomic type, the value has the non-atomic
version of the type of the lvalue; otherwise, the value has the type of the lvalue. If the
lvalue has an incomplete type and does not have array type, the behavior is undefined. If
the lvalue designates an object of automatic storage duration that could have been
declared with the register storage class (never had its address taken), and that object
is uninitialized (not declared with an initializer and no assignment to it has been
performed prior to use), the behavior is undefined.
...
6.3.2.3 Pointers
...
2 For any qualifier q, a pointer to a non-q-qualified type may be converted to a pointer to
the q-qualified version of the type; the values stored in the original and converted pointers
shall compare equal.
...
6.5.16.1 Simple assignment
Constraints
1 One of the following shall hold:112)
...
— 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;
...
112) The asymmetric appearance of these constraints with respect to type qualifiers is due to the conversion
(specified in 6.3.2.1) that changes lvalues to ‘‘the value of the expression’’ and thus removes any type
qualifiers that were applied to the type category of the expression (for example, it removes const but
not volatile from the type int volatile * const).
C 2011 Online Draft
So you can take the address of a non-const object and assign it to a const pointer. You can write a new value directly to j, but you can't write a new value to *p, even though j and *p designate the same object.
This is a useful and desirable feature. A common use case is to declare pointer arguments in functions to be const as a promise that the function will not attempt to update the pointed-to object, such as in the strcat function:
char *strcat( char * restrict s1, const char * restrict s2 );
If I write something like
char foo[10] = "foo";
char bar[] = "bar";
strcat( foo, bar );
Even though bar is not const, strcat cannot write to it through the *s2 argument.
But the constraint is asymmetrical - you cannot assign the address of a const object to a non-const pointer.
In general, you can assign from a less restrictive type to a more restrictive type, but not the other way around.
Why is necessary do casting when I dereference a void pointer?
I have this example:
int x;
void* px = &x;
*px = 9;
Can you proof why this don't work?
By definition, a void pointer points to an I'm-not-sure-what-type-of-object.
By definition, when you use the unary * operator to access the object pointed to by a pointer, you must know (well, the compiler must know) what the type of the object is.
So we have just proved that we cannot directly dereference a void pointer using *; we must always explicitly cast the void pointer to some actual object pointer type first.
Now, in many people's minds, the "obvious" answer to "what type does/should a 'generic' pointer point to?" is "char". And, once upon a time, before the void type had been invented, character pointers were routinely used as "generic" pointers. So some compilers (including, notably, gcc) extend things a bit and let you do more (such as pointer arithmetic) with a void pointer than the standard requires.
So that might explain how code like that in your question might be able to "work". (In your case, though, since the pointed-to type was actually int, not char, if it "worked" it was only because you were on a little-endian machine.)
...And with that said, I find that the code in your question does not work for me, not even under gcc. It first gives me a non-fatal warning:
warning: dereferencing ‘void *’ pointer
But then it changes its mind and decides this is an error instead:
error: invalid use of void expression
A second compiler I tried said something similar:
error: incomplete type 'void' is not assignable
Addendum: To say a little more about why the pointed-to type is reuired when you dereference a pointer:
When you access a pointer using *, the compiler is going to emit code to fetch from (or maybe store to) the pointed-to location. But the compiler is going to have to emit code that accesses a certain number of bytes, and in many cases it may matter how those byte(s) are interpreted. Both the number and the interpretation of the bytes is determined by the type (that's what types are for), which is precisely why an actual, non-void type is required.
One of the best ways I know of appreciating this requirement is to consider code like
*p + 1
or, even better
*p += 1
If p points to a char, the compiler is probably going to emit some kind of an addb ("add byte") instruction.
If p points to an int, the compiler is going to emit an ordinary add instruction.
If p points to a float or double, the compiler is going to emit a floating-point addition instruction. And so on.
But if p is a void *, the compiler has no idea what to do. It complains (in the form of an error message) not just because the C standard says you can't dereference a void pointer, but more importantly, because the compiler simply doesn't know what to do with your code.
In short:
The target of an assignment expression must be a modifiable lvalue, which cannot be a void expression. This is because the void type does not represent any values - it denotes an absence of a value. You cannot create an object of type void.
If the expression px has type void *, then the expression *px has type void. Attempting to assign to *px is a constraint violation and the compiler is required to yell at you for it.
If you want to assign a new value to x through px, then you have to cast px to an int * before dereferencing:
*((int *)px) = 5;
Chapter and verse:
6.2.5 Types
...
19 The void type comprises an empty set of values; it is an incomplete object type that
cannot be completed.
...
6.3.2.1 Lvalues, arrays, and function designators
1 An lvalue is an expression (with an object type other than void) that potentially
designates an object;64) if an lvalue does not designate an object when it is evaluated, the
behavior is undefined. When an object is said to have a particular type, the type is
specified by the lvalue used to designate the object. A modifiable lvalue is an lvalue that
does not have array type, does not have an incomplete type, does not have a const-qualified type, and if it is a structure or union, does not have any member (including,
recursively, any member or element of all contained aggregates or unions) with a const-qualified type.
...
6.3.2.2 void
1 The (nonexistent) value of a void expression (an expression that has type void) shall not
be used in any way, and implicit or explicit conversions (except to void) shall not be
applied to such an expression. If an expression of any other type is evaluated as a void
expression, its value or designator is discarded. (A void expression is evaluated for its
side effects.)
...
6.3.2.3 Pointers
1 A pointer to void may be converted to or from a pointer to any object type. A pointer to
any object type may be converted to a pointer to void and back again; the result shall
compare equal to the original pointer.
...
6.5.3.2 Address and indirection operators
...
4 The unary * operator denotes indirection. If the operand points to a function, the result is
a function designator; if it points to an object, the result is an lvalue designating the
object. If the operand has type ‘‘pointer to type’’, the result has type ‘‘type’’. If an
invalid value has been assigned to the pointer, the behavior of the unary * operator is
undefined.102)
...
6.5.16 Assignment operators
...
Constraints
2 An assignment operator shall have a modifiable lvalue as its left operand.
More specifically, dereferencing a void pointer violates the wording of 6.5.3.2 Address and indirection operators, paragraph 4:
The unary * operator denotes indirection. If the operand points to a function, the result is a function designator; if it points to an object, the result is an lvalue designating the object. If the operand has type ''pointer to type'', the result has type ''type''. If an invalid value has been assigned to the pointer, the behavior of the unary * operator is undefined.
Since a pointer to void has no "type" - it can't be dereferenced. Note that this is beyond undefined behavior - it is a violation of the C language standard.
It probably doesn't work because it violates a rule in the ISO C standard which requires a diagnostic, and (I'm guessing) your compiler is treating that as a fatal situation.
According to ISO C99, as well as the C11 Draft (n1548), the only constraint on the use of the * dereferencing operator is "[t]he operand of the unary *operator shall have pointer type." [6.5.3.2¶2, n1548] The code we have here meets that constraint, and has no syntax error. Therefore no diagnostic is required for the use of the * operator.
However, what is the meaning of the * operator applied to a void * pointer?
"The unary * operator denotes indirection. If the operand points to a function, the result is a function designator; if it points to an object, the result is an lvalue designating the object. If the operand has type ‘‘pointer to type’’, the result has type ‘‘type’’. [6.5.3.2¶4, n1548]
The type void is neither a function nor an object type, so the middle sentence, which talks about producing a function or object designator, is not applicable to our case. The last sentence quoted above is applicable; it gives a requirement that an expression which dereferences a void * has void type.
Thus *px = 9; runs aground because it's assigning an int value to a void expression. An assignment requires an lvalue expression of object type; void is not an object type and the expression is certainly not an lvalue. The exact wording of the constraint is: "An assignment operator shall have a modifiable lvalue as its left operand." [6.5.16¶2, n1548] Violation of this constraint requires a diagnostic.
It appears from my perhaps naive reading of the standard that the expression *px as such is valid; only no attempt must be made to extract a result from it, or use it as the target of an assignment. If that is true, it could be used as an expression statement whose value is discarded: if (foo()) { *px; }, and it could be redundantly cast to void also: (void) *px. These apparently pointless situations might be somehow exploited by, or at least arise in, certain kinds of macros.
For instance, if we want to be sure that the argument of some macro is a pointer we can take advantage of the constraint that * requires a pointer operand:
#define MAC(NUM, PTR) ( ... (void) *(PTR) ...)
I.e. somewhere in the macro we dereference the pointer and throw away the result, which will diagnose if PTR isn't a pointer. It looks like ISO C allows this usage even if PTR is a void *, which is arguably useful.
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
Unlike C++, C has no notion of a const_cast. That is, there is no valid way to convert a const-qualified pointer to an unqualified pointer:
void const * p;
void * q = p; // not good
First off: Is this cast actually undefined behaviour?
In any event, GCC warns about this. To make "clean" code that requires a const-cast (i.e. where I can guarantee that I won't mutate the contents, but all I have is a mutable pointer), I have seen the following "conversion" trick:
typedef union constcaster_
{
void * mp;
void const * cp;
} constcaster;
Usage: u.cp = p; q = u.mp;.
What are the C language rules on casting away constness through such a union? My knowledge of C is only very patchy, but I've heard that C is far more lenient about union access than C++, so while I have a bad feeling about this construction, I would like an argument from the standard (C99 I suppose, though if this has changed in C11 it'll be good to know).
It's implementation defined, see C99 6.5.2.3/5:
if the value of a member of a union object is used when the most
recent store to the object was to a different member, the behavior is
implementation-defined.
Update: #AaronMcDaid commented that this might be well-defined after all.
The standard specified the following 6.2.5/27:
Similarly, pointers to qualified or unqualified versions of compatible
types shall have the same representation and alignment
requirements.27)
27) The same representation and alignment requirements are meant to
imply interchangeability as arguments to functions, return values from
functions, and members of unions.
And (6.7.2.1/14):
A pointer to a union object, suitably converted, points to each of its
members (or if a member is a bitfield, then to the unit in which it
resides), and vice versa.
One might conclude that, in this particular case, there is only room for exactly one way to access the elements in the union.
My understanding it that the UB can arise only if you try to modify a const-declared object.
So the following code is not UB:
int x = 0;
const int *cp = &x;
int *p = (int*)cp;
*p = 1; /* OK: x is not a const object */
But this is UB:
const int cx = 0;
const int *cp = &cx;
int *p = (int*)cp;
*p = 1; /* UB: cx is const */
The use of a union instead of a cast should not make any difference here.
From the C99 specs (6.7.3 Type qualifiers):
If an attempt is made to modify an object defined with a const-qualified type through use
of an lvalue with non-const-qualified type, the behavior is undefined.
The initialization certainly won't cause UB. The conversion between qualified pointer types is explicitly allowed in §6.3.2.3/2 (n1570 (C11)). It's the use of content in that pointer afterwards that cause UB (see #rodrigo's answer).
However, you need an explicit cast to convert a void* to a const void*, because the constraint of simple assignment still require all qualifier on the LHS appear on the RHS.
§6.7.9/11: ... The initial value of the object is that of the expression (after conversion); the same type constraints and conversions as for simple assignment apply, taking the type of the scalar to be the unqualified version of its declared type.
§6.5.16.1/1: (Simple Assignment / Contraints)
... 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;
... one operand is a pointer
to an object type, and the other is a pointer to a qualified or unqualified version of
void, and the type pointed to by the left has all the qualifiers of the type pointed to
by the right;
I don't know why gcc just gives a warning though.
And for the union trick, yes it's not UB, but still the result is probably unspecified.
§6.5.2.3/3 fn 95: If the member used to read the contents of a union object is not the same as the member last used to store a value in the object, the appropriate part of the object representation of the value is reinterpreted as an object representation in the new type as described in 6.2.6 (a process sometimes called "type punning"). This might be a trap representation.
§6.2.6.1/7: When a value is stored in a member of an object of union type, the bytes of the object representation that do not correspond to that member but do correspond to other members take unspecified values. (* Note: see also §6.5.2.3/6 for an exception, but it doesn't apply here)
The corresponding sections in n1124 (C99) are
C11 §6.3.2.3/2 = C99 §6.3.2.3/2
C11 §6.7.9/11 = C99 §6.7.8/11
C11 §6.5.16.1/1 = C99 §6.5.16.1/1
C11 §6.5.2.3/3 fn 95 = missing ("type punning" doesn't appear in C99)
C11 §6.2.6.1/7 = C99 §6.2.6.1/7
Don't cast it at all. It's a pointer to const which means that attempting to modify the data is not allowed and in many implementations will cause the program to crash if the pointer points to unmodifiable memory. Even if you know the memmory can be modified, there may be other pointers to it that do not expect it to change e.g. if it is part of the storage of a logically immutable string.
The warning is there for good reason.
If you need to modify the content of a const pointer, the portable safe way to do it is first to copy the memory it points to and then modify that.