11

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?

cpx
  • 17,009
  • 20
  • 87
  • 142
Dave
  • 12,408
  • 12
  • 64
  • 67

6 Answers6

18

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
  • 219,201
  • 40
  • 422
  • 469
  • 2
    The *address* of some variables is allowed to be in a constant expression, though (those with static storage duration). – caf Sep 21 '10 at 01:53
15

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
  • 41,948
  • 7
  • 88
  • 128
  • 1
    +1, the ones the preprocessor can't handle are called 'restricted constant expressions', I believe. – Carl Norum Sep 20 '10 at 22:07
  • @Carl: Nice to know, thanks. Is "restricted constant expressions" defined in some reference? I don't find it in C11 standard. – Tim Aug 15 '17 at 16:39
  • 1
    Hi @Tim, I don't have a spec handy, but Microsoft has something here: https://learn.microsoft.com/en-ca/cpp/c-language/c-constant-expressions – Carl Norum Aug 17 '17 at 19:53
5

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
  • 233,326
  • 40
  • 323
  • 462
  • 1
    +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.. GitHub STOP HELPING ICE Sep 21 '10 at 01:17
  • That's what i was looking for! – abhiarora Mar 18 '17 at 20:42
3

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
  • 84,970
  • 20
  • 145
  • 172
  • 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 Sep 20 '10 at 21:35
  • @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 Sep 20 '10 at 21:46
  • @cHao: `const` is a type qualifier. It applies to objects ... and no object can be a constant. – pmg Sep 20 '10 at 21:58
  • 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 Sep 20 '10 at 22:03
  • @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 Sep 20 '10 at 22:13
  • 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 Sep 20 '10 at 22:16
  • @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 Sep 20 '10 at 22:34
  • What about `const volatile int fixed = 42;`? :) – pmg Sep 20 '10 at 22:50
  • @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 Sep 20 '10 at 23:19
  • 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 Sep 20 '10 at 23:28
  • `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 Sep 21 '10 at 17:00
2

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
  • 77,689
  • 9
  • 166
  • 211
2

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

Jens Gustedt
  • 76,821
  • 6
  • 102
  • 177