Constant Expressions in C

S

skibud2

Consider this example:

u8 my_array[10];

for (i = 0; i < (sizeof(my_array)/sizeof(u8)); i++) {
...
}

In the standard C specification, is the evaluation of
'(sizeof(my_array)/sizeof(u8))' to 10 guarenteed to happen at compile
time? Or is it a compiler optimization? From what I have read, it
looks like it is a compiler optimization, but I would like to get
confirmation.

Now if is not guarenteed to happen at compile time, is it better to do
something like the following:

const u8 my_array_size = (sizeof(my_array)/sizeof(u8));

to guarentee that you are not doing a divide a run time? On the
systems I have worked on, divides take a very long time. However,
almost every compiler that I have dealt with does the evaluation at
compile time.
 
C

christian.bau

Now if is not guarenteed to happen at compile time, is it better to do
something like the following:

const u8 my_array_size = (sizeof(my_array)/sizeof(u8));

to guarentee that you are not doing a divide a run time? On the
systems I have worked on, divides take a very long time. However,
almost every compiler that I have dealt with does the evaluation at
compile time.

Just curious: Which compiler didn't?
 
M

Malcolm McLean

skibud2 said:
Consider this example:

u8 my_array[10];

for (i = 0; i < (sizeof(my_array)/sizeof(u8)); i++) {
...
}

In the standard C specification, is the evaluation of
'(sizeof(my_array)/sizeof(u8))' to 10 guarenteed to happen at compile
time? Or is it a compiler optimization? From what I have read, it
looks like it is a compiler optimization, but I would like to get
confirmation.

Now if is not guarenteed to happen at compile time, is it better to do
something like the following:

const u8 my_array_size = (sizeof(my_array)/sizeof(u8));

to guarentee that you are not doing a divide a run time? On the
systems I have worked on, divides take a very long time. However,
almost every compiler that I have dealt with does the evaluation at
compile time.
Effectively you are guaranteed that the compiler will evaluate constant
expressions at compile time. The standard doesn't specify any particular
sequence of machine code instructions as long as the behaviour is correct,
because it was decided not to try impose performance requirements at all.
 
I

Ian Collins

skibud2 said:
Consider this example:

u8 my_array[10];

for (i = 0; i < (sizeof(my_array)/sizeof(u8)); i++) {
...
}

In the standard C specification, is the evaluation of
'(sizeof(my_array)/sizeof(u8))' to 10 guarenteed to happen at compile
time? Or is it a compiler optimization? From what I have read, it
looks like it is a compiler optimization, but I would like to get
confirmation.
sizeof(my_array)/sizeof(u8) is required to be a compile time constant,
consider

int my_array[10];
int other[sizeof(my_array)/sizeof(int)];
Now if is not guarenteed to happen at compile time, is it better to do
something like the following:

const u8 my_array_size = (sizeof(my_array)/sizeof(u8));
Better in the sense it makes the code easier to read.
 
J

Jack Klein

Consider this example:

u8 my_array[10];

for (i = 0; i < (sizeof(my_array)/sizeof(u8)); i++) {
...
}

In the standard C specification, is the evaluation of
'(sizeof(my_array)/sizeof(u8))' to 10 guarenteed to happen at compile
time? Or is it a compiler optimization? From what I have read, it
looks like it is a compiler optimization, but I would like to get
confirmation.

Now if is not guarenteed to happen at compile time, is it better to do
something like the following:

const u8 my_array_size = (sizeof(my_array)/sizeof(u8));

I wouldn't do this, C is not C++. If you define this at file scope,
it has external linkage and the compiler must actually create the
object, because it cannot tell whether another translation unit will
refer to it by name.

Even if you define it as static, some C compilers will create the
object and read its value when you use it in an expression.
to guarentee that you are not doing a divide a run time? On the
systems I have worked on, divides take a very long time. However,
almost every compiler that I have dealt with does the evaluation at
compile time.

Despite what others have said, the C language standard does not
require a compiler to evaluate constant expressions at compile time
unless it needs to use the value at compile time, as in Ian's example
of using it to define the size of an array.

Another such case would be where an integer constant is used as a case
value in a switch statement.

In this particular case, it makes much more sense to define the value
ahead of time and use it both for defining the array and when you need
the number of its elements:

#define MY_ARRAY_SIZE /* some value */

u8 my_array [MY_ARRAY_SIZE];

In the general case, you are better off using a nameless enum, so long
as the value fits in the range of an int:

enum { my_array_size = sizeof my_array / sizeof *my_array };

This will be evaluated at compile time and not create an object at run
time.

Also note that in general, the expression for the number of elements
should make use of the explicit conversion of an array name to a
pointer, and using that to access the type of the members:

elements = sizeof array_name / sizeof *array_name;

If you ever decide that you need to change the type, say to int or
long, you don't have to hunt down and make sure you change all the
sizeof expressions.

--
Jack Klein
Home: http://JK-Technology.Com
FAQs for
comp.lang.c http://c-faq.com/
comp.lang.c++ http://www.parashift.com/c++-faq-lite/
alt.comp.lang.learn.c-c++
http://www.club.cc.cmu.edu/~ajo/docs/FAQ-acllc.html
 
I

Ian Collins

Jack said:
Despite what others have said, the C language standard does not
require a compiler to evaluate constant expressions at compile time
unless it needs to use the value at compile time, as in Ian's example
of using it to define the size of an array.
Thanks for the clarification Jack, I wasn't aware of that "unless".
 
P

pete

skibud2 said:
Consider this example:

u8 my_array[10];

for (i = 0; i < (sizeof(my_array)/sizeof(u8)); i++) {
...
}

In the standard C specification, is the evaluation of
'(sizeof(my_array)/sizeof(u8))' to 10 guarenteed to happen at compile
time? Or is it a compiler optimization? From what I have read, it
looks like it is a compiler optimization, but I would like to get
confirmation.

Now if is not guarenteed to happen at compile time, is it better to do
something like the following:

const u8 my_array_size = (sizeof(my_array)/sizeof(u8));

to guarentee that you are not doing a divide a run time? On the
systems I have worked on, divides take a very long time. However,
almost every compiler that I have dealt with does the evaluation at
compile time.

You currently engaged in premature microoptimization.
The compiler may evaluate the constant expression
either at compile time or during run time.
Constant expressions of integer type
are commonly evaluated at compile time.
Either of the two ways of coding that you've shown
could be slower or faster.

If (sizeof array / sizeof array[0]) is evaluated at compile time,
then there's a chance that comparing
i < compile_time_constant
might be faster than
i < run_time_variable
 
C

christian.bau

If (sizeof array / sizeof array[0]) is evaluated at compile time,
then there's a chance that comparing
i < compile_time_constant
might be faster than
i < run_time_variable

On the other hand, a constant might also be slower than a runtime
variable. (And I do have real examples for this, where a construct
like

static int s_one = 1;
int one = s_one;

and using "one" instead of "1" with a particular compiler led to
significantly faster code. )
 
K

kar1107

If (sizeof array / sizeof array[0]) is evaluated at compile time,
then there's a chance that comparing
i < compile_time_constant
might be faster than
i < run_time_variable

On the other hand, a constant might also be slower than a runtime
variable. (And I do have real examples for this, where a construct
like

static int s_one = 1;
int one = s_one;

and using "one" instead of "1" with a particular compiler led to
significantly faster code. )

<OT>

That really surprises me. And 'significantly' faster? that's puzzling.
Mostly small integer constants (that can fit in, say 16 bits) are
directly
encoded in the same machine instruction. Thus no load/store for the
operand.
So I'm wondering why any compiler would ever do things slowly on using
a literal '1'
than using thru' a variable (named like one above).

</OT>

Karthik
 
K

Keith Thompson

If (sizeof array / sizeof array[0]) is evaluated at compile time,
then there's a chance that comparing
i < compile_time_constant
might be faster than
i < run_time_variable

On the other hand, a constant might also be slower than a runtime
variable. (And I do have real examples for this, where a construct
like

static int s_one = 1;
int one = s_one;

and using "one" instead of "1" with a particular compiler led to
significantly faster code. )

<OT>
That really surprises me. And 'significantly' faster? that's puzzling.
Mostly small integer constants (that can fit in, say 16 bits) are
directly
encoded in the same machine instruction. Thus no load/store for the
operand.
So I'm wondering why any compiler would ever do things slowly on using
a literal '1'
than using thru' a variable (named like one above).

Machine instructions still have to be loaded from memory; "one" could
have been stored in a register. (That's pure speculation, off the top
of my head.)
 
C

christian.bau

That really surprises me. And 'significantly' faster? that's puzzling.
Mostly small integer constants (that can fit in, say 16 bits) are
directly
encoded in the same machine instruction. Thus no load/store for the
operand.
So I'm wondering why any compiler would ever do things slowly on using
a literal '1'
than using thru' a variable (named like one above).

The PowerPC has a single instruction that performs that operation a =
b << c; if a, b and c are all in registers. There is _no_ single
instruction to calculate a = 1 << c: Two instructions are needed, one
to load the constant 1 into a register, and one to shift. Now if you
have an operation that calculates 1 << n in the critical path of a
very short loop with gazillions of iterations then this can make a
significant difference. This happened to me on a compiler that was
actually quite good at optimisations, except that it didn't recognise
that a constant can actually be a loop-invariant that should be
removed from a loop!

Similar things can happen with floating-point constants used in a
loop; a compiler should hold them in a register but often doesn't
whereas complex operations could be removed as loop-invariants from a
loop, with the result being kept in a register.
 
P

Peter Nilsson

Vacuously true.
Thanks for the clarification Jack, I wasn't aware of that "unless".

What "unless"?

The standard simply says (Epmhasis mine): "A constant expression _can_
be evaluated during _translation_ rather than runtime..." There is no
"must"
or "shall" precluding runtime evaluation of all constant expressions.

Expectations of QoI is the only guide. But when it comes to constant
expressions, expectations are pretty high these days. :)
 
I

Ian Collins

Peter said:
Vacuously true.
Vacuous?


What "unless"?

The standard simply says (Epmhasis mine): "A constant expression _can_
be evaluated during _translation_ rather than runtime..." There is no
"must"
or "shall" precluding runtime evaluation of all constant expressions.

Expectations of QoI is the only guide. But when it comes to constant
expressions, expectations are pretty high these days. :)
I have never come across a compiler that does not do this, so naturally
I assumed it was standard behaviour.
Your sig is missing the space after the --
 
P

Peter Nilsson

Ian Collins said:

Yes, the notion of compile and link time are not mentioned. The word
"compiler"
only appears in a non-normative footnote of N1124. Obviously there is
an intuitive
mapping between the translation phases...

Preprocessing - TP1..4
Compilation - TP5..7
Linking - TP8

That said, there is often a degree of overlap on actual
implementations,
particularly between compilation and preprocessing, especially with
pragma
extensions.
I have never come across a compiler that does not do this,

Nor are you likely to. Conditional preprocessing often involves
constant
expressions, so you're unlikely to find an implementation that can't
work
them out as it goes.
so naturally I assumed it was standard behaviour.

Whilst highly pragmatic and practical in nature, the language standard
is nonetheless an abstraction.
Your sig is missing the space after the --

As is...

Of course, I believe that you typed/inserted it.

Either google or IE managed to delete it. AFAIK, I'm not in a position
to
rectify this. If you're going to tell me how annoying it is for you,
then
realise that as a very reluctant google groups user I probably have to
do
things ten times more annoying than manually trimming signatures.
 
I

Ian Collins

Peter said:
Of course, I believe that you typed/inserted it.

Either google or IE managed to delete it. AFAIK, I'm not in a position
to
rectify this. If you're going to tell me how annoying it is for you,
then
realise that as a very reluctant google groups user I probably have to
do
things ten times more annoying than manually trimming signatures.
Like reading badly wrapped lines :)
 
D

David Thompson

Vacuously true.


What "unless"?

The standard simply says (Epmhasis mine): "A constant expression _can_
be evaluated during _translation_ rather than runtime..." There is no
"must"
or "shall" precluding runtime evaluation of all constant expressions.

Expectations of QoI is the only guide. But when it comes to constant
expressions, expectations are pretty high these days. :)

True. But for some _integer_ constant expressions:

- the ICE specifying bitfield width in a struct must not be greater
than the 'normal' width (before C99, of int) nor less than zero and
if zero must not be named; this is a constraint and so any violation
of it must be diagnosed. It's hard to do that without evaluating the
constant expression at compile time, unless you weasel out with a
blanket diagnostic like "this program may contain errors".

- the ICE specifying the value of an enumeratee must be in range of
int, as a constraint. This might not be too hard to defer, except that
the enumeratee can in turn be used in other ICEs which may need to be
evaluated.

- if the (integer) expression for array bound in a declarator is
constant it must be positive as a constraint; plus whether two array
types are compatible depends on whether their bounds are ICEs with the
same value, and compatibility is in some constraints.

- (ICEs for) case labels in a switch must be distinct, a constraint.

And as already noted, (integer) constant expressions in #if or #elif
must be evaluated at preprocessing time, at least in principle BEFORE
compile time, because
#if 0
#include "nonexistentfile"
#endif
int main (void) { return 0; }
must succeed (or at least not fail because of that construct).

OTOH it may actually make sense to defer floating-point constant
expressions in cases of cross compilation where the target floating
point arithmetic is different from the host and not worth the trouble
of simulating or (God forbid) not documented well enough to simulate.

- formerly david.thompson1 || achar(64) || worldnet.att.net
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,733
Messages
2,569,439
Members
44,829
Latest member
PIXThurman

Latest Threads

Top