【发布时间】:2020-11-11 05:33:23
【问题描述】:
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()被调用? -
我喜欢使用
int 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,因为sizeof的参数中的i未评估。 @chqrlie 这是否接近您所寻求的? -
sizeof(*pu2)不评估*pu2,这与标准规范相反 也许它已被评估并且您得到未定义的行为,看起来它没有被评估。 -
@LanguageLawyer:鉴于
pu2被显式初始化为NULL,评估*pu2应该有未定义的行为,这确实可能会被忽视,例如如果代码被省略,这是相当可能是因为U类型的大小可以在不查看pu2的情况下确定。 -
@KamilCuk:我不希望评估
foo(),但取消引用空指针应该有可见的副作用,尽管标准没有要求。事实上,即使pu2是volatile限定的,指针也不会被clang 解引用。
标签: c language-lawyer c99 variable-length-array expression-evaluation