6

当我需要将数组传递给函数时,似乎该函数的所有以下声明都将起作用

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 上使用是错误的呢?

4

7 回答 7

12

为了理解差异,我们需要了解两个不同的上下文

  • 上下文中,类型数组的名称T等价于指向 type 的指针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需要一个指向 an 的指针int,所以函数定义和使用匹配。传递给f()的是指向a( &a[0]) 的第一个元素的指针。

如果是

int a[]={1,2,3,4};
int b[4] = a;

该名称b在对象上下文中使用,并且不会简化为指针。(顺便说一下,a这里在一个值上下文中,并简化为一个指针。)

现在,int b[4];分配 4 s 的存储值int并为其命名ba也被分配了类似的存储空间。因此,实际上,上述分配意味着“我想让存储位置与之前的位置相同”。这没有意义。

如果你想复制ainto的内容b,那么你可以这样做:

#include <string.h>
int b[4];
memcpy(b, a, sizeof b);

或者,如果您想要一个指向的b指针a

int *b = a;

在这里,a在值上下文中,并归约为指向 的指针int,因此我们可以分配aint *

最后,在初始化数组时,您可以为其分配显式值:

int a[] = {1, 2, 3, 4};

在这里,a 有 4 个元素,初始化为 1、2、3 和 4。您还可以这样做:

int a[4] = {1, 2, 3, 4};

如果列表中的元素数少于数组中的元素数,则其余值均设为 0:

int a[4] = {1, 2};

设置a[2]a[3]为 0。

于 2010-01-09T22:42:42.213 回答
7
void f(int arr[]);
void f(int arr[4]);

语法具有误导性。它们都与此相同:

void f(int *arr);

即,您正在传递一个指向数组开头的指针。您没有复制数组。

于 2010-01-09T21:45:09.023 回答
6

C 不支持数组的赋值。在函数调用的情况下,数组衰减为指针。C 确实支持指针的分配。几乎每天都在这里问这个问题——你们在读什么没有解释这一点的 C 教科书?

于 2010-01-09T21:45:27.337 回答
3

试试memcpy。

int a[]={1,2,3,4};
int b[4];
memcpy(b, a, sizeof(b));

感谢您指出这一点,史蒂夫,我已经有一段时间没有使用 C 语言了。

于 2010-01-09T21:48:07.650 回答
1

要获得对它的直觉,您必须了解机器级别上发生了什么。

初始化语义(= {1,2,3,4})的意思是“完全以这种方式将它放在二进制图像上”,因此可以编译。

数组分配会有所不同:编译器必须将其转换为一个循环,该循环实际上会遍历元素。C 编译器(或 C++,就此而言)从不做这样的事情。它理所当然地希望你自己做。为什么?因为你能。所以,它应该是一个子程序,用 C (memcpy) 编写。这一切都是为了简单和接近你的武器,这就是 C 和 C++。

于 2010-01-09T23:30:29.753 回答
0

注意 in 的a类型int a[4]int [4].

但是TypeOf ( &a) == int (*)[4]!= int [4]

另请注意,的类型ais 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
}
于 2010-04-05T00:49:15.063 回答
0

我想澄清一下。答案中有一些误导性提示......以下所有函数都可以采用整数数组:

void f(int arr[])
void f(int arr[4])
void f(int *arr) 

形式上的论点是不一样的。因此编译器可以以不同的方式处理它们。在内部内存管理的意义上,所有参数都指向指针。

void f(int arr[])

... f() 采用任意大小的数组。

void f(int arr[4])

... 形式参数表示数组大小。

void f(int *arr)

...您还可以传递一个整数指针。f() 对大小一无所知。

于 2016-10-14T15:19:35.170 回答