Does Making a Struct Volatile Make All Its Members Volatile

Does making a struct volatile make all its members volatile?

Another question can be asked (or simply another way to look at the original question):

Does making a struct const make all its members const?

If I have:

struct whatever { int data; };

const whatever test;

Will test.data be const too?

My answer is : Yes. If you declare an object of type whatever with const then all its members will be const too

Similarly, if you declare an object of type whatever with volatile then all its members will be volatile too, just like if you declare the object with const, all it's member will be const too.

const and volatile are two faces of the same coin; they're so that the Standard often refers to them as cv-qualifiers.


Quoting from the Standard ($7.1.5.1/8)

[Note: volatile is a hint to the
implementation
to avoid aggressive
optimization involving the object
because the value of the object might
be changed by means undetectable by an
implementation. See 1.9 for detailed
semantics. In general, the semantics
of volatile are intended to be the
same in C + + as they are in C. ]

That means, if your object is an instance of a struct, then the compiler cannot avoid aggressive optimization involving the object, unless it avoids aggressive optimization of each of it's members. (Otherwise, how else it can avoid optimization involving the object?)


Related topic:

Why do we use volatile keyword in C++?

Volatile Struct Semantics

In your example, the two are the same. But the issues revolve around pointers.

First off, volatile uint8_t *foo; tells the compiler the memory being pointed to is volatile. If you want to mark the pointer itself as volatile, you would need to do uint8_t * volatile foo;

And that is where you get to the main differences between marking the struct as volatile vs marking individual fields. If you had:

typedef struct
{
uint8_t *field;
} foo;

volatile foo f;

That would act like:

typedef struct
{
uint8_t * volatile field;
} foo;

and not like:

typedef struct
{
volatile uint8_t *field;
} foo;

volatile struct = struct not possible, why?

This is ill-formed because FOO has an implicit copy constructor defined as:

FOO(FOO const&);

And you write FOO test = foo; with foo of type volatile FOO, invoking:

FOO(volatile FOO const&);

But references-to-volatile to references-to-non-volatile implicit conversion is ill-formed.

From here, two solutions emerge:

  1. don't make volatile to non-volatile conversions;
  2. define a suited copy constructor or copy the object members "manually";
  3. const_cast can remove the volatile qualifier, but this is undefined behavior to use that if your underlying object is effectively volatile.

Could I possibly use memcopy() for that?

No you cannot, memcpy is incompatible with volatile objects: thre is no overload of it which takes pointers-to-volatile, and there is nothing you can do without invoking undefined behavior.

So, as a conclusion, your best shot if you cannot add a constructor to FOO is to define:

FOO FOO_copy(FOO volatile const& other)
{
FOO result;
result.a = other.a;
result.b = other.b;
result.c = other.c;
return result;
}

Or with C++11's std::tie:

FOO FOO_copy(FOO volatile const& other)
{
FOO result;
std::tie(result.a, result.b, result.c) = std::tie(other.a, other.b, other.c);
return result;
}

In C, how do you declare the members of a structure as volatile?

Exactly the same as non-struct fields:

#include <stdio.h>
int main (int c, char *v[]) {
struct _a {
int a1;
volatile int a2;
int a3;
} a;
a.a1 = 1;
a.a2 = 2;
a.a3 = 3;
return 0;
}

You can mark the entire struct as volatile by using "volatile struct _a {...}" but the method above is for individual fields.

Passing a pointer to a struct with a volatile member as a function argument

Short answer: No need to add volatile on hdlPtr.

Long Answer: Unless the hdlPtr can be changed in some unexpected way, there is no need to declare it volatile. Given that it's being local to the function, it can not be changed by anything other than the uartInitialize. Given that you declared it 'const', it can not be changes by the uartInitialize itself.

Is the order of writes to separate members of a volatile struct guaranteed to be preserved?

c

They will not be reordered.

C17 6.5.2.3(3) says:

A postfix expression followed by the . operator and an identifier designates a member of a structure
or union object. The value is that of the named member, 97) and is an lvalue if the first expression is
an lvalue. If the first expression has qualified type, the result has the so-qualified version of the type
of the designated member.

Since data has volatile-qualified type, so do data.bar and data.foo. Thus you are performing two assignments to volatile int objects. And by 6.7.3 footnote 136,

Actions on objects so declared [as volatile] shall not be “optimized out” by
an implementation or reordered except as permitted by the rules for evaluating expressions.

A more subtle question is whether the compiler could assign them both with a single instruction, e.g., if they are contiguous 32-bit values, could it use a 64-bit store to set both? I would think not, and at least GCC and Clang don't attempt to.

How to initialise a volatile structure with a non-volatile structure?

You are missing what the error message actually says. The compiler is telling you that to assign the whole structure in one go needs a "copy constructor" which understands the volatile qualifier and there isn't one. Have a look at this answer for a discussion of what the error means.

But when you assign the individual elements of the structure one by one no copy constructor is needed and so the code works fine. Why are you "trying to avoid" this?

What you are expecting the volatile qualifier to do? In C/C++ it ONLY prevents the compiler from optimising away your variables or the code which uses them. Nothing more.

It wouldn't be useful to define a stock copy constructor for volatile structures, since your concurrency requirements will differ from everyone else's.

To guarantee that your structure elements are assigned consistently, you may need to disable interrupts, like this:

cli();
inner.x0 = outer.x0 + 1;
inner.y0 = outer.y0 + 1;
inner.width = 0;
inner.height = outer.height - 2;
sei();

But you'll have to analyse precisely what you need and that's heading off topic.



Related Topics



Leave a reply



Submit