Kenneth Brody said:
Keith Thompson wrote:
[...]
Determining the size of the object foo doesn't require evaluating
'foo' any more than determining the size of n requires evaluating it
to determine that its current value is 10. For a non-VLA type, the
size is known at compilation time. For a VLA type, the size is
associated with the type, not with some object of the type.
For example, given:
int n = 10;
typedef int vla[n];
vla foo, bar;
the size of the type "vla" will, in any sane implementation, be stored
once (probably in some anonymous object known to the compiler).
[...]
Consider:
size_t foo(int n)
{
int bar[n];
return sizeof bar;
}
Doesn't "bar" need to be evaluated, at least to the point of finding
out where its size has been stored?
Evaluating 'bar' means fetching the values of its elements; since they
haven't been initialized, doing so would invoke undefined behavior.
What needs to be "evaluated" is the *type* of bar -- just as it would
be if bar were an ordinary array. (In the latter case the evaluation
is trivial, since the size is constant.)
(I don't use VLAs, as many [most?] of the compilers I use don't
support it, so this is mostly a thought experiment to me. But it
does make me wonder.)
What about:
#include <stdio.h>
int main(void)
{
int n=10;
int x=1,y=1;
char vla[n][n];
char non_vla[10][10];
size_t foo = sizeof vla[x++];
size_t bar = sizeof non_vla[y++];
printf("x is now %d\n",x);
printf("y is now %d\n",y);
return 0;
}
Since "vla[x++]" is a VLA, and therefore must be evaluated, does this
mean that side-effects are done as well? Will x be 2?
According to the current wording of the standard, x++ must be
evaluated, but y++ must not be evaluated.
But also, consider this:
#include <stdio.h>
int main(void)
{
int n = 10;
int x = 1, y = 1;
char vla[10][n];
char non_vla[n][10];
size_t foo = sizeof vla[x++];
size_t bar = sizeof non_vla[y++];
printf("x is now %d\n", x);
printf("y is now %d\n", y);
return 0;
}
The standard, if taken literally, requires x to be incremented, but
not y, since 'vla' is a VLA, but 'non_vla' is not a VLA (though it's a
fixed-size array of VLAs).
And here's an even more amusing example:
#include <stdio.h>
int func(void)
{
puts("In func, returning 42");
return 42;
}
int main(void)
{
size_t size = sizeof(int[func()][10]);
printf("size = %d\n", (int)size);
return 0;
}
Since the argument to sizeof is a non-VLA type, it must not be
"evaluated" -- but it *must* be "evaluated" in order to determine the
size.
The fix is (a) to refer to "variably-modified" types rather than just
to VLAs, (b) to "evaluate" the argument of sizeof only if it's a
variably-modified type name, not if it's an expression, and (c) to
define what it means to "evaluate" a type name (namely, to evaluate
any expressions appearing within it).
As a programmer, it's easy to avoid this problem. Most 'sizeof'
expressions should apply to expressions anyway (usually to lvalues).
If you really need to apply 'sizeof' to a VLA type, keep it simple;
either define the type using expressions with no side effects, or use
a typedef so the side effects occur only when the type is first
declared.