1、数组本质回顾
数组是一段连续的内存空间;
数组的空间大小为:sizeof(ArrayType)*ArraySize;
数组名可以看作指向数组首元素的常量指针;
2、指针的运算
2.1指针与整数的运算
运算规则为:
p+n;==(unsigned int )p+n*sizeof(*p);
2.2指针与指针的运算
指针之间只支持减法运算,且参与运算的指针类型必须相同:
运算规则为:
P1-p2;==((unsigned int)p1-(unsigned int)p2)/sizeof(type);
当两个指针指向同一个数组中的元素时,指针相减才有意义,得到的差是指针所指元素的下标差;
当两个指针指向的元素不在同一个数组中时,结果无意义
2.3 指针的关系运算
指针关系运算的前提是同时指向同一个数组中的元素,然后就可以进行关系运算;
指针之间可进行关系运算:< <= > >= == != ;前四个其实比较的是指针所指元素的下标,
后两个可在任意两个指针之间的(==,!=)运算,无限制!
3、数组元素的访问
3.1 以下标形式
这种形式很常见,例如:
int a[5]={1,2,3,4,5};
a[0]=a[0]+1;
a[1]=a[1]+1;
这里实际上会做指针与整数的运算,a[1]=(unsigned int)a+sizeof(type)*1;
3.2 以指针形式
基于数组名是数组首元素地址,例如:
int a[5];
*a=1;
*(a+1)=2;
实际上这里会先做指针与整数之间的加法运算,得出地址;运算规则如上所述:*(a+1)=*((unsigned int)a+sizeof(type)*1)
3.3 下标VS 指针 谁更好?
从理论上而言,当指针以固定增量在数组中移动时,其效率高于使用下标的代码;
当指针增量为1且硬件具有硬件增量模型时,表现更好!
现代编译器生成代码优化率已经大大提高,在固定增量时,下标形式的效率已经和指针形式相当;但从可读性和代码维护性的角度看,下标形式更好一点;
4、a和&a的区别
如果有如下定义
int a[5];
那么,a为数组首元素地址;
&a为整个数组的起始地址;
它们的值虽然一样,但是意义不一样,具体的区别就在于指针运算:
a+1;===> (unsigned int)a+sizeof(*a)*1
&a+1;===>(unsigned int)(&a)+sizeof(*(&a))*1=(unsigned int)(&a)+sizeof(a)*1;
因为*与&互为逆运算;
5、指针运算经典面试题
计算输出结果?#include <stdio.h>
int main()
{
int a[5] = {1, 2, 3, 4, 5};
int* p1 = (int*)(&a + 1);
int* p2 = (int*)((int)a + 1);
int* p3 = (int*)(a + 1);
printf("%d, 0x%08X, %d\n", p1[-1], p2[0], p3[1]);
return 0;
}
计算方法分析:
首先要分别计算出p1,p2,p3的值:
假设a的起始地址为:10;即是a的首元素地址为10;
p1= (int*)(&a + 1);==> (unsigned int)(&a)+sizeof(*(&a))*1==10+sizeof(a)*1=10+20=30;
P2=(int*)((int)a + 1);==>(int)a+1=10+1=11;
P3=(int*)(a + 1);==>(unsigned int)a+sizeof(*a)*1=10+4=14;
则:
p1[-1]=(unsigned int)p1+sizeof(int)*(-1)=30+(-4)=26;
p2[0]=(unsigned int)p2+sizeof(int)*0=11;
P3[1]=(unsigned int)p3+sizeof(int)*1=14+4=18;
详细分析看图:
6、数组做为函数参数
C语言中,如果一个函数传入的参数是一个数组,那么编译器将会把这个参数编译成对应的指针,到了函数内部,它就不再是一个数组,而仅仅是一个指针;
void func(int a[]); ==> void func(int *a);
void func(int a[5]);==> void func(init *a)
看上面的函数定义,虽然我们定义了形参数组有5个元素,但是经过编译,实际上在func函数内部并不能得到这个数组的大小5;如下示例:
#include <stdio.h>
#define DIM(array) (sizeof(array)/sizeof(*array))
int fun_size(int num[500])
{
return DIM(num);
}
int main()
{
int a[5] = {1, 2, 3, 4, 5};
printf("a数组的大小:%d\tfunc函数计算的大小:%d\n",DIM(a),fun_size(a));
return 0;
}
运行结果:
7、指针和数组的对比
- 数组声明时编译器自动分配一片连续的内存空间;
- 指针声明时只分配用于容纳指针的4个字节的空间;
- 在作为函数参数时,数组参数和指针参数等价;
- 数组名在多数情况下可以看作常量指针,其值不能改变;
- 指针本质是变量,保存在其中的值被看作内存中的地址;