Стандарт C имеет этот язык:
6.5.3.4 Операторы sizeof и _Alignof
Семантика
- Оператор
sizeof
возвращает размер (в байтах) своего операнда, который может быть выражением или именем типа в скобках. Размер определяется типом операнда. Результат — целое число. Если тип операнда является типом массива переменной длины, вычисляется операнд; в противном случае операнд не оценивается и результатом является целочисленная константа.
Мне неясно, что Стандарт подразумевает под Если тип операнда является типом массива переменной длины, операнд оценивается
- Если тип операнда представляет собой тип массива переменной длины, кажется, что нет никакой цели для оценки аргумента, поскольку размер может быть определен из определения типа, как это предусмотрено в 6.7.6.2 Массив. деклараторы, что Размер каждого экземпляра типа массива переменной длины не меняется в течение его жизни.
- С другой стороны, если операнд представляет собой имя в скобках типа массива переменной длины, например, в
sizeof(char[foo()])
, выражение размера должно оцениваться во время выполнения для вычисления размера, но язык Стандарта, похоже, не охватывает этот случай ( что такое тип имени типа?)
Следует ли изменить язык стандарта C для уточнения?
Вот тестовая программа, иллюстрирующая поведение в некоторых конкретных случаях VLA:
#include <stdio.h>
static int N = 0;
int foo(void) { return ++N; }
int main() {
typedef char S[foo()]; // foo() is called
printf("typedef char S[foo()];\t"); printf("N=%d\n", N);
printf("sizeof(S)=%d\t\t", (int)sizeof(S)); printf("N=%d\n", N);
typedef char U[foo()]; // foo() is called
printf("typedef char U[foo()];\t"); printf("N=%d\n", N);
printf("sizeof(U)=%d\t\t", (int)sizeof(U)); printf("N=%d\n", N);
S s1;
printf("S s1;\t\t\t"); printf("N=%d\n", N);
printf("sizeof(s1)=%d\t\t", (int)sizeof(s1)); printf("N=%d\n", N);
S s2;
printf("S s2;\t\t\t"); printf("N=%d\n", N);
printf("sizeof(s2)=%d\t\t", (int)sizeof(s2)); printf("N=%d\n", N);
U u1;
printf("U u1;\t\t\t"); printf("N=%d\n", N);
printf("sizeof(u1)=%d\t\t", (int)sizeof(u1)); printf("N=%d\n", N);
U *pu1 = &u1;
printf("U *pu1 = &u1;\t\t"); printf("N=%d\n", N);
printf("sizeof(*pu1)=%d\t\t", (int)sizeof(*pu1)); printf("N=%d\n", N);
U *pu2 = NULL;
printf("U *pu2 = NULL;\t\t"); printf("N=%d\n", N);
// sizeof(*pu2) does not evaluate *pu2, contrary to the Standard specification
printf("sizeof(*pu2)=%d\t\t", (int)sizeof(*pu2)); printf("N=%d\n", N);
char x2[foo()][foo()]; // foo() is called twice
printf("char x2[foo()][foo()];\t"); printf("N=%d\n", N);
printf("sizeof(x2)=%d\t\t", (int)sizeof(x2)); printf("N=%d\n", N);
printf("sizeof(x2[0])=%d\t\t", (int)sizeof(x2[0])); printf("N=%d\n", N);
// sizeof(char[foo()]) evaluates foo()
printf("sizeof(char[foo()])=%d\t", (int)sizeof(char[foo()])); printf("N=%d\n", N);
return 0;
}
Вывод (как clang, так и gcc):
typedef char S[foo()]; N=1
sizeof(S)=1 N=1
typedef char U[foo()]; N=2
sizeof(U)=2 N=2
S s1; N=2
sizeof(s1)=1 N=2
S s2; N=2
sizeof(s2)=1 N=2
U u1; N=2
sizeof(u1)=2 N=2
U *pu1 = &u1; N=2
sizeof(*pu1)=2 N=2
U *pu2 = NULL; N=2
sizeof(*pu2)=2 N=2
char x2[foo()][foo()]; N=4
sizeof(x2)=12 N=4
sizeof(x2[0])=4 N=4
sizeof(char[foo()])=5 N=5
sizeof(*pu2) does not evaluate *pu2, contrary to the Standard specification
Вы имеете в виду, что если будет оцениватьсяsizeof(*pu2)
, то вы ожидаете, что будет вызванfoo()
? - person KamilCuk   schedule 21.07.2020int i = 0; char a[rand()%2 + 1]; printf("%zu\n", sizeof a[i++]); printf("%d\n", i);
шагi
.int i = 0; char a[42]; printf("%zu\n", sizeof a[i++]); printf("%d\n", i);
не увеличиваетi
, посколькуi
в аргументеsizeof
не оценивается. @chqrlie Это близко к тому, что вы ищете? - person chux - Reinstate Monica   schedule 21.07.2020sizeof(*pu2)
не оценивает*pu2
, что противоречит стандартной спецификации. Возможно, он оценивается, и вы получаете неопределенное поведение, которое выглядит так, как будто оно не оценивается. - person Language Lawyer   schedule 21.07.2020pu2
явно инициализируется какNULL
, оценка*pu2
должна иметь неопределенное поведение, которое действительно может остаться незамеченным, например, если код пропущен, что вполне вероятно, поскольку размер, если типU
, можно определить, даже не глядя наpu2
. - person chqrlie   schedule 22.07.2020foo()
будет оцениваться, но разыменование нулевого указателя должно иметь видимые побочные эффекты, хотя стандарт этого не требует. На самом деле, даже еслиpu2
имеет квалификациюvolatile
, указатель не разыменовывается clang. - person chqrlie   schedule 22.07.2020i++
не требуется для определения типаsizeof a[i++]
, и на самом деле типa[i++]
не является типом массива переменной длины, поэтому оценка не должна выполняться. . Также обратите внимание на 6.7.6.2 Деклараторы массивов p5 Если выражение размера является частью операнда оператораsizeof
и изменение значения выражения размера не повлияет на результат оператора, оно не указано, оценивается ли выражение размера.a
должно быть определено какchar a[10][rand() % 2 + 1];
дляsizeof a[i++]
для увеличенияi
. - person chqrlie   schedule 22.07.2020*pu2
явно оценивается в выражении*pu2 = 42;
, но поскольку это левый операнд оператора присваивания, он не подвергается преобразованию lvalue (раздел 6.3.2.1/p2). Это же предложение также исключает операнд оператора sizeof из преобразования lvalue, поэтому нет оснований полагать, чтоsizeof *pu2
будет разыменовывать NULL больше, чем*pu2 = 42
. - person rici   schedule 22.07.2020char x2[foo()][foo()];
неясно, есть ли у васchar x2[3][4]
илиchar x2[4][3]
. Порядок оценки, вероятно, зависит от компилятора. Вы должны напечататьsizeof(x2[0])
, чтобы узнать, что применимо. - person Jonathan Leffler   schedule 22.07.2020sizeof(x2[0])=4
, но gcc в linux печатаетsizeof(x2[0])=3
. Я не нашел ничего в Стандарте, чтобы снять эту двусмысленность. - person chqrlie   schedule 22.07.2020