C函数:数组参数长度

apeeds0o  于 5个月前  发布在  其他
关注(0)|答案(2)|浏览(60)

这个问题建立在this one的基础上,它描述了以下内容是如何等效的:

int f(int a[10]) { ... } // the 10 makes no difference
int f(int a[]) { ... }
int f(int *a) { ... }

字符串
在有关Function prototype scope的文档中,提供了以下示例:

int f(int n, int a[n]); // n is in scope, refers to first param


这就引出了一个问题:在何种程度上,以下内容是等价的:

// 1.
int f(int n, int a[n]) { ... }
int f(int n, int *a) { ... }
// my guess: exactly equivalent

// 2.
int x = 10; int f(int a[x]) { ... }
int x = 10; int f(int *a) { ... }
// my guess: exactly equivalent

// 3.
int f(int a[n], int n) { ... }
int f(int *a, int n) { ... }
// my guess: not equivalent: first f won't compile, second will


结论性问题:

  • 假设int f(int arr[n])int f(int *arr)都可以编译,那么它们总是等价的吗?
  • 为什么编译器要查找标识符n,即使它的值不会被使用。
  • 我猜:在源代码中包含n的唯一原因是作为文档来指示数组应该具有的最小长度。确保n在范围内,并且具有正确的类型,确保此文档有意义
bxfogqkk

bxfogqkk1#

假设int f(int arr[n])int f(int *arr)都可以编译,那么它们总是等价的吗?
C标准在这一点上是有缺陷的;它没有说明int arr[n]中的n是否被评估。
int arr[n]名义上声明了一个可变长度数组(假设n是某个对象的标识符,而不是常量的宏)。C标准说,可变长度数组的大小会被评估,它还说参数声明int arr[n]会自动调整为int *arr,但不清楚这些在. GCC and Clang differ on this point中出现的顺序; Clang计算数组大小,但GCC不计算。对于非限定对象n,这没有区别,但是,如果n是volatile的,或者数组大小是其他有副作用的表达式,它确实有区别。
为什么编译器要查找标识符n,即使它的值不会被使用。
编译器需要解析代码以确定其语法结构。可以在那里出现有效的关键字,例如staticconstrestrictvolatile_Atomic。其他关键字可能是错误的。或者您可以使用其他表达式,例如n * 3。如果你有n,并且它是int的标识符,编译器可以忽略它。如果你有n,并且它是一个函数的标识符,编译器应该发出一个诊断消息。无论那里有什么,编译器需要对其进行分析,并接受它或发出适当的诊断消息。
参数声明中的数组大小在多维数组中更有用,如void f(size_t m, size_t n, int Array[m][n])。在这里,m如上所述,可以省略,但n被使用,并且是关键的。

iyr7buue

iyr7buue2#

对于初学者,根据C标准(6.7.6.3函数声明符(包括原型)):
7将参数声明为“array of type”应调整为“qualified pointer to type”,其中类型限定符(如果有的话)是在数组类型派生的[ and ]中指定的那些。如果关键字static也出现在数组类型派生的[ and ]中,则对于每次调用函数,对应的实际参数的值将提供对数组的第一个元素的访问,该数组至少具有与size表达式指定的元素一样多的元素。
其次(6.7.6.2数组声明符)
4如果size不存在,则数组类型为不完整类型。如果size为 * 而不是表达式,则数组类型为未指定大小的变长数组类型,只能在声明或具有函数原型作用域的类型名中使用; 146)这样的数组仍然是完整类型。如果大小是整数常量表达式并且元素类型具有已知的常量大小,数组类型不是可变长度数组类型;否则,数组类型是可变长度数组类型。(可变长度数组是实现不需要支持的条件特性;参见6.10.8.3。)
C标准中第一个引号中突出显示的短语适用于具有可变长度数组类型、不可变数组类型(当数组的大小由整数常量表达式指定时)和不完整数组类型的参数声明。
在这个函数声明的例子中,

int f(int a[10]) { ... } // the 10 makes no difference
int f(int a[]) { ... }
int f(int *a) { ... }

字符串
具有数组类型的参数由编译器调整为指向数组元素类型的指针。因此所有三个函数声明(注意必须提供一个定义)都声明了同一个函数。
这同样适用于这个函数声明的例子

// 1.
int f(int n, int a[n]) { ... }
int f(int n, int *a) { ... }


在第一个声明中,第二个参数具有可变长度数组类型。

int f(int , int [*]);


至于第三个例子

// 3.
int f(int a[n], int n) { ... }
int f(int *a, int n) { ... }


然后如果n没有在第一个函数声明之前在文件作用域中声明,那么编译器显然会发出一个错误,即标识符(或名称)n没有声明。但是如果变量n将在函数声明之前声明,那么上述两个声明将是等价的。
这是一个演示程序

//gcc 7.4.0

#include  <stdio.h>

int f( int [*], int );

int f( int *, int );

int n = 10;

int f(int a[n], int n) 
{
    for ( int i = 0; i < n; i++ )
    {
        printf( "%d ", a[i] );
    }
    
    putchar( '\n' );
    
    return 0;
}

int main(void)
{
    int a[20];
    const size_t N =  sizeof( a ) / sizeof( *a );
             
    for ( size_t i = 0; i < N; i++ )
    {
        a[i] = i;
    }
             
    f( a, N );              
}


程序输出为

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19

相关问题