是否[] []和(*a)[]等效为函数参数?

hac*_*cks 11 c arrays gcc function-parameter language-lawyer

功能原型

void foo(int n, int a[][]);
Run Code Online (Sandbox Code Playgroud)

给出了关于不完整类型的错误

void foo(int n, int (*a)[]);  
Run Code Online (Sandbox Code Playgroud)

编译.根据衰减规则int a[][]相当于int (*a)[]在这种情况下,因此也int (*a)[]应该给出关于不完整类型的错误,但GCC似乎接受它.有什么我想念的吗?这可能是一个GCC错误,但我没有发现任何与之相关的内容.

AnT*_*AnT 9

不,它们不等同于函数参数.他们不是在完全相同的方式在参数声明等价foobar

struct S;
void foo(struct S* s); // OK
void bar(struct S a[]); // ERROR: incomplete type is not allowed
Run Code Online (Sandbox Code Playgroud)

不等同.

C不允许将不完整类型作为数组元素(参见C 1999 6.7.5.2/1:"[...]元素类型不应是不完整或函数类型.[...]")此限制适用于数组参数声明的方式与应用于任何其他数组声明的方式相同.即使稍后将数组类型的参数隐式调整为指针类型,C也不会对函数参数列表中的数组声明提供特殊处理.换句话说,在上述调整之前检查数组参数声明的有效性.

int a[][]是同一件事:尝试声明一个包含类型元素的数组int [],这是一个不完整的类型.同时,int (*a)[]完全合法 - 指向不完整类型的指针没什么不寻常的.

作为旁注,C++"修复"了这个问题,允许在参数声明中使用不完整类型的数组.但是,原始的C++仍然禁止int a[][]参数,int (&a)[]参数甚至int (*a)[]参数.据推测,这可以在C++ 17中修复/允许(http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#393)

  • @cheroky:`int a [] []`将`a`声明为`int []`类型的元素数组.由于`int []`是一个不完整的类型,因此该声明无效."int"不完整的事实无论如何都无济于事. (2认同)
  • @haccks:嗯,我的答案的重点是C在检查数组声明*的正确性之前*它执行数组参数到指针参数的转换.检查失败了.出于同样的原因,参数声明中的`int a [-2]`也会失败,即使数组大小"并不重要",并且它"将无论如何都会衰减到`int*`".但语言的规则是这样写的.数组声明即使在参数列表中使用也应该是一个有效的数组声明,即使它稍后会衰减到指针. (2认同)

dbu*_*ush 6

在不需要知道大小的上下文中允许不完整类型.

有这个声明:

int a[][]
Run Code Online (Sandbox Code Playgroud)

它甚至作为函数参数也是无效的,因为需要一个数组维的大小来知道如何在第二维上执行指针运算.

但这有效:

int (*a)[];
Run Code Online (Sandbox Code Playgroud)

因为为了使用指向它的指针,不需要知道数组的大小.

C标准的 6.2.7节给出了这样一个声明的例子:

5示例给出以下两个文件范围声明:

int f(int (*)(), double (*)[3]);
int f(int (*)(char *), double (*)[]);
Run Code Online (Sandbox Code Playgroud)

由此产生的函数的复合类型是:

int f(int (*)(char *), double (*)[3]);
Run Code Online (Sandbox Code Playgroud)

此示例显示了与类型声明double (*)[3]兼容的类型声明double (*)[]

但是,由于缺少大小,您不能像2D数组那样直接使用它.以下是一些示例.如果您尝试这样做:

void foo(int n, int (*a)[])
{
    int i,j;
    for (i=0;i<n;i++) {
        for (j=0;j<n;j++) {
            printf("a[%d][%d]=%d\n",i,j,a[i][j]);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

编译器(如预期的那样)告诉你:

error: invalid use of array with unspecified bounds
         printf("a[%d][%d]=%d\n",i,j,a[i][j]);
         ^
Run Code Online (Sandbox Code Playgroud)

你可以通过利用这样一个事实来解决这个问题:即使是不确定的大小,在大多数情况下衰减到指针:

#include <stdio.h>

void foo(int n, int (*a)[])
{
    int i,j;
    for (i=0;i<n;i++) {
        // dereference "a", type is int[], which decays to int *
        // now manually add "n" ints times the row
        int *b = *a + (n*i);
        for (j=0;j<n;j++) {
            printf("a[%d][%d]=%d\n",i,j,b[j]);
        }
    }
}

int main()
{
    int a[2][2] = { {4,5},{6,7} };
    foo(2,a);

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

这使用以下输出编译清理:

a[0][0]=4
a[0][1]=5
a[1][0]=6
a[1][1]=7
Run Code Online (Sandbox Code Playgroud)

即使在函数之外,int (*)[]也可以使用语法:

#include <stdio.h>

int main()
{
    int a[2][2] = { {4,5},{6,7} };
    int i,j,n=2;
    int (*aa)[];
    // "a" decays from int[2][2] to int (*)[2], assigned to int (*)[]
    aa = a;
    for (i=0;i<n;i++) {
        int *b = *aa + (n*i);
        for (j=0;j<n;j++) {
            printf("a[%d][%d]=%d\n",i,j,b[j]);
        }
    }

    return 0;
}
Run Code Online (Sandbox Code Playgroud)