C 为什么不';指向VLA函数参数的t指针会自动推断其大小,目前是否有良好的使用方法?

C 为什么不';指向VLA函数参数的t指针会自动推断其大小,目前是否有良好的使用方法?,c,language-lawyer,c11,C,Language Lawyer,C11,据我所知,每个VLA都有一个大小的隐藏变量,可以通过sizeof运算符“获取”该值。这里我没有得到的是指向函数参数中使用的VLA的指针——为什么它们的大小不能自动推导并存储在这个隐藏变量中——为什么我们应该显式地提供它。在这种情况下,既然我们已经有了“指向未知大小数组的指针”类型,为什么还要使用它呢 我的意思是: void func(size_t, int (*)[*]); //function accepting pointer to VLA void func_1(size_t, int

据我所知,每个VLA都有一个大小的隐藏变量,可以通过
sizeof
运算符“获取”该值。这里我没有得到的是指向函数参数中使用的VLA的指针——为什么它们的大小不能自动推导并存储在这个隐藏变量中——为什么我们应该显式地提供它。在这种情况下,既然我们已经有了“指向未知大小数组的指针”类型,为什么还要使用它呢

我的意思是:

void func(size_t, int (*)[*]); //function accepting pointer to VLA

void func_1(size_t, int (*)[]); //function accepting pointer to array of unknown bound

void func(size_t sz, int (*parr)[sz]) //implementation of 'func'
{
    printf("%lu", sizeof(*parr) / sizeof(int));

    printf("%lu", sz);
}

void func_1(size_t sz, int(*parr)[]) //implementation of 'func_1'
{
    //printf("%lu", sizeof(*parr) / sizeof(int)); //error: invalid application of 'sizeof' to an incomplete type 'int []'

    printf("%lu", sz);
}
如我所见,使用'func'而不是'func_1'的唯一好处是'sizeof'运算符将返回初始值'sz'的副本

上述功能的示例用法:

int main()
{
    size_t sz = 3;

    int arr[sz];

    func(sizeof(arr) / sizeof(int), &arr);

    func_1(sizeof(arr) / sizeof(int), &arr);

    return 0;
}
为什么不能隐式指定指向VLA参数的指针的大小?这至少可以很好地利用语法:

void func(int (*parr)[*]) // size copied from function argument
{
    printf("%lu", sizeof(*parr) / sizeof(int));

    printf("%lu", sz);
}
然后像这样调用函数:

int main()
{
    size_t sz = 3;

    int arr[sz];

    func(&arr);

    return 0;
}
将导致值为“3”的数组隐藏大小变量作为隐藏参数传递给“func”,创建类似于使用当前语法实例化先前的“func”并使用“sizeof”运算符传递数组的代码


如果您对将建议的语法编译成任何叮当作响的编译器有足够的兴趣,您将得到一个复活节彩蛋(;。

可变长度数组的长度只在本地知道(必须是这样,以便堆栈分配存储并提供
sizeof
语义)。当它传递给函数时,它只是一个常规数组类型,因此必须显式传递大小。

在一个令人讨厌的乱七八糟的问题中,几十年前就应该被废除,但现在有太多代码需要依赖,C编译器认为数组类型的所有函数参数都等同于poin的参数无论是否指定了数组大小,都要指定元素类型

定义一种语法可能不会有任何主要的技术困难,通过这种语法,函数可以传递任意大小的数组,并自动知道它们有多大。不幸的是:

  • 目前,将数组传递给函数意味着将指针传递给第一个元素;如果添加一个新语法,它意味着其他东西,则会产生比它解决的更大的混乱

  • 如果现有代码接收一个指针和一个整数,并将该指针视为标识指定长度数组的第一个元素,那么将该“数组”传递给需要“新样式”数组的方法可能会很棘手


  • 第二个问题可以通过添加一种语法来解决,该语法将指针/长度组合转换为“已知大小的数组”,该数组可以传递给接受此类内容的方法。然而,前一个问题可能无法解决,除非或直到C被一种不关注指针“优化”的语言取代甚至不再相关。

    可变长度数组(VLA)的大小与类型相关,而与对象无关

    例如,假设:

    size_t n = 41;
    int vla[n+1];
    printf("sizeof vla = %zu\n", sizeof vla);
    
    编译器创建一个初始化为
    n+1
    的匿名对象,我称之为
    \u anon
    ,并定义一个类型
    int[\u anon]
    ,它是数组对象
    vla
    的类型(请注意,以后更改
    n
    的值不会更改
    \u anon
    的值或vla类型的大小)

    你也可以写:

    size_t n = 41;
    typedef int vla_t[n+1];
    vla_t vla;
    
    sizeof
    应用于表达式(在本例中,该表达式恰好是一个对象名)会生成该表达式类型的大小


    func
    内部,
    vla
    的类型不可见,因此无法获取其大小。
    parr
    是指向未指定大小的
    int
    数组的指针。类型
    int[]
    (未指定大小的数组)和
    int[n+1]
    (长度为
    n+1
    的可变长度数组)`兼容,但不是同一类型。

    没有任何“隐藏变量”。
    sizeof
    在编译时被计算。请参见抱歉,但这种情况不同。这不是普通类型,而是VA类型。编辑后我会再说一遍。调用
    func(&arr);
    将不知道有多少数组元素,甚至不知道它是数组还是单个值。没有隐藏的大小,它只是一个指针。但是'sizeof(arr)是的。在“VA”的情况下,大部分实现都是通过隐藏变量完成的。我感觉你不知道类型系统是什么。我现在就停止评论,希望能遇到一些受过更好教育的C程序员(他们读过ISO标准-best).那么VLA参数类型的建议是什么呢?就目前而言,我看不出它们有什么好的用途。它非常有限……它允许函数声明对其VLA类型的长度避而不谈,同时允许定义根据实际参数指定它。它本身不会导致传递大小。因此基本上它只允许n某个值的隐式副本,稍后可由sizeof运算符接收。这是我的看法。是的。我的理解是,该功能的设计旨在避免以任何方式使ABI复杂化。从调用约定的角度来看,VLA参数是非实体的。唯一需要的支持是其函数的代码生成elf@Sneftel我不太明白你的意思?更重要的是,为什么我们要使用指向VLA的指针而不是指向未知大小数组的指针?@FISOCPP:int*[]类型的参数相当于类型
    int**
    ;在
    *
    周围添加括号不会改变这一点。虽然可以声明VLA变量,理论上也可以声明VLA参数,但没有VLA类型,也没有指向VLA的指针(与指向其第一个元素的指针不同)不,是的。来吧-ISO书呆子-你在哪里?给定
    int n=42;
    int[n]
    是VLA类型,而
    int(*)[n]
    是指针-