--事物的难度远远低于对事物的恐惧!
上一篇我们已经对指针和数组作了简单的分析,那么这一篇我们继续来对他们做个详细的分析,首先来提出一个问题:
数组名可以当做常量指针使用,那指针是否也可以当做数组名来使用呢?为了说明这个问题,我们先来看看数组的两种访问方式,对于数组int array[5] = {0};访问方式有:
-下标形式: array[1] = 2;
-指针形式:*(array + 1) = 2;
两种访问方式的比较:
-指针以固定增量在数组中移动是,效率高于下标访问形式
-指针增量为1且硬件具有硬件啊增量 模型时,效率更高
-指针 形式与下标下标形式的转换:
a[n] -> *(a + n) -> *(n + a) -> n[a]
虽然说理论上指针形式访问比下标形式效率更高,但是现代编译器的优化率已经相当高,指针与下标两种访问形式效率已经相当了,但是下标访问形式更利于代码的可读性和可维护性。
知道了数组的两种访问方式,我们来看看下边的代码:
#include <stdio.h> int main() { int a[5] = {0}; int* p = a; int i = 0; for(i=0; i<5; i++) { p[i] = i + 1; //指针去访问 } for(i=0; i<5; i++) { printf("a[%d] = %d\n", i, *(a + i)); //打印数组元素值 } printf("\n"); for(i=0; i<5; i++) { i[a] = i + 10; } for(i=0; i<5; i++) { printf("p[%d] = %d\n", i, p[i]); } return 0; }
执行的输出结果为,从输出结果来看,数组确实可以通过指针和下标两种方式去访问:
下边来看一个比较容易出错也比较有意思的知识点:对于一个数组 int array[5],array 和&array有什么区别?
-array为数组首元素的地址
-&array为整个数组的地址
-array 与 &array的值相同,但是代表的意义不同,主要区别在于它们的指针运算
-array + 1 -> (unsigned int)a + sizeof(*a)
-&array + 1 -> (unsigned int)(&a) + sizeof(*&a) -> (unsigned int)(&a) + sizeof(a)
为了更深的理解上边的内容,下边以一段代码来展示:
#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, %d, %d\n", p1[-1], p2[0], p3[1]); return 0; } //以上代码,下边那个输出是正确的? // A. 数组下标不能是负数,程序无法运行 // B. p1[-1]将输出随机数,p2[0]输出2, p3[1]输出3 // C. p1[-1]将输出乱码, p2[0]和p3[1]输出2
下边我们来编译执行一下:
从输出结果来看,好像三个选项都没有符合的,下边我们来逐一分析下:
- 第一个输出:由于我们是将&a + 1 赋值给p1指针,所以p1指针就指向了数组的末端,那么p[-1] -> *(p-1)是不是就指向前一个数组元素呢?也就是值为5的那个元素,对没错,就是这样的,现在理解了吗?
- 第二个输出:数组名a代表首元素的地址,但是我们做了(int)a + 1这样的处理,实际就是将首元素的地址转换成普通整数,普通整在加1,就是很普通的数值运算,假如a = 0xaabbccd0,那么(int)a + 1 -> 0xaabbccd1,以这个值作为内存地址去取值,得到的肯定是随机数或乱码。
- 第三个输出:这个比较容易理解,p3指向数组元素a[1], p3 + 1就指向数组元素a[2],得到的值也就是3。
下边我们来说说数组参数
-数组作为函数参数时,编译器将其编译成对应的指针
void fun(int a[]) -> void fun(int *a)
void fun(int a[5]) -> void fun(int *a)
即数组作为函数参数时,函数内部是不知道数组的大小的,所以一般情况下,函数传入数组参数的同时,需要另外定义一个参数来标示数组大小。
总结:
-数组名和指针仅仅是可以交互使用,数组名不是指针,指针的本质也不是数组
-数组名是数组首元素的地址,不是数组的地址
-函数的数组参数退化为指针