After casting a volatile variable in c, is the variable still volatile? - c

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.

Related

Constant Integer pointer passed to the function expecting a integer pointer modifies the value of constant integer? [duplicate]

I have the following piece of code:
void TestFunc(const void * const Var1, const float Var2)
{
*(float*)Var1 = Var2;
}
It looks like I am changing the value of the const object the const pointer points to (thanks sharptooth), which should not be allowed. Fact is, none of the compilers I tried issued a warning. How is this possible?
As others mentioned, the cast removes the 'constness' of the destination as far as the expression is concerned. When you use a cast the compiler treats the expression according to the cast - as long as the cast itself it valid (and C-style casts are pretty much the big hammer). This is why you don't get an error or warning. You're essentially telling the compiler, "be quiet, I know what I'm doing, this is how you should treat things". In fact, casts are probably the #1 way for programmers to get the compiler to stop issuing warnings.
Your assignment expression may or may not be undefined behavior. It is permitted to cast away constness if the object actually pointed to is not const.
However, if the object pointed to is const, then you have undefined behavior.
void TestFunc(const void * const Var1, const float Var2)
{
*(float*)Var1 = Var2;
}
int
main(void)
{
float x = 1.0;
const float y = 2.0;
TestFunc( &x, -1.0); // well defined (if not particularly great style)
TestFunc( &y, -2.0); // undefined behavior
return 0;
}
You're treading dangerous waters...
In general (I'm sure there are exceptions), casting so that expressions treat objects as they really are is supported, well-defined behavior in C/C++.
This particular behavior is covered in the standards mostly by statements that modifying a const object through a cast (or something) that removes the const qualifier is undefined. The inference is that doing the same for a non-const object is not undefined. An example given in the C++ standard makes this clear.
C90 6.5.3 - Type Qualifiers (C99 6.7.3):
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.
C++ 7.1.5.1 The cv-qualifiers
A pointer or reference to a cv-qualified type need not actually point or refer to a cv-qualified object, but it is treated as if it does; a const-qualified access path cannot be used to modify an object even if the object referenced is a non-const object and can be modified through some other access path. [Note: cv-qualifiers are
supported by the type system so that they cannot be subverted without casting (5.2.11). ]
Except that any class member declared mutable (7.1.1) can be modified, any attempt to modify a const
object during its lifetime (3.8) results in undefined behavior.
...
[Example:
...
int i = 2; //not cv-qualified
const int* cip; //pointer to const int
cip = &i; //OK: cv-qualified access path to unqualified
*cip = 4; //ill-formed: attempt to modify through ptr to const
int* ip;
ip = const_cast<int*>(cip); //cast needed to convert const int*to int*
*ip = 4; //defined: *ip points to i, a non-const object
const int* ciq = new const int (3); //initialized as required
int* iq = const_cast<int*>(ciq); //cast required
*iq = 4; //undefined: modifies a const object
You change the object the pointer points to, not the pointer value.
C-style cast acts like a const_cast and removes the const modifier off the pointer. The compiler now has nothing to moan about.
The cast is legal, but the behaviour is undefined.

Why doesn't re-assigning a pointer to a const int give a compile error?

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.

Are both of these volatile qualifier usages redundant?

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

C pass address of a function return value as function parameter

I have two functions:
void a(int * p);
int b();
Is it possible to pass the address of the return value of function b to function a something like this: a(&b()) ?
A compound literal seems to do the trick (requires a C99 compiler):
int a()
{
return 5;
}
void b(int *a)
{
// do something with a
printf("%d\n", *a);
}
int main(void)
{
b(&(int){ a() });
}
Output is 5.
Only by using a temporary variable:
int result = b();
a(&result);
EDIT: Apparently there's also a way to do this using a compound literal, described in another answer here.
Nope - what would be the address of that return value? You need to store the return value in a variable, then pass the address of that variable in.
No. C11 says that (emphasis mine):
6.5.3.2.1
The operand of the unary & operator shall be either a function designator, the result of a [] or unary * operator, or an lvalue that designates an object that is not a bit-field and is not declared with the register storage-class specifier.
Now it clearly isn't a function, a [] or a unary *. Let's see what is an lvalue:
6.3.2.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. ...
64) The name ‘‘lvalue’’ comes originally from the assignment expression E1 = E2, in which the left
operand E1 is required to be a (modifiable) lvalue. It is perhaps better considered as representing an
object ‘‘locator value’’. What is sometimes called ‘‘rvalue’’ is in this International Standard described
as the ‘‘value of an expression’’.
An obvious example of an lvalue is an identifier of an object. As a further example, if E is a unary
expression that is a pointer to an object, *E is an lvalue that designates the object to which E points.
So in short, an lvalue designates an object. Since b() doesn't designate a specific object, it is not an lvalue. You can tell this by seeing also that b() = 2; is wrong.
No you can't do that in C. Also note C doesn't have something called as reference as in C++.
For that C has pointers to achieve the same behaviour..
To achieve the same goal in C use temporary variable like,
int val;
val = b();
a( &val ); // & is called as "address of" operator in C
To answer why there is no reference in C, from my understanding pointer and reference are meant to achieve the same goal - minimizing or avoiding data copy. For this C's pointer is good enough, though technically both have some difference in C++ (taken for reference)
You could pass a reference to b() as an argument to a then call b from a:
void a(void (*b)(int)) {
// call b
int local_var = b(<something>);
}

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