数组也是变量
变量本质是一个地址,变量名是与地址绑定的一个标号,变量类型是占用内存字节长度。数组也是变量,只是长度不一样。
数组中左右值含义
数组int a[10]会出现的情况有a、a[0]、&a、&a[0]
- a[0]:做左值,代表第0个元素格子;做右值,代表首元素的地址的内容。
- a:做左值,代表a整个数组空间,不能直接被赋值;做右值代表数组的首元素的地址,等效于 &a[0];
- &a:禁止左值,&a是一个常量(地址数),做右值,代表整个数组的首地址;于a的意义不同,地址数字相同,参与运算的时候结果会不同。
- &a[0]:禁止左值,做右值时代表第首元素的地址,所以做右值时,与a是一样的。
- 重点结论:
(1)右值a和&a[0]指数组首元素首地址,完全等效;
(2)右值&a指数组的首地址,不是首元素地址,虽然地址是一样,但意义不一样,会影响计算结果。
指针与数组的关系
数组是指针的二次封装,数组的变量名可以看成一个指针变量名,他指向数组的首元素首地址。
指针方式访问数组,*(指针±偏移量);例:int a[10];b= *(a+1);数组下标访问其实是c的包装,底层还是使用的指针+偏移的方式访问的。注意:偏移量+1不是地址加1,而是加一个数据类型的长度,如int型则编译的时候地址实际上是+4,但是不涉及写程序,所以指针+1,刚好指向数组的下一个元素。所以任何时候,在使用指针+1的时候一定要考虑数据类型,如果是结构体类型,那么+1移动的是一整个结构体。
指针类型
- 指针本身的类型都是int*类型,指针变量自己的内存永远都是存储地址(int型),在内存中都占4个字节。
- 不同的是指针指向的对象的类型不同,即指向内存的解析方式不同。
- 所以不同的类型的的指针,指的是指向的对象的类型不同,如int *p、float *p。
sizeof
sizeof(x);x可以是变量名,也可以是变量类型,两个结果完全相同,如sizeof(int);sizeof(typdef struct );特别是在使用结构体的时候我们喜欢使用变量类型和变量名,应只使用1种。
int a[100];sizeof(a);sizeof检测数组长度时,a既不是左值也不是右值,只是数组名,返回的是数组占用的内存空间,注意不是数组的数据个数,上例中的大小应该是4*100=400。
函数传参可以传数组,但是实际上传过去的并不是数组整体,而是数组的首元素地址,和函数传参的设置为指针完全相同;如下例:
int fa[100];
void func(int num);
func(a);
在func函数中使用sizeof(a),计算出的结果是4;
int a[100];
void b(int *p);
func(a);
在func函数中使用sizeof(a),计算出的结果是4;
以上两句传的参数全是等效的,都是传的首元素首地址,所以如果想传数组长度,需要另外加1个传参;
sizeof计算数组长度
int a[100];
sizeof(a) / sizeof(a[0]); //数组总长度 ÷ 单个元素大小=数组元素个数;
实参 & 形参
函数
函数可以看成1个加工机器,函数名是1个指向函数体的指针常量。
函数只能有1个返回值,通常用来返回函数运行正确与否。函数的输入输出都是使用形参,行业共识,输入使用普通变量或者const指针,输出使用非const指针。
传值调用
实参与形参地址不同,只是将实参的内容赋值到了形参中(普通变量、结构体直接传参);
普通变量(int a),在传值调用(函数传参)中,实参与型参是不同的两个内存,即不同的变量,他们内容相同;实参是右值,形参左值。
结构体变量作为形参时,与普通变量完全相同。但是效率很低,因为需要把所有的值都赋值一次,所以推荐使用指针传递,这样还可以间接访问到实参。
传址调用
实参与形参地址相同,是同一个内存(数组,结构体指针传参)。
数组变量(int a[100]),在传址调用(函数传参)中,实参与型参是相同的两个内存,即同一个变量,他们内容相同,所以可以通过改变形参来间接改变实参。数组作为形参时,数组大小x(table[x])是可有可无的。因为传的是指针,与长度没关系。