views:

158

answers:

6

I'm a tad confused between what is and is not a Constant Expression in C, even after much Googleing. Could you provide an example of something which is, and which is not, a Constant Expression in C?

+7  A: 

A constant expression can be evaluated at compile time. That means it has no variables in it. For example:

5 + 7 / 3

is a constant expression. Something like:

5 + someNumber / 3

is not, assuming someNumber is a variable (ie, not itself a compile-time constant).

Carl Norum
Thanks very much Carl, extremely helpful! :)
Dave Gallagher
The *address* of some variables is allowed to be in a constant expression, though (those with static storage duration).
caf
+8  A: 

There is another subtlety to constant expressions. There are some things that are known to the compiler, but cannot be known to the preprocessor.

For example (24*60*60) can be computed by both, but sizeof struct foo is only known to the compiler. This distinction can matter if you are trying to verify that a struct is defined to meet an externally mandated size, or that its members are mapped at externally specified offsets. (This use case often arises when coding device drivers where the struct describes device registers as layed out in memory space.)

In that instance you cannot simply say #if (sizeof(struct UART) == 12) because the preprocessor operates at a pass ahead of the compilation and simply cannot know the size of any types. It is, however, a constant expression and would be valid as an initializer for a global variable (e.g. int UARTwords = sizeof(struct UART) / sizeof(short);), or to declare the size of an array (e.g. unsigned char UARTmirror[sizeof(struct UART)];)

RBerteig
+1, the ones the preprocessor can't handle are called 'restricted constant expressions', I believe.
Carl Norum
+1  A: 

Any single-valued literal is a constant expression.

3     0.0f    '\n'

(String literals are weird, because they're actually arrays. Seems "hello" isn't really a constant, as it ends up having to be linked and all that, and the address and contents can change at runtime.)

Most operators (sizeof, casts, etc) applied to constants or types are constant expressions.

sizeof(char)
(byte) 15

Any expression involving only constant expressions is itself also a constant expression.

15 + 3
0.0f + 0.0f
sizeof(char)

Any expression involving function calls or non-constant expressions is usually not a constant expression.

strlen("hello")
fifteen + x

Any macro's status as a constant expression depends on what it expands to.

/* Always a constant */
#define FIFTEEN 15

/* Only constant if (x) is
#define htons(x)  (( ((x) >> 8) | ((x) << 8) ) & 0xffff) 

/* Never constant */
#define X_LENGTH  strlen(x)

I originally had some stuff in here about const identifiers, but i tested that and apparently it doesn't apply in C. const, oddly enough, doesn't declare constants (at least, not ones "constant" enough to be used in switch statements). In C++, however, it does.

cHao
Note that sizeof() is *always* a constant expression, even if the argument is a non-const expression since it is the *type* on the expression that is the argument, not the expression itself.
Clifford
@Clifford: in `C99`, sizeof applied to a VLA does not specify a constant "6.5.3.4 ... If the type of the operand is a variable length array type, the operand is evaluated; otherwise, the operand is not evaluated and the result is an integer constant."
pmg
@cHao: `const` is a type qualifier. It applies to objects ... and no object can be a constant.
pmg
Hm, I doubt a bit that string literals are constant expressions. They have type `char[]`, you can take an address of them and apply the `[]` operator to them, e.g `"Hello"[1]` is a valid expression.
Jens Gustedt
@pmg: If you say `const int zero = 0`, then `switch (x) { case zero: break; default: break; }` compiles in C++. It's still not valid C, though.
cHao
Yeah @cHao ... `C` and `C++` have their differences. In `C`, your `zero` above is, first of all, an object. You can take its address, apply `sizeof` to it, ... (don't know if you can do that in `C++`)
pmg
@pmg: Seems so. In C++ `zero` acts like both a real variable and a constant expression. I can take the address of it, apply `sizeof`, everything except change its value. Even that can be done to some degree, but i wouldn't count on it not to screw everything up. (The compiler probably hard-coded 0 instead of `zero` wherever it needed a constant.)
cHao
What about `const volatile int fixed = 42;`? :)
pmg
@pmg, many I/O device registers are correctly declared `const volatile`. The `const` indicates a read-only register. The `volatile` indicates a register whose value can change outside the flow of control of this compilation unit, and a hardware status register is a great example of such a value. The whole trick is to get the declaration of the register to be defined (and linked) so that it is placed on the actual I/O device, of course.
RBerteig
I mean: is `fixed` as in `const volatile int fixed = 42;` a `C++` constant usable, for instance, as a case label in a switch statement? (I don't have a C++ compiler here, or I'd test it ... and I don't feel like searching for a C++ specification and look through it)
pmg
@pmg: Good point.
Clifford
`volatile const` s aren't actually constants -- they're just read-only variables. So no, they wouldn't be treated as constants. I'm pretty sure `extern` stuff has some odd rules about it too, particularly if it's not declared and set at the same time.
cHao
+1  A: 

Another fun little wrinkle: in C, the value of an 'enum' is a constant, but may only be used after the declaration of the 'enum' is complete. The following, for example, is not acceptable in standard C, though it is acceptable in C++:

enum {foo=19, bar, boz=bar+5;};

It could be rewritten:

enum {foo=19, bar}; enum {boz=bar+5;};

though this would end up defining multiple different enumeration types, rather than one which holds all the values.

supercat
Just to mention that these `enum` constants have type `int`.
Jens Gustedt
+1  A: 

Also integral character constants as 'a' or '\n' are constants that the compiler recognizes as such. They have type int.

Jens Gustedt
+2  A: 

Nobody seems have mentioned yet another kind of constant expression: address constants. The address of an object with static storage duration is an address constant, hence you can do this kind of thing at file scope:

char x;
char *p = &x;

String literals define arrays with static storage duration, so this rule is also why you can do this at file scope:

char *s = "foobar";
caf
+1, but it should be noted that this kind of constant expression is usually **not** an actual constant at compile time, and maybe not at link time either if you're using dynamic linking. In most real-world implementations, it results in a *relocation*.
R..