sizeof оценка массива переменной длины

sizeof операнд будет оценивать операнд, если это массив переменной длины.

6.5.3.4, p2: если тип операнда представляет собой тип массива переменной длины, операнд оценивается;

Тем не менее, этот код работает, и я предполагаю, что он определен:

#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. Код определен?
  2. Вычисляется ли операнд sizeof?
  3. Разве оценка не должна разыменовывать указатель?
  4. В чем разница между первым и вторым printf, учитывая контекст?

Программа кажется правильной при любом количестве дополнительных отступлений:

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;
}

person this    schedule 19.08.2015    source источник
comment
Но тип операнда, как показано, int. Нет?   -  person kaylum    schedule 19.08.2015
comment
Я думаю, что это утверждение означает, что если вы делаете sizeof(a), то оценивается a[r]. То есть нужно получить фактическое значение r. Но я не эксперт по языкам, поэтому, пожалуйста, поправьте, если это не то, что это означает.   -  person kaylum    schedule 19.08.2015
comment
возможный дубликат Действителен ли нулевой указатель разыменования в операции sizeof   -  person bleakgadfly    schedule 19.08.2015
comment
@bleakgadfly Не дубликат, потому что он не обращается к VLA напрямую.   -  person this    schedule 19.08.2015
comment
Возможный дубликат поведения sizeof для массивов переменной длины (только C)   -  person Evan Carroll    schedule 27.06.2018


Ответы (2)


a сам по себе является VLA. Однако a[0]->i не является, его тип int.

Так что sizeof( a[0]->i ) это просто sizeof(int). sizeof здесь оператор времени компиляции, a[0]->i не оценивается, код определен.

person Yu Hao    schedule 19.08.2015
comment
Как вы думаете, можно ли вызвать ub с помощью sizeof и VLA? - person this; 19.08.2015

Операнд

sizeof a[0]->i

не является VLA и поэтому не оценивается. Ни было бы

sizeof a[0]

только такие конструкции

sizeof a

необходимо оценить его аргумент во время выполнения, потому что размер этого животного неизвестен во время компиляции.

Изменить: Такая оценка, конечно, может быть ошибочной. Если у тебя есть

double (*p)[n];

поэтому указатель на VLA. потом

sizeof *p

является выражением времени выполнения и ошибочным, если вы не инициализировали p должным образом.

person Jens Gustedt    schedule 19.08.2015
comment
Как вы думаете, можно ли вызвать ub с помощью sizeof и VLA? - person this; 19.08.2015
comment
@this, вы не можете вызвать UB, неопределенное поведение - это не поведение, а его отсутствие по определению. Если вы спрашиваете, может ли оцениваемое выражение быть ошибочным, да, конечно. Просто возьмите p в качестве указателя на VLA и выполните sizeof *p. Если p равно 0, это приведет к сбою вашей программы. - person Jens Gustedt; 19.08.2015
comment
Я уже пробовал это несколько раз, но программа работала. Может быть, я должен как новый вопрос. Надеюсь, вы не возражаете, если я сделаю ссылку на этот ответ? - person this; 19.08.2015
comment
Я должен был сказать, что может привести к сбою вашей программы. Проблема с ошибочным кодом, у которого нет определенного поведения, заключается в том, что он может делать что угодно, даже казаться, что он работает. Тогда ты сам по себе. - person Jens Gustedt; 19.08.2015
comment
Я это знаю, я не говорил, что код определен. Я также знаю, что такое уб. Тем не менее это странно, поскольку очевидно, что sizeof в этом случае не оценивается так же, как простое разыменование указателя, которое приведет к сбою. Так почему же разница. - person this; 19.08.2015
comment
Потому что стандарт различает только выражения, которые оцениваются по их типу во время компиляции, и выражения, которые оцениваются по их значению во время выполнения. Поскольку здесь требуется версия времени выполнения, вы находитесь во втором случае, и все требования к выражению применяются. Таким образом, стандарт не определяет поведение, если p равно 0 или не инициализировано. Теперь ваш поставщик компилятора может определить поведение для этой ситуации, поскольку он может вывести размер из определения p. Он также может попытаться уважать p и посмотреть там. Вы должны проверить, документируют ли они такие вещи. - person Jens Gustedt; 19.08.2015
comment
Стандарт мог бы устранить любое требование, чтобы sizeof выполнял какое-либо действие во время выполнения, если бы он запрещал определение типов массивов переменного размера в выражении sizeof. Учитывая int x[sz];, выражение sizeof x не будет выражением времени компиляции, но даст значение sz, с которым было создано x (компилятору может потребоваться или не потребоваться создать для этого скрытую переменную, в зависимости от того, можно ли изменить sz позже). - person supercat; 19.08.2015
comment
@supercat, в стандарте C нет концепции действия. получить значение sz и вычислить скрытую переменную — это просто оценка во время выполнения. И, как вы видите, вещи быстро станут длинными для описания и сложными для реализации. Обычно комитет C считает, что они не должны навязывать определенные реализации и что разработчики должны иметь достаточную небрежность, чтобы сделать вещи простыми и эффективными. - person Jens Gustedt; 19.08.2015
comment
@JensGustedt: акт чтения скрытой переменной, значение которой не может измениться в течение ее жизни, не может иметь побочных эффектов, и, таким образом, sizeof, который ничего не делает, кроме вычисления линейной функции таких значений, не будет иметь побочных эффектов. Как есть, учитывая int x[sz];, оценку sizeof x[f()]; мог бы оценить f, даже если не было бы определенных средств, с помощью которых f мог бы сделать что-либо, что повлияло бы на значение, вычисленное sizeof. Правило, которое просто говорит, что sizeof никогда не будет вычислять выражение, будет проще, чем правило, позволяющее вычислять выражение с помощью VLA. - person supercat; 19.08.2015
comment
@supercat, sizeof никогда не будет оценивать выражение здесь просто невозможно в рамках существующей терминологии. Я думаю, что вы имеете в виду вычисление выражения в sizeof никогда не должно иметь побочных эффектов. Это звучит вполне разумно, но это скорее эмпирическое правило, которому все должны следовать, так или иначе, не только в sizeof. Любой, кто использует ++ внутри сложных выражений (определение типа!), должен нести ответственность за свои действия. - person Jens Gustedt; 19.08.2015