评估 sizeof(type_name) 中的 type_name 的确切条件是什么? GCC 在 sizeof(int [(f(), 100)]) 中计算 f()

问题描述 投票:0回答:1

背景

标准说(C17,6.5.3.4¶2):

sizeof
运算符产生其操作数的大小(以字节为单位),该操作数可以是表达式或带括号的类型名称。大小由操作数的类型确定。结果是一个整数。如果操作数的类型是变长数组类型,则对操作数求值;否则,不计算操作数,结果是整数常量。

令人困惑的是,措辞并没有区分

sizeof
的两个语法上下文:

  • sizeof 
    一元表达式
  • sizeof(
    类型名称
    )

我相信 type-name 从技术上讲并不“具有”类型,而是“表示”(名称、指定)类型。另外,在类型名称作为参数的情况下,我会直观地用评估其中的所有赋值表达式来表达这一点——如果不是为了准确性,那么为了清晰起见。

无论如何,我理解这个措辞的意思是,在

sizeof(type)
的情况下,
type
中的所有表达式都将被精确计算,如果(ie: 当且仅当)
type
表示 VLA(可变长度)数组)类型。

问题

考虑到上述内容,以下代码的最后一个

printf
语句具有令人惊讶的结果:

#include <stdio.h>

void f(int i) {
    printf("side effect %d; ", i);
}

int main(void) {
    int n = 9;
    int a[n];
    printf("%zu\n", sizeof a);                   // 36
    printf("%zu\n", sizeof a[n++]);              // 4
    printf("%d\n", n);                           // 9
    printf("%zu\n", sizeof(int [n++]));          // 36
    printf("%d\n", n);                           // 10
    printf("%zu\n", sizeof(int [(f(0), 100)]));
      // side effect 0; 400 (type of operand: int, a non-VLA type)

    return 0;
}

int i
的参数
f
- 在问题标题中省略 - 用于调试目的和玩耍,如果有人想区分对该函数的不同调用。)

  • 简单明了:
    int [(f(0), 100)]
    int [100]
    类型,它不是数组类型。那么:为什么
    f(0)
    会被评估?
  • 更一般地说:计算
    type_name
    中的
    sizeof(type_name)
    (或其中的表达式)的确切条件是什么?

顺便说一句,

sizeof(int [f(0), 100])
(表示数组大小的逗号表达式周围没有括号)会导致错误,我在以下问题中讨论:如果作为数组大小的一部分,为什么必须将用作数组大小的逗号表达式括在括号中数组声明符?

其他参考资料

标准(C17 草案)中讨论数组声明符语法的相关位置包括:6.7.7 ¶1、6.7.6.2 ¶3.

Keith Thompson 对有关 VLA 的问题的

这个回答是相关的。但请注意,我的问题不是关于 VLA 本身(尽管它在上面的代码中使用它们用于对比目的)。

c language-lawyer sizeof variable-length-array compiler-bug
1个回答
0
投票

根据C17标准(6.6常量表达式):

2 常量表达式可以在翻译过程中进行计算,而不是 比运行时,因此可以在任何地方使用 可能是常数。

3 常量表达式不得包含赋值、自增、 递减、函数调用或逗号运算符,除非它们是 包含在未计算的子表达式中。1

就像这个表达一样

sizeof(int [(f(0), 100)])

带有逗号运算符的子表达式

(f(0), 100)
不是数组声明中的常量子表达式,则声明了一个可变长度数组,其大小在 runtome 中计算。

© www.soinside.com 2019 - 2024. All rights reserved.