1

sizeof operand will evaluate the the operand if it is a variable-length array.

6.5.3.4, p2: If the type of the operand is a variable length array type, the operand is evaluated;

Yet this code is working, and I'm assuming it is defined:

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

struct test
{
    struct test* t;
    int i;  
};

int main(void) 
{
    int r = ( rand() % 100 ) + 1;
    assert( r > 0 );
    struct test* a[r];
    for( size_t i = 0; i < r; i++ )
    {
        a[i] = NULL;
    }

    printf("%zu\n" , sizeof( a[0]->i ) );
    //printf("%d\n", a[0]->i ); //this will of course crash the program

    return 0;
}
  1. Is the code defined?
  2. Is the sizeof operand evaluated?
  3. Shouldn't evaluation dereference the pointer?
  4. What is the difference between the first and second printf, given the context?

The program seems to be correct with any amount of additional deferences:

struct other
{
    int i;  
};

struct test
{
    struct other* t; 
};

int main(void) 
{
    int r = ( rand() % 100 ) + 1;
    assert( r > 0 );
    struct test* a[r];
    for( size_t i = 0; i < r; i++ )
    {
        a[i] = NULL;
    }

    printf("%zu\n" , sizeof( a[0]->t->i ) );
    //printf("%d\n", a[0]->t->i ); //this will of course crash the program

    return 0;
}
Yu Hao
  • 119,891
  • 44
  • 235
  • 294
this
  • 5,229
  • 1
  • 22
  • 51
  • But the operand type as shown is an `int`. No? – kaylum Aug 19 '15 at 06:48
  • 2
    I think what the statement means is that if you do `sizeof(a)` then `a[r]` is evaluated. That is, the actual value of `r` needs to be obtained. But I'm no language expert so please correct away if that's not what it means. – kaylum Aug 19 '15 at 06:50
  • possible duplicate of [Is dereferencing null pointer valid in sizeof operation](http://stackoverflow.com/questions/19785518/is-dereferencing-null-pointer-valid-in-sizeof-operation) – implmentor Aug 19 '15 at 06:58
  • @bleakgadfly Not a duplicate because it doesn't address VLAs directly. – this Aug 19 '15 at 07:02
  • Possible duplicate of [Behavior of sizeof on variable length arrays (C only)](https://stackoverflow.com/questions/14995870/behavior-of-sizeof-on-variable-length-arrays-c-only) – Evan Carroll Jun 26 '18 at 21:16

2 Answers2

3

a itself is a VLA. However, a[0]->i is not, its type is int.

So sizeof( a[0]->i ) is simply sizeof(int). sizeof here is a compile time operator, a[0]->i is not evaluated, the code is defined.

Yu Hao
  • 119,891
  • 44
  • 235
  • 294
2

The operand of

sizeof a[0]->i

is not a VLA and is thus not evaluated. Neither would be

sizeof a[0]

only constructs like

sizeof a

need to evaluate its argument at runtime, because the size of that animal is not known at compile time.

Edit: Such an evaluation can be erroneous, of course. If you have

double (*p)[n];

so a pointer to a VLA. Then

sizeof *p

is a run time expression and erroneous if you didn't initialize p properly.

Jens Gustedt
  • 76,821
  • 6
  • 102
  • 177
  • Do you think it is possible to cause ub, with sizeof and VLAs? – this Aug 19 '15 at 07:12
  • @this, you can't "cause" UB, undefined behavior is not behavior, but by definition the absence of it. If you are asking if the expression that is evaluated can be erroneous, yes, sure. Just take `p` to be a pointer to a VLA and do `sizeof *p`. If `p` is `0` this will crash your program. – Jens Gustedt Aug 19 '15 at 07:54
  • I already tried that several times but the program worked. Maybe I should as a new question. I hope you don't mind if I make a link to this answer? – this Aug 19 '15 at 08:01
  • I should have said "may crash your program". The problem with erroneous code that has no defined behavior is that is can do anything, even appear to work. You are on your own, then. – Jens Gustedt Aug 19 '15 at 08:06
  • I know that, I didn't say that the code is defined. I also know what ub is. Still it is strange, since clearly sizeof in that case is not evaluated the same way as a plain pointer dereference which will crash. So why the difference. – this Aug 19 '15 at 08:07
  • Because standard only distinguishes between expressions that are evaluated for their type at compile time and expressions that are evaluated for their value at run time. Since here the run time version is needed, you are in the second case and all requirements for the expression apply. So the standard doesn't define a behavior if `p` is `0` or is uninitialized. Now your compiler provider *may* define a behavior for that situation, because it may deduce the size from the definition of `p`. It could also try to deference `p` and look there. You'd have to check if they document such a thing. – Jens Gustedt Aug 19 '15 at 08:18
  • The Standard could have eliminated any requirement to have `sizeof` perform any action at run-time if it had forbid the *definition* of variable-size array types within a `sizeof` expression. Given `int x[sz];`, the expression `sizeof x` would not be a compile-time expression, but would yield the value of `sz` with which `x` was created (the compiler may or may not need to create a hidden variable for that, depending upon whether `sz` could be modified later). – supercat Aug 19 '15 at 18:35
  • @supercat, the C standard doesn't have the concept of "action". *yield the value `sz`* and *evaluate a hidden variable* is simply a run time evaluation. And as you see, things would quickly become lengthy to describe and tricky to implement. Usually the C committee feels that they shouldn't impose particular implementations and that there should be enough slackness for implementors to make things simple and efficient. – Jens Gustedt Aug 19 '15 at 19:29
  • @JensGustedt: The act of reading a hidden variable whose value cannot change within its lifetime cannot have side-effects, and thus a `sizeof` which does nothing except compute a linear function of such values would not have side-effects. As it is, given `int x[sz];`, evaluation of `sizeof x[f()]`; could evaluate `f` even though there would be no defined means by which `f` could do anything that would affect the value computed by `sizeof`. A rule which simply said that "sizeof" will never evaluate the expression would be simpler than one which allows the expression to be evaluated with VLAs. – supercat Aug 19 '15 at 19:36
  • @supercat, *`sizeof` will never evaluate the expression* here is simply not possible within the existing terminology. I think what you mean is *the evaluation of an expression in `sizeof` should never have side effects*. This sounds completely reasonable, but is more a rule of thumb that everybody should follow, anyhow, not only in `sizeof`. Anybody who uses `++` inside complicated expressions (definition of a type!) should be made responsible for his acts. – Jens Gustedt Aug 19 '15 at 19:42