2010-01-09 37 views
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 

那么,为什么数组作为argum传递一个函数的入口是可以的,但用于简单赋值的rhs是错误的?

+3

问题的标题需要重写,当前的标题是过于通用和小写。 – 2010-01-10 04:34:47

回答

12

对于理解不同,我们需要了解两种不同上下文

  • 上下文,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&a[0])的第一个元素的指针。

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

名称b的情况下在一个对象上下文中使用,并且不降低到指针。 (顺便说一下,这里a处于值上下文,并减少了一个指针。)

现在,int b[4];分配的4个int S吸留值得并给出了名称b给它。 a也被分配了类似的存储空间。所以,实际上,上述分配意味着“我想使存储位置与先前的位置相同”。这没有意义。

如果你想副本a内容为b,那么你可以做:

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

或者,如果你想要一个指针b是指向a

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

+0

编写得很好。 – benzado 2010-01-09 23:35:08

+0

你能提供一个关于对象和值上下文的阅读地点的参考吗? – 2011-12-23 13:57:02

+0

我从来没有听说过“物体背景”与“价值背景” – 2011-12-23 14:02:16

7
void f(int arr[]); 
void f(int arr[4]); 

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

void f(int *arr); 

即,您传递的是指向数组的开始的指针。您不复制阵列。

+3

是的,但是(在C99中)'void f(int arr [static 4]){...}' 是特殊的,因为这允许编译器假定'arr'不是'NULL'并且其大小至少4. – Jed 2010-01-09 21:55:33

6

C不支持数组的赋值。在函数调用的情况下,数组衰减为指针。 C支持指针的分配。这里几乎每天都会问到这个问题 - 你们读的C文本书不能解释这一点吗?

3

尝试使用memcpy。

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

感谢您指出了这一点,史蒂夫,它已经有一段时间,因为我用C.

+1

'memcpy(b,a,4 * sizeof(int)'。或'sizeof(b)'。 – 2010-01-09 22:31:20

1

为了让你的直觉的话,你必须明白这是怎么回事机器水平。

初始化语义(= {1,2,3,4})意思是“把它放在你的二进制图像上”,所以这可以编译。

数组赋值可能会有所不同:编译器必须将其转换为循环,实际上将在元素上迭代。 C编译器(或C++,就此而言)从未做过这样的事情。它理所当然地希望你自己去做。为什么?因为你能。所以,它应该是一个用C(memcpy)编写的子程序。这一切都是关于简单和亲近你的武器,这是C和C++的全部。

0

请注意,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 
} 
+0

您应该使用“%zu”打印size_t(在C99中)。 – Jens 2011-04-17 07:43:16

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()不知道有关的大小。

相关问题