Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does discarding a value result in reading it?

Consider the following example:

{
  int x;
  (void)x; // silence the "unused" warning
  ...
}

Does this lead to undefined behavior due to x being read uninitialized? If yes, then does this mean that in the following code a memory read instruction (to read the pointee) must be emitted by the compiler?

volatile char* p=getP();
(void)*p;

I'm interested in both the C and C++ rules regarding this, in case they differ.

like image 863
Ruslan Avatar asked Oct 15 '19 10:10

Ruslan


People also ask

Can you dispute a data value and calculate a more accurate mean?

Can therefore dispute a data value and calculate a more accurate mean. ? However, having more results to generate a mean does not in itself make the results more accurate. If you have a zero error making every result biased, that won't go away in the averaging process.

Does having more results mean more accurate results?

However, having more results to generate a mean does not in itself make the results more accurate. If you have a zero error making every result biased, that won't go away in the averaging process. Random errors are spread about the mean and so they go away when you do the average. ?

What is the advantage of using repeated results?

By using repeated results, I could detect any anomalous results and find out an average of the repeated results to obtain a more accurate result because any random errors would be excluded out. ? This is a 3 mark question in an exam tomorrow, and I'm not too sure what to write. It's GCSE ISA. It's supposed to say accurate & not reliable...

Does repetition of results increase accuracy?

If you have a zero error making every result biased, that won't go away in the averaging process. Random errors are spread about the mean and so they go away when you do the average. ? Strictly speaking, repetition of results would increase precision, it would do nothing to improve accuracy. Actually, repeating doesn't improve precision.


2 Answers

In C++, an access is coupled with a lvalue-to-rvalue conversion. This conversion is what takes an "identity" and produces its value. On the subject of discarded value expressions, the C++ standard says this:

[expr] (emphasis mine)

12 In some contexts, an expression only appears for its side effects. Such an expression is called a discarded-value expression. The array-to-pointer and function-to-pointer standard conversions are not applied. The lvalue-to-rvalue conversion is applied if and only if the expression is a glvalue of volatile-qualified type and it is one of the following:

  • ( expression ), where expression is one of these expressions,
  • id-expression,
  • subscripting,
  • class member access,
  • indirection,
  • pointer-to-member operation,
  • conditional expression where both the second and the third operands are one of these expressions, or
  • comma expression where the right operand is one of these expressions.

[ Note: Using an overloaded operator causes a function call; the above covers only operators with built-in meaning.  — end note ] If the expression is a prvalue after this optional conversion, the temporary materialization conversion is applied. [ Note: If the expression is an lvalue of class type, it must have a volatile copy constructor to initialize the temporary that is the result object of the lvalue-to-rvalue conversion.  — end note ] The glvalue expression is evaluated and its value is discarded.

In the non-volatile case, there is no lvalue-to-rvalue conversion. Therefore we can say with confidence the variable is not accessed.

In the volatile case however, you have indirection inside the cast-expression, and that undergoes a lvalue-to-rvalue conversion. As such, the volatile is read, and you get undefined behavior out of reading an uninitialized object.

To avoid the undefined behavior around volatile glvalues, one can annotate the relevant variables as [[maybe_unused]]. That is the sanctioned way, and is the one I'd prefer over a cast in the non-volatile case too.

like image 74
StoryTeller - Unslander Monica Avatar answered Oct 11 '22 00:10

StoryTeller - Unslander Monica


Relevant part from the C standard:

6.3.2.2 void

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.)

Where side-effects are defined by 5.1.2.3/2:

Accessing a volatile object, modifying an object, modifying a file, or calling a function that does any of those operations are all side effects, which are changes in the state of the execution environment.

Reading a non-volatile variable is not a side-effect.

That is, if accessing x is a side-effect, then the code must be evaluated (executed). This is only the case when x is volatile. So (void)x; will not trigger undefined behavior.

Otherwise it would have been undefined behavior to use the local variable x, because its address is never taken anywhere inside the scope.

In case of *p you have a clear lvalue access of a volatile-qualified variable through the * operator, so the compiler must read the variable. Regardless of the cast to (void).

The example below would also be evaluated, but invokes undefined behavior (unless the address of the pointer p itself is taken):

char* volatile p;
(void)p;
like image 31
Lundin Avatar answered Oct 11 '22 01:10

Lundin