Details of what constitutes a constant expression in C?

2.2k views Asked by At

C defines at least 3 levels of "constant expression":

  • constant expression (unqualified)
  • arithmetic constant expression
  • integer constant expression

6.6 paragraph 3 reads:

Constant expressions shall not contain assignment, increment, decrement, function-call, or comma operators, except when they are contained within a subexpression that is not evaluated.

So does this mean 1,2 is not a constant expression?

Paragraph 8 reads:

An arithmetic constant expression shall have arithmetic type and shall only have operands that are integer constants, floating constants, enumeration constants, character constants, and sizeof expressions. Cast operators in an arithmetic constant expression shall only convert arithmetic types to arithmetic types, except as part of an operand to a sizeof operator whose result is an integer constant.

What are the operands in (union { uint32_t i; float f; }){ 1 }.f? If 1 is the operand, then this is presumably an arithmetic constant expression, but if { 1 } is the operand, then it's clearly not.

Edit: Another interesting observation: 7.17 paragraph 3 requires the result of offsetof to be an integer constant expression of type size_t, but the standard implementations of offsetof, as far as I can tell, are not required to be integer constant expressions by the standard. This is of course okay since an implementation is allowed (under 6.6 paragraph 10) to accept other forms of constant expressions, or implement the offsetof macro as __builtin_offsetof rather than via pointer subtraction. The essence of this observation, though, is that if you want to use offsetof in a context where an integer constant expression is required, you really need to use the macro provided by the implementation and not roll your own.

2

There are 2 answers

14
Chris Lutz On BEST ANSWER

Based on your reading, 1,2 isn't a constant expression. I don't know why it isn't, just that I agree with you that it isn't (despite the fact that it probably should be).

6.5.2 specifies compound literals as a postfix operator. So in

(union { uint32_t i; float f; }){ 1 }.f

The operands are (union { uint32_t i; float f; }){ 1 } and f to the . operator. It is not an arithmetic constant expression, since the first argument is a union type, but it is a constant expression.

UPDATE: I was basing this on a different interpretation of the standard.

My previous reasoning was that (union { uint32_t i; float f; }){ 1 }.f met the criteria for a constant expression, and was therefore a constant expression. I still think it meets the criteria for a constant expression (6.6 paragraph 3) but that it is not any of the standard types of constant expressions (integer, arithmetic, or address) and is therefore only subject to being a constant expression by 6.6 paragraph 10, which allows implementation-defined constant expressions.

I'd also been meaning to get to your edit. I was going to argue that the "hack" implementation of offsetof was a constant expression, but I think it's the same as above: it meets the criteria for a constant expression (and possibly an address constant) but is not an integer constant expression, and is therefore invalid outside of 6.6 paragraph 10.

11
Sjoerd On

If 1,2 would be a constant expression, this would allow code like this to compile:

{ // code        // How the compiler interprets:
  int a[10, 10]; // int a[10];

  a[5, 8] = 42;  // a[8] = 42;
}

I don't know whether it is the real reason, but I can imagine that emitting an error for this (common?) mistake was considered more important than turning 1,2 into a constant expression.

UPDATE: As R. points out in a comment, the code about is not longer a compiler error since the introduction of VLAs.