【问题标题】:Does "int (*)[]" decay into "int **" in a function parameter?函数参数中的“int (*)[]”是否衰减为“int **”?
【发布时间】:2015-03-08 20:06:21
【问题描述】:

我今天早些时候在programmers.stackexchange 上发布了这个question。我一直认为int (*)[] 确实不会在函数参数中衰减为int **,但我收到的多个回答表明它确实如此。

我在函数参数中大量使用了int (*)[],但现在我变得非常困惑。

当我使用gcc -std=c99 -pedantic -Wall编译这个函数时

void function(int (*a)[])
{
    sizeof(*a);
}

我收到此错误消息:

c99 -Wall -pedantic -c main.c -o main.o
main.c: In function ‘function’:
main.c:3:11: error: invalid application of ‘sizeof’ to incomplete type ‘int[]’ 
make: *** [main.o] Error 1

这表明*a 的类型为int [] 而不是int *

谁能解释一下int (*)[]之类的东西是否会在函数参数中衰减为int **,并给我一些参考(也许来自标准文档)来证明为什么会这样。

【问题讨论】:

  • 我不这么认为,但我不确定。
  • 没有。只有数组和函数会衰减。指针不会衰减。
  • 当然int (*)[] 没有得到调整。它既不是数组类型也不是函数类型。
  • @KerrekSB:这是 C,而不是 C++,也许它们在这里有所不同。 g++ 不接受我上次评论的 TU,说类型不兼容。 FWIW,我上次的评论不正确。指向不完整类型的指针始终是完整类型(在 C 中)。但尽管如此,int (*)[]int (*)[42] 在 C 中是兼容的。(顺便说一句,我所知道的指向不完整数组类型的指针的唯一问题是 *a[0] 无效,而 **a 很好。)
  • @jamesqf: int (*a)[] 表示a 是指向int 数组的指针,而int *a[] 表示a 是指向int 的指针数组.

标签: c arrays function pointers language-lawyer


【解决方案1】:

只有数组类型在传递给函数时转换为指向其第一个元素的指针。 a 的类型是指向int 数组的指针,即它是指针类型,因此没有转换

对于原型

void foo(int a[][10]);

编译器将其解释为

void foo(int (*a)[10]);  

那是因为a[] 是数组类型。 int a[][10] 永远不会转换为 int **a。也就是说,answer 中的第二段是错误的且具有误导性。

作为函数参数,int *a[] 等价于int ** 这是因为a数组 类型。

【讨论】:

  • 也许你的答案被否决了,因为它是 array 衰减而不是“array name”?然而:不是我!
  • @alk; Decay 在 C 标准中的任何地方都没有提及。
  • 标准真的使用“convert”吗?
  • @alk;是的。如果您有兴趣,那么我会发布一些来自标准的报价。
  • @alk; 6.5.2.1 数组下标当在表达式x[i][j] 中使用时,该数组依次转换为指向ints 中第一个的指针,因此x[i][j] 产生一个int 同时,在这种情况下,标准中没有像 decay 这样使用的东西。
【解决方案2】:

int (*)[] 是指向int 数组的指针。

在您的示例中,*a 可以衰减为 int*。但是sizeof(*a) 不会衰减;它本质上是sizeof(int[]),这是无效的。

a 根本不会衰减(它是一个指针)。

【讨论】:

  • 您可以强制它衰减:sizeof( ( 0 , *a ) ) 并获取 int 指针的大小。
【解决方案3】:

N1256 §6.7.5.3/p7-8

7 将参数声明为“类型的数组”应为 调整为''限定指针 type'',其中类型限定符(如果有)是在数组类型派生的 [] 中指定的那些。如果关键字 static 也出现在数组类型的 [] 中 推导,然后对于函数的每次调用, 相应的实际参数应提供对第一个的访问 数组的元素至少与指定的元素一样多 大小表达式。

8 将参数声明为“函数返回类型”应 调整为“指向函数返回type”的指针,如 6.3.2.1.

int (*)[] 是“指向int 数组的指针”。它是“type 的数组”吗?不,是指针。是“函数返回type”吗?不,是指针。因此它没有得到调整。

【讨论】:

    【解决方案4】:

    int (*a)[] 的情况下,sizeof *a 不起作用的原因有一个:数组没有元素计数。没有元素计数,就无法计算大小。

    因此,a 上的任何指针运算都将不起作用,因为它是根据对象的大小定义的。由于数组的大小是不确定的,因此不能对指针本身使用指针算术。数组表示法是根据指针算术定义的,所以sizeof a[0][0](或任何涉及a[n] 的表达式都不起作用,而sizeof (*a)[0] 会起作用。

    这实际上意味着您可以用指针做很少的事情。唯一允许的事情是:

    • 使用一元 * 运算符解除对指针的引用
    • 将指针传递给另一个函数(函数参数的类型必须是数组数组或指向数组的指针)
    • 获取指针的大小(以及对齐或类型,如果您的编译器支持其中一个或两个)
    • 将指针分配给兼容类型

    如果您的编译器支持可变长度数组 (VLA),并且您知道大小,则可以通过在函数体的开头添加一行来解决此问题,如下所示

    void
    foo (int (*a0)[], size_t m, size_t n)
    {
      int (*a)[n] = a0;
      ...
    }
    

    没有 VLA,您必须采取其他措施。

    值得注意的是,动态分配不是int (*)[] 的一个因素。数组数组衰减为指向数组的指针(就像我们在这里一样),因此在将它们传递给函数时它们是可互换的(sizeof 和任何 _Alignoftypeof 关键字是运算符,而不是函数)。这意味着指向的数组必须是静态分配的:一旦数组衰减到指针,就不会再发生衰减,所以不能说指向数组的指针(int (*)[])与指向指针的指针相同(int **)。否则你的编译器会很乐意让你将int [3][3] 传递给一个接受int ** 的函数,而不是想要int (*)[]int (*)[n]int [][n]int [m][n] 形式的参数。

    因此,即使您的编译器不支持 VLA,您也可以使用静态分配的数组将其所有元素组合在一起这一事实:

    void foo (int (*a0)[], size_t m, size_t n)
    {
      int *a = *a0;
      size_t i, j;
    
      for (i = 0; i < m; i++)
        {
          for (j = 0; j < n; j++)
            {
              // Do something with `a[i * n + j]`, which is `a0[i][j]`.
            }
        }
      ...
    }
    

    用作二维数组的动态分配的一维数组具有相同的属性,所以这仍然有效。只有当第二维是动态分配的,这意味着像for (i = 0; i &lt; m; i++) a[i] = malloc (n * sizeof *a[i]); 这样的循环来单独分配每个子数组,这个原则才不起作用。这是因为你有一个指针数组(int *[],或数组衰减后的int **),它指向内存中另一个位置的数组的第一个元素,而不是数组的数组,它保留了所有的项目在一起。

    所以:

    • 不,int (*p)[]int **q 不能以相同的方式使用。 p 是一个指向数组的指针,这意味着所有项目都从p 中存储的地址开始分组在一起。 q是一个指向指针的指针,这意味着项目可能分散在不同的地址,存储在q[0]q[1],...,q[m - 1]中。

    • sizeof *p 不起作用,因为p 指向一个包含未知数量元素的数组。编译器无法计算每个元素的大小,所以对p本身的操作非常有限。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-01-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-09-14
      相关资源
      最近更新 更多