Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to discard the designator of a void expression?

C11 has the following description of void expressions:

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

I have read many people's explanations of this passage. Now, I probably understand what it means to discard its value. However, the "designator is discarded" in this passage puzzles me.

What is meant by "designator is discarded"? What does the "designator" refer to here? Could you give me an example to have a look?

This question might be very simple and perhaps of no use at all. But I still want to know the intention of the standard here.

like image 528
user24723440 Avatar asked Dec 12 '25 00:12

user24723440


2 Answers

The C standard uses the word “designator” to refer to three things, and the only one that is applicable here is a function designator.

The name of a function is a function designator, and the result of applying unary * to a pointer to a function is a function designator.

Places where expressions are evaluated as void expressions are:

  • The expression in an expression statement.
  • The left operand of the , operator.
  • The third part in the parentheses of a for statement.

In saying the function designator is discarded, the standard is telling us, when f is a function name, that there is no effect of the expression-statement f;, that the evaluation of f, 3 produces just 3, and that there is no effect from the f in for (i = 0; i < n; f) ….

Since each of these examples would have the same semantics whether f were present or were replaced with some other expression with no side effects, like 0, there is no reason in C semantics to use a function name in these places or to use a function pointer with * applied rather than merely using the function pointer. (The function pointer expression could contain side effects, so the code could be semantically different if it were absent.) The committee likely specified what happens when function designators are evaluated as void expressions for completeness, and we can imagine that leftover function designators might arise out of various preprocessor constructions. (For example, when various features are selected by preprocessor definitions, the macros are replaced with various needed expressions, and when the features are not selected, the macros are replaced by some default “we do not care about this” code that happens to be a function designator.)

Supplement

It is arguable that there is no need to include a designator in 6.3.2.2 because a void expression never consists of a function designator because a function designator is automatically converted to a pointer, and it is the pointer that is discarded. C 2018 6.3.2.1 4 says that a function designator is automatically converted to a pointer except when it is the operand of sizeof or of unary &. So, for example, in the statement-expression main;, main is not the operand of sizeof or unary &, so it is converted to a pointer to main, and then the pointer is discarded.

The other two things the standard uses the word “designator” for are designators in initializers, like the .foo in struct bar x = { .foo = 3 };, and the member designator in the offsetof macro. Neither of these is a thing that could appear as a void expression.

The standard also says that an identifies “designate” entities, including objects, functions, types, labels. Of these, only objects and functions can appear in void expressions (C 2018 6.5.1 2: “An identifier is a primary expression, provided it has been declared as designating an object (in which case it is an lvalue) or a function (in which case it is a function designator).” However, if we consider an object to be a designator and conclude that foo; discards the designator foo, then there is a problem when foo is volatile, as foo; would merely discard the designator and not perform lvalue conversion, including a read of the volatile object foo.

Although I do not have concrete examples, I suspect programmers working with hardware registers would expect foo; to perform a read of the volatile object. This also supports the hypothesis above that a function designator is never actually a void expression, since, if lvalue conversion is performed on an object foo in foo;, we would expect that the function designator conversion is performed on a function foo in foo;.

like image 69
Eric Postpischil Avatar answered Dec 13 '25 16:12

Eric Postpischil


The C Standard distinguishes between values and function designators, that are not referred to as values:

6.3.2.1 Lvalues, arrays, and function designators

[...] A function designator is an expression that has function type. Except when it is the operand of the sizeof operator, a typeof operator, or the unary & operator, a function designator with type "function returning type" is converted to an expression that has type "pointer to function returning type".

Given the proximity (6.3.2.1 and 6.3.2.2) it is likely designator in the quoted paragraph refers to a function designator.

An expression that evaluates to a function designator (eg: the name of a function (main) or a call to the signal function (that technically returns a pointer to a function)), that is not stored into an object, passed to another function or used as an initializer, is simply discarded (ie: not used). It is only evaluated for its side effects, if any.

like image 42
chqrlie Avatar answered Dec 13 '25 16:12

chqrlie



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!