C11 and constant expression evaluation in switch-case labels

我与影子孤独终老i 提交于 2019-12-13 10:06:44

问题


following this question Why doesn't gcc allow a const int as a case expression?, basically the same as What promoted types are used for switch-case expression comparison? or Is there any way to use a constant array with constant index as switch case label in C?.

From the first link, I tried to replace :

case FOO:                       // aka 'const int FOO = 10'

with :

case ((int) "toto"[0]):         // can't be anything *but* constant

Which gives :

https://ideone.com/n1bmIb -> https://ideone.com/4aOSXR = works in C++

https://ideone.com/n1bmIb -> https://ideone.com/RrnO2R = fails in C

I don't quite understand since the "toto" string can't be anything but a constant one, it isn't even a variable, it lies in the void of the compiler memory. I'm not even playing with the 'const' fuzzy logic of the C language (that really stands for "read-only, not constant, what did you expect?"), the problem is either "array access" or "pointer referencing" into a constant expression that do not evaluate in C, but do quite well in C++.

I expected to use this "trick" to use a HASH_MACRO(str) to generate unique case labels values from a key identifier, leaving eventually the compiler to raise an error in case of collision because of similar label values found.

OK, ok, I was told these restrictions were made to simplify language tooling (preproc, compiler, linker) and C ain't no LISP, but you can have full featured LISP interpreter/compilers for a fraction of the size of a C equivalent, so that's no excuse.

Question is : is there an "extension" to C11 that just allows this "toto" thingy to work in GCC, CLANG and... MSVC ? I don't want to go the C++ path (typedef's forward declarations don't work anymore) and because embedded stuff (hence the compile-time hash computation for space-time distortion).

Is there an intermediary "C+" language that is more 'permissive' and 'understand' embedded a little better, like -Praise the Lords- "enums as bitfield members", among nice other things we cannot have (because of out-of-reality standards evolving like snails under a desert sun) ?

#provemewrong, #changemymind, #norustplease


回答1:


It doesn't matter whether or not it could be known to the compiler at compile time. The case label needs to have a value that is an integer constant expression (C11 6.8.4.2p3).

  1. The expression of each case label shall be an integer constant expression and no two of the case constant expressions in the same switch statement shall have the same value after conversion. There may be at most one default label in a switch statement. (Any enclosed switch statement may have a default label or case constant expressions with values that duplicate case constant expressions in the enclosing switch statement.)

And the definition of an integer constant expression is in C11 6.6p6:

  1. An integer constant expression shall have integer type and shall only have operands that are integer constants, enumeration constants, character constants, sizeof expressions whose results are integer constants, _Alignof expressions, and floating constants that are the immediate operands of casts. Cast operators in an integer constant expression shall only convert arithmetic types to integer types, except as part of an operand to the sizeof or _Alignof operator.

Since "toto" is none of integer constants, enumeration constants, character constants, constant sizeof, _Alignof expressions or floating point constant cast to an integer; and that list was specified in the constraints section of the standard, the compiler must not pass this silently. (Even a conforming compiler may still successfully compile the program, but it must diagnose this as a constraint violation.)


What you can use is chained ? : to resolve the index to a character constant, i.e.

  x == 0 ? 't' 
: x == 1 ? 'o'
: x == 2 ? 't'
: x == 3 ? 'o'

This can be written into a macro.




回答2:


"toto[0]" is not an integer constant expression as C defines the term:

6.6 Constant expressions
...
6    An integer constant expression117) shall have integer type and shall only have operands that are integer constants, enumeration constants, character constants, sizeof expressions whose results are integer constants, _Alignof expressions, and floating constants that are the immediate operands of casts. Cast operators in an integer constant expression shall only convert arithmetic types to integer types, except as part of an operand to the sizeof or _Alignof operator.
117) An integer constant expression is required in a number of contexts such as the size of a bit-field member of a structure, the value of an enumeration constant, and the size of a non-variable length array. Further constraints that apply to the integer constant expressions used in conditional-inclusion preprocessing directives are discussed in 6.10.1.

C 2011 online draft




回答3:


The issue that you're running into is that, in C, "toto" is an array of chars. Sure, it's constant in memory, but it's still just an array. The [] operator indexes in an array (from a pointer). If you wanted, you would be able to edit a compiled binary and change the string "toto" to something else. In a sense, it is not compile-time known. It's equivalent to doing:

char * const ___string1 = "toto";
...
case ((int) ___string1[0]):

(This is a little forced and redundant, but it's just for demonstration)

Note that the type of the elements of a string literal is char, not const char.

The case, must be a constant however, as it is built into the compiled program control flow.



来源:https://stackoverflow.com/questions/50510859/c11-and-constant-expression-evaluation-in-switch-case-labels

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!