typedef (pointer to) VLA是否需要评估大小表达式?

3

typedef 可变长度数组需要评估大小表达式吗?

int f(void);
int main(void)
{
    typedef int (T) [ f() ];       // is f required to be evaluated ?
    T x;
    return sizeof x;
}

typedef指向VLA的指针是否需要评估大小表达式?

int f(void);
int main(void)
{
    typedef int (*T) [ f() ];      // is f is required to be evaluated ?
    T x;
    return sizeof x;
}

更新. 在f的定义中可见,那么对它的调用可以被优化掉:

int f(void)
{
    return 4;
}

int main(void)
{
    typedef int (*T) [ f() ];
    return sizeof(T);
}

生成的代码(包括GCC和LLVM):

main:
        mov     eax, 8
        ret

这是可以预期的,因为没有真正需要调用f来确定指针的大小。

1个回答

6
根据C标准(§6.7.8类型定义),在存储类别说明符为typedef的声明中,每个声明符都定义了一个标识符作为typedef名称,该名称表示以6.7.6中描述的方式指定的标识符类型。与变长数组声明符相关联的任何数组大小表达式都会在到达typedef名称的声明时按执行顺序进行求值。C标准中还有一个例子:如果typedef名称表示变长数组类型,则数组的长度在定义typedef名称时固定,而不是每次使用时。
void copyt(int n)
{
    typedef int B[n]; // B is n ints, n evaluated now
    n += 1;
    B a; // a is n ints, n without += 1
    int b[n]; // a and b are different sizes
    for (int i = 1; i < n; i++)
        a[i-1] = b[i];
}

这是一个演示程序。

#include <stdio.h>

int f( void )
{
    static int n;
    
    return ++n;
}

void g( void )
{
    typedef int ( *T )[ f() ];
    
    T p;
    
    printf( "sizeof( *p ) = %zu\n", sizeof( *p ) );
}

int main(void) 
{
    for ( size_t i = 0; i < 10; i++ )
    {
        g();
    }
}

程序输出为
sizeof( *p ) = 4
sizeof( *p ) = 8
sizeof( *p ) = 12
sizeof( *p ) = 16
sizeof( *p ) = 20
sizeof( *p ) = 24
sizeof( *p ) = 28
sizeof( *p ) = 32
sizeof( *p ) = 36
sizeof( *p ) = 40

有趣的是,根据这里的最佳答案,OP代码中(第一个片段)的sizeof x表达式是未定义行为。 - Adrian Mole
如果OP的f()的定义在一个单独的TU中,则无法在编译时评估其sizeof - Adrian Mole
@AdrianMole:VLA的大小在运行时而非编译时进行评估。 - Jonathan Leffler
@AdrianMole:你链接中提到的未定义行为是由于对未初始化指针进行解引用造成的(即使忽略了未初始化指针的值;重要的是类型)。这不适用于sizeof x(但适用于sizeof *p)。 - Chris Dodd
@JonathanLeffler 嗯。我猜我需要修改我的回复,关于 sizeof 运算符的工作原理。我一直认为VLA是“不完整类型”,但事实并非如此。 C11明确指出,VLA是一个完整的类型。 - Adrian Mole

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接