一、前言在C/C++语言中,数组类型的变量不能直接赋值。但是如果把数组放在结构体中,然后给结构体的变量赋值,就可以把数组的内容复制到里面。很多朋友对此不是很理解,只是强行记忆,下面我就试着用自己的理解来描述一下,希望对大家有所帮助!二、数组的各种运算1、错误代码inta[5]={1,2,3,4,5};intb[5];b=a;对于上面的赋值语句,编译器会报错:assignmenttoexpressionwitharraytype,即不能为数组类型的变量赋值。那么此时编译器是如何解释a和b的呢?这个问题将在下面讨论。有一个地方要提一下:第一条语句中的=操作不是赋值,而是初始化。C/C++语法规定,在定义变量时,可以使用运算符=进行初始化。2.使用结构复制数组typedefstruct{intarr[5];}array_wrap;array_wrapa={{1,2,3,4,5}};array_wrapb;b=a;这里的赋值操作是针对结构体变量的,C语言标准允许这种行为,是合法的。变量a的所有内容(即这个变量所占内存空间的内容)都会原样复制到变量b中。3.其他拷贝方式由于不能直接给数组类型的变量赋值,只能另寻他法,例如:使用memcpy(b,a,sizeof(int)*5);复制一整段内存空间的内容;使用for/while等循环语句将数组中的每个元素逐一复制:b[i]=a[i];3.语言标准和编译器是设计出来的,所以我们在编程的时候需要严格遵守这些规则。在这些规则中,有一条:只有标量和结构可以出现在赋值运算符=的左侧。但是数组类型不是标量,因此无法对结构进行赋值。理论上,如果C/C++语言愿意,直接给数组赋值是“可以”的(那就需要修改语法标准),但标准委员会权衡后做出了现行规定各种场景的优劣,这是权衡各种因素综合考虑的结果。也就是说,在目前的数组操作标准中,利大于弊。既然标准已经制定成这样了,我们就来分析一下编译器是如何遵循和实现这个标准的。1.数组和指针的二义性关系很多人都记得这个:数组的名字就是指向数组起始地址的指针。这是错误的,或者说不准确。在C/C++中,数组就是数组,指针就是指针。数组在内存中有一定的空间(每个元素的大小x元素的数量)。只是在表达式中,数组名会“暂时”代表数组第一个元素的常量指针(前提:没有运算符sizeof和&)。下面这段代码,打印结果是一样的:inta[5]={1,2,3,4,5};printf("a=%p\n",a);printf("&a=%p\n",&a);在第一个printf中,a将“暂时”表示指向第一个元素的常量指针。在第二个printf中,a代表一个数组,与指针无关。在其前面加上地址符号&表示获取数组的地址,与第一个元素的地址重合。.注意:代码编译成二进制文件后,没有变量的概念,C/C++代码中的所有变量都是通过地址“传递”的。2、为什么不能给数组变量赋值?有了上面的基本认识,就好办了。对于以下代码:inta[5]={1,2,3,4,5};intb[5];b=a;在赋值语句b=a中,左边的b是数组类型,右边的a被编译器“暂时”表示为指向第一个元素的常量指针,但数组不是标量,不能放置在赋值运算符=的左边,所以编译器报错:非法!由于在表达式中,数组名暂时由第一个元素的常量指针表示,这意味着我们无法计算数组名本身,例如:a++、a--等操作无法进行。例如:下面的遍历方式是非法的:inta[5]={1,2,3,4,5};for(inti=0;i<5;i++){//常量指针,不能自增操作printf("a[%d]=%d\n",i,*a++);}3.如果函数参数是一个数组,考虑下面的函数:voidfunc(intarr[5]){for(inti=0;i<5;++i){printf(*arr++);//合法!}}形参arr形式上看似是一个数组,但实际上被编译器认为是一个指针,相当于:voidfunc(int*arr)。所以在printf的打印语句中,arr是可以递增的。PS:在这种场景下,需要额外传递一个参数来告知元素个数。调用该函数的代码如下:inta[5]={1,2,3,4,5};乐趣(一);数组名暂时表示第一个元素的常量指针,在传递参数时,参数arr的值就是数组第一个元素的内存地址。4、为什么结构体中的数组可以复制?有了以前的语法标准,这个问题好像不用讨论了~~赋值的目的是什么?就是让一个内存空间的内容和另一个内存空间的内容完全一样。如果要完成复制操作,就需要知道这块内存空间的大小。编译器知道一个结构变量占用空间的大小,所以在复制的时候,类似于memcpy,一个字节一个字节的复制一个结构变量占用的空间。5.参数传递和返回值调用函数时,实参向形参的传递;函数执行后的返回值;这两种情况都涉及变量的分配。关于参数传递,上面已经说了:编译器把形式参数当作普通的指针类型。对于函数返回值,也是一样,不能直接返回一个数组,因为它只是一个临时的常量指针,代表第一个元素。当然可以利用结构体的可赋值特性,将数组包裹在其中,达到复制的效果。五、小结记住这两句话:1、数组是数组,指针是指针,互不相干。2、在表达式中,数组名将“暂时”代表数组第一个元素的常量指针(前提:没有运算符sizeof和&)
