数组类型 – 指定/用作函数参数的规则
当我需要将一个数组传递给一个函数时,似乎所有以下函数声明都可以工作
void f(int arr[]) void f(int arr[4]) // is this one correct?
为了这:
int a[]={1,2,3,4}; f(a);
但是当我将一个数组分配给另一个数组时,它就失败了
int a[]={1,2,3,4}; int b[4] = a; // error: array must be initialized with a brace-enclosed initializer
那么为什么作为函数的参数传递的数组是可以的,但是在简单赋值的rhs上使用是错误的?
为了理解差异,我们需要了解两种不同的背景 。
- 在值上下文中,类型
T
的数组的名称等同于指向类型T
的指针,并且等于指向数组的第一个元素的指针。 - 在对象上下文中,类型
T
的数组的名称不会缩减为指针。
什么是对象上下文?
在a = b;
, a
在对象上下文中。 当您获取变量的地址时,它将在对象上下文中使用。 最后,当您对变量使用sizeof
运算符时,它将在对象上下文中使用。 在所有其他情况下,变量用于值上下文。
现在我们掌握了这些知识,当我们这样做时:
void f(int arr[4]);
它完全等同于
void f(int *arr);
如您所知,我们可以省略函数声明中的大小(上面的4)。 这意味着您无法知道传递给f()
的“数组”的大小。 以后,当你这样做时:
int a[]={1,2,3,4}; f(a);
在函数调用中,名称a
在值上下文中,因此它缩减为指向int
的指针。 这很好,因为f
需要一个指向int
的指针,所以函数定义和使用匹配。 传递给f()
是指向( &a[0]
)的第一个元素的指针。
如果是
int a[]={1,2,3,4}; int b[4] = a;
名称b
用于对象上下文,而不是缩减为指针。 (顺便说一下,这里是一个值上下文,并缩减为指针。)
现在, int b[4];
分配4 int
的存储空间并给它起名称b
。 a
也被分配了类似的存储空间。 因此,实际上,上述分配意味着“我想使存储位置与先前位置相同”。 这没有意义。
如果要将a
的内容复制到b
,则可以执行以下操作:
#include int b[4]; memcpy(b, a, sizeof b);
或者,如果你想要一个指向b
的指针b
:
int *b = a;
这里, a
是值上下文,并缩减为指向int
的指针,因此我们可以将a
指定给一个int *
。
最后, 在初始化数组时 ,您可以为其指定显式值:
int a[] = {1, 2, 3, 4};
这里有一个有4个元素,初始化为1,2,3和4.您还可以这样做:
int a[4] = {1, 2, 3, 4};
如果列表中的元素数少于数组中元素的数量,则其余值将取0:
int a[4] = {1, 2};
将a[2]
和a[3]
为0。
void f(int arr[]); void f(int arr[4]);
语法有误导性。 它们都是这样的:
void f(int *arr);
即,您正在将指针传递给数组的开头。 你没有复制数组。
C不支持数组的赋值。 在函数调用的情况下,数组衰减为指针。 C确实支持指针的分配。 这里几乎每天都会问这个问题 – 你们在阅读哪些C教科书并不能解释这个问题?
试试memcpy。
int a[]={1,2,3,4}; int b[4]; memcpy(b, a, sizeof(b));
感谢你指出这一点,史蒂夫,自从我使用C以来已经有一段时间了。
为了获得您的直觉,您必须了解机器级别的情况。
初始化语义(= {1,2,3,4})意味着“以这种方式将它放在你的二进制映像上”,这样就可以编译了。
数组赋值会有所不同:编译器必须将其转换为循环,实际上迭代元素。 C编译器(或C ++,就此而言)永远不会做这样的事情。 理所当然地希望你自己做。 为什么? 因为你能。 所以,它应该是一个用C(memcpy)编写的子程序。 这完全是关于你的武器的简单性和贴近性,这是C和C ++的全部内容。
注意int a[4]
的a
的类型是int [4]
。
但是TypeOf ( &a
)== int (*)[4]
!= int [4]
。
还要注意a
的值的类型是int *
,它与以上所有不同!
这是您可以尝试的示例程序:
int main() { // All of these are different, incompatible types! printf("%d\n", sizeof (int[4])); // 16 // These two may be the same size, but are *not* interchangeable! printf("%d\n", sizeof (int (*)[4])); // 4 printf("%d\n", sizeof (int *)); // 4 }
我想澄清一下。 答案中有一些误导性的提示……以下所有函数都可以采用整数数组:
void f(int arr[]) void f(int arr[4]) void f(int *arr)
但正式的论点并不相同。 因此编译器可以以不同方式处理它们 在内部内存管理的意义上,所有参数都会引发指针。
void f(int arr[])
… f()采用任意大小的数组。
void f(int arr[4])
…forms参数表示数组大小。
void f(int *arr)
…您还可以传递整数指针。 f()对尺寸一无所知。