Why is "volatileQualifiedExpr + volatileQualifiedExpr" not necessarily UB in C but in C++?
Asked Answered
E

1

10

When I today read the C Standard, it says about side effects

Accessing a volatile object, modifying an object, modifying a file, or calling a function that does any of those operations are all side effects

and the C++ Standard says

Accessing an object designated by a volatile glvalue (3.10), modifying an object, calling a library I/O function, or calling a function that does any of those operations are all side effects

Hence because both forbid unsequenced side effects to occur on the same scalar object, C allows the following, but C++ makes it undefined behavior

int a = 0;
volatile int *pa = &a;

int b = *pa + *pa;

Am I reading the specifications correctly? And what is the reason for the discrepancy, if so?

Evilminded answered 14/10, 2012 at 10:17 Comment(9)
I don't think that there is a discrepancy. For C the type of the object is the type through which the object is read, not the type an object originally has been declared. So *pa is an lvalue designating a volatile qualified object.Seurat
@JensGustedt i was assuming it refers to the concept of "effective type". Which for an object with a declared type is defined to be that declared type. So for the access "*pa", its effective type is "int" here unless I'm missing something.Evilminded
"When I today read the C Standard" <- Do you usually read the C standard in the morning or at night? (173k rep, sigh...)Marysa
@H2CO3 i was fixing wikipedia and wanted to get a C Standard ref :)Evilminded
Ah.. fixing wiki's Comp-Sci articles on variable volatility. Cause just reading the C-standard would be weird, but doing so to update wiki-comp-sci articles is much more...normal? =P Btw. great question.Interlunar
Yes, right, the effective type here is int. Interestingly if you would have used volatile int *pa = calloc(sizeof *pa, 1); the effective type would volatile.Seurat
Another thing that comes to mind is that after int i = 0; int b = i++ + i++; // i = 2 now, but volatile int i = 0; int b = i++ + i++; // i = 1 now. I believe that all operations on a volatile operand are pooled together across a single assignment. In C++ this is explicitly left undefined.Autoeroticism
I do not find the words 'effective type' together in the C++ standard. Just to clarify (as it took me a while to get this just now), the side effect here just means that the program is influenced by something outside of the program flow, not necessarily that it has a side effect. Reading the whole section I agree with the interpretation that this is not allowed in C++ as the program can't "wait" for the completed access from two volatile reads at the same time. (don't know about C).Lethargy
@JohanLundberg, effective type is a concept in the C standard, not C++.Seurat
L
1

I don't believe there is an effective variation between C and C++ in this regards. Though the wording on sequencing varies the end result is the same: both result in undefined behaviour (though C seems to indicate the evaluation will suceed but with an undefined result).

In C99 (sorry, don't have C11 handy) paragraph 5.1.2.3.5 specifies:

— At sequence points, volatile objects are stable in the sense that previous accesses are complete and subsequent accesses have not yet occurred.

Combined with your quote from 5.1.2.3.2 would indicate the value of pa would not be in a stable state for at least one of the accesses to pa. This makes logical sense since the compiler would be allowed to evaluate them in any order, just once, or at the same time (if possible). It doesn't actually define what stable means however.

In C++11 there is explicit reference to unsequenced oeprations at 1.9.13. Then point 15 indicates such unsequenced operations on the same operand is undefined. Since undefined behaviour can mean anything happens it is perhaps strong than C's unstable behaviour. However, in both cases there is no guaranteed result of your expression.

Leonardo answered 14/10, 2012 at 15:3 Comment(5)
sorry I dont understand this answer.Evilminded
In short, there is no real difference in the standard between C and C++ regarding unsequenced access to volatiles. Neither guarantees an outcome for the calculation.Leonardo
This answer seems to have the relevant C11 wording: (6.5p2) "If a side effect on a scalar object is unsequenced relative to either a different side effect on the same scalar object or a value computation using the value of the same scalar object, the behavior is undefined."Stonybroke
That's a good update. It now formalizes it is also undefined in C. So behaviour is the same in both languages now.Leonardo
@edA-qamort-ora-y : I believe the behavior has always been the same in both languages. The OP (Johannes) provided a couple of quotes from the standards which seem to say basically the same thing: accessing a volatile object from two directions at once is bad news. C11 adopts C++11's concept of sequenc ing (as opposed to sequence points), but it doesn't change the implications re UB in this case.Hellespont

© 2022 - 2024 — McMap. All rights reserved.