PC *_*ite 17 c sizeof variable-length-array
这个答案的评论部分中的一个论点促使我提出这个问题.
在以下代码中,bar指向可变长度数组,因此sizeof在运行时而不是编译时确定.
int foo = 100;
double (*bar)[foo];
Run Code Online (Sandbox Code Playgroud)
该论点是关于sizeof当操作数是可变长度数组时是否使用计算其操作数,在未初始化sizeof(*bar)时产生未定义的行为bar.
是否使用未定义的行为,sizeof(*bar)因为我正在取消引用未初始化的指针?sizeof当类型是可变长度数组时,是实际评估的操作数,还是仅确定其类型(sizeof通常如何工作)?
编辑:每个人似乎都引用了C11选秀中的这段话.有谁知道这是否是官方标准中的措辞?
Kei*_*son 11
其他两个答案已经引用了N1570 6.5.3.4p2:
的
sizeof操作者产生其操作数的大小(以字节为单位),其可以是表达或类型的括号名称.大小由操作数的类型确定.结果是整数.如果操作数的类型是可变长度数组类型,则计算操作数; 否则,不评估操作数,结果是整数常量.
根据标准中的该段,是的,sizeof评估操作数.
我要说这是标准中的缺陷; 在运行时评估某些东西,但操作数不是.
让我们考虑一个更简单的例子:
int len = 100;
double vla[len];
printf("sizeof vla = %zu\n", sizeof vla);
Run Code Online (Sandbox Code Playgroud)
根据标准,sizeof vla评估表达式vla.但是,这是什么意思?
在大多数情况下,评估数组表达式会产生初始元素的地址 - 但sizeof运算符是一个明确的例外.我们可以假设评估vla意味着访问其元素的值,这些元素具有未定义的行为,因为这些元素尚未初始化.但是没有其他上下文对数组表达式的求值访问其元素的值,在这种情况下绝对不需要这样做.(更正:如果使用字符串文字初始化数组对象,则会评估元素的值.)
当声明vla被执行时,编译器将创建一些匿名元数据来保存数组的长度(它必须,因为为lenafter vla定义一个新值并且分配后不会改变长度vla).所有必须要确定的sizeof vla是将该存储值乘以sizeof (double)(或者只是为了检索存储的值,如果它以字节为单位存储大小).
sizeof 也可以应用于带括号的类型名称:
int len = 100;
printf("sizeof (double[len]) = %zu\n", sizeof (double[len]));
Run Code Online (Sandbox Code Playgroud)
根据标准,sizeof表达式评估类型.那是什么意思?显然,它必须评估当前的价值len.另一个例子:
size_t func(void);
printf("sizeof (double[func()]) = %zu\n", sizeof (double[func()]));
Run Code Online (Sandbox Code Playgroud)
这里的类型名称包括函数调用.评估sizeof表达式必须调用该函数.
但是在所有这些情况下,没有实际需要评估数组对象的元素(如果有的话),并且没有必要这样做.
sizeof应用于VLA以外的任何其他任何东西都可以在编译时进行评估.sizeof应用于VLA(对象或类型)的差异在于必须在运行时评估某些内容.但必须评估的事情不是操作数sizeof; 它只是确定操作数大小所需要的,而不是操作数本身.
标准表示sizeof如果该操作数是可变长度数组类型,则计算操作数.这是标准的缺陷.
回到问题中的示例:
int foo = 100;
double (*bar)[foo] = NULL;
printf("sizeof *bar = %zu\n", sizeof *bar);
Run Code Online (Sandbox Code Playgroud)
我添加了一个初始化,NULL以使解除引用bar具有未定义的行为更加清晰.
*bar是类型double[foo],是VLA类型.原则上,*bar对其进行评估,因为bar未初始化将导致未定义的行为.但同样,没有必要取消引用bar.编译器在处理类型时会生成一些代码double[foo],包括将foo(或foo * sizeof (double))的值保存在匿名变量中.要评估的所有工作sizeof *bar就是检索该匿名变量的值.如果标准被更新以定义sizeof 一致的语义,那么很明显,评估sizeof *bar是明确定义的,并且产生100 * sizeof (double) 而不必取消引用bar.
M.M*_*M.M 10
是的,这会导致未定义的行为.
在N1570 6.5.3.4/2中我们有:
sizeof运算符产生其操作数的大小(以字节为单位),该操作数可以是表达式或类型的带括号的名称.大小由操作数的类型确定.结果是整数.如果操作数的类型是可变长度数组类型,则计算操作数 ; 否则,不评估操作数,结果是整数常量.
现在我们有了一个问题:是*bar可变长度数组类型的类型吗?
由于bar声明为VLA的指针,因此取消引用它应该产生VLA.(但我没有看到具体的文字说明是否这样做).
注意:可以在这里进行进一步的讨论,也许可以认为它的*bar类型double[100]不是VLA.
假设我们同意类型*bar实际上是VLA类型,那么在sizeof *bar表达式*bar中进行评估.
bar在这一点上是不确定的.现在看6.3.2.1/1:
如果左值在评估时未指定对象,则行为未定义
由于bar未指向对象(由于不确定),因此评估*bar会导致未定义的行为.
| 归档时间: |
|
| 查看次数: |
1128 次 |
| 最近记录: |