VLA и побочный эффект в операнде sizeof

Я знаю, что sizeof никогда не оценивает свой операнд, за исключением конкретного случая, когда указанный операнд является VLA. Или я думал, что знаю.

void g(int n) {
    printf("g(%d)\n", n);
}

int main(void) {
    int i = 12;

    char arr[i]; // VLA

    (void)sizeof *(g(1), &arr); // Prints "g(1)"
    (void)sizeof (g(2), arr);   // Prints nothing

    return 0;
}

Что происходит?

На всякий случай, это скомпилировано с помощью GCC 5.1 на Coliru.


person Quentin    schedule 24.06.2015    source источник


Ответы (1)


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

Мое понимание того, как sizeof взаимодействует с VLA, на самом деле правильное, что подтверждает следующая цитата (спасибо @this!):

6.5.3.4 Операторы sizeof и _Alignof
Если тип операнда является типом массива переменной длины, операнд оценивается; в противном случае операнд не оценивается и результатом является целочисленная константа

Это не то, что вызывает это удивительное (для меня) поведение.

(void)sizeof (g(2), arr);

В подвыражении (g(2), arr) оператор запятой запускает распад arr от массива к указателю. Таким образом, операнд sizeof больше не является VLA, а является простым char*, и он возвращается к тому, чтобы не вычислять свой операнд.

Очевидно, это поведение было изменено в C++, где оператор запятой больше не разрушает массивы.

person Quentin    schedule 24.06.2015
comment
Вы можете добавить это где-нибудь: 6.5.3.4 Операторы sizeof и _Alignof Если тип операнда является типом массива переменной длины, операнд вычисляется; в противном случае операнд не оценивается, и результатом является целочисленная константа. так что читатели лихорадки будут сбиты с толку. - person this; 24.06.2015
comment
Однако, возможно, я что-то упускаю; sizeof() — это оператор времени компиляции, а не функция времени выполнения. поэтому (void)sizeof() не имеет смысла, поскольку sizeof() возвращает значение size_t (фактически длинное целое), а не указатель, а sizeof() ожидает только один параметр. - person user3629249; 26.06.2015
comment
@ user3629249 sizeof является оператором строго времени компиляции и не оценивает свой оперант, за исключением, когда его операндом является VLA, тогда он оценивает его во время выполнения. Приведение (void) не имеет ничего общего с указателями: оно явно отбрасывает только что вычисленное значение sizeof. На самом деле это не влияет на поведение программы, но это лучший стиль, чем просто выражение, которое вычисляется, а затем отбрасывается (для чего GCC выдает предупреждение). [продолжение] - person Quentin; 26.06.2015
comment
@user3629249 user3629249 Наконец, не позволяйте синтаксису обмануть вас: sizeof — это оператор, а не функция, и он не принимает круглые скобки, когда его операнд является выражением (он делает это для типа). Круглые скобки здесь должны отдавать предпочтение оператору запятой (который не является запятой в списке аргументов). sizeof по-прежнему принимает один операнд, который является результатом того, что указано в скобках. - person Quentin; 26.06.2015