文章目录
- 1 数组的访问方式
- 1.1 两种数组访问方式
- 1.2 下标形式和指针形式对比
- 2 a 和 &a 的区别
- 3 数组参数
- 4 小结
在开始之前,先思考一个问题:数组名可以当作常量指针使用,那么指针是否也可以当作数组名来使用呢?
1 数组的访问方式
1.1 两种数组访问方式
访问数组中的元素有两种访问方式,通过下标访问和通过指针访问数组
1.2 下标形式和指针形式对比
下标形式和指针形式基本是等价的,但是效率略有区别
- 指针以固定增量在数组中移动时,效率略高于下标形式
- 指针增量为 1 且硬件具有硬件增量模型时,效率更高
注意:现代编译器的生成代码优化率已大大提高,在固定增量时,下标形式的效率已经和指针形式相当;但从可读性和代码维护的角度来看,下标形式更优
下标形式与指针形式之间可以相互转换,转换如下:
这里 a[n] 可是可以转换为 n[a] 的哦
下面通过一个例子说明数组的指针访问和下标访问
#include
int main()
{int a[5] &#61; {0};int* p &#61; a;int i &#61; 0;for (i &#61; 0; i < 5; i&#43;&#43;){p[i] &#61; i &#43; 1;}for (i &#61; 0; i < 5; i&#43;&#43;){printf("a[%d] &#61; %d\n", i, *(a &#43; i));}printf("\n");for (i &#61; 0; i < 5; i&#43;&#43;){i[a] &#61; i &#43; 10;}for (i &#61; 0; i < 5; i&#43;&#43;){printf("p[%d] &#61; %d\n", i, p[i]);}return 0;
}
编译运行结果如下&#xff1a;
$ gcc 29-1.c -o 29-1
$ ./29-1
a[0] &#61; 1
a[1] &#61; 2
a[2] &#61; 3
a[3] &#61; 4
a[4] &#61; 5p[0] &#61; 10
p[1] &#61; 11
p[2] &#61; 12
p[3] &#61; 13
p[4] &#61; 14
从结果我们可以看到&#xff0c;通过指针和数组都可以实现访问数组中的元素&#xff0c;可以把数组名当作常量指针使用&#xff0c;也可以把指针当作数组名来使用&#xff0c;a[i] 和 i[a] 是等效的
指针和数组的概念不是完全相等的&#xff0c;二着是有区别的&#xff0c;我们通过一个例子来说明指针数组和指针的不同
#include
int main()
{extern int a[];printf("&a &#61; %p\n", &a);printf("a &#61; %p\n", a);printf("*a &#61; %d\n", *a);return 0;
}
int a[] &#61; {1, 2, 3, 4, 5};
编译运行结果如下&#xff1a;
$ gcc 29-2.c ext.c -o 29-2
$ ./29-2
&a &#61; 0x560167697010
a &#61; 0x560167697010
*a &#61; 1
a 是数组首元素的地址&#xff0c;&a 是整个数组的地址&#xff0c;在数值上二者是相同的&#xff0c;*a 表示取元素第一个元素。
如果把 29-2.c 的第 6 行 extern int a[]; 改为 extern int *a; 代码更改如下&#xff1a;
#include
int main()
{extern int *a; printf("&a &#61; %p\n", &a);printf("a &#61; %p\n", a);printf("*a &#61; %d\n", *a);return 0;
}
再次编译运行&#xff0c;结果完全不一样了
$ gcc 29-2.c ext.c -o 29-2
$ ./29-2
&a &#61; 0x562f0c0ba010
a &#61; 0x200000001
段错误 (核心已转储)
为什么会出现这个运行结果呢&#xff1f;编译器编译 ext.c 后&#xff0c;数组 a 的地址就是 0x562f0c0ba010&#xff0c;等价于 a 的地址就是 0x562f0c0ba010。编译 29-2.c 时&#xff0c;碰见 extern int* a; 就寻找其他位置定义的 a 的值&#xff0c;a 的地址为 0x562f0c0ba010&#xff0c;所以 &a 就是取 a 的地址&#xff0c;就是 0x562f0c0ba010&#xff0c;a 的值是该地址下对应的数据&#xff0c;为 0x200000001。*a 表示取地址值为 0x200000001 的数据&#xff0c;不合法&#xff0c;是不允许访问的&#xff0c;返回段错误。
下面来说明一下为什么地址 0x562f0c0ba010 对应的数据为 0x200000001&#xff0c;我们打印的是 %p&#xff0c;%p 代表的是&#xff1a;按十六进制输出数据&#xff0c;长度为 8 个字节&#xff0c;Linux 是小端序系统&#xff0c;低地址存放低位&#xff0c;数组存储方式如下&#xff0c;取 8 字节数据为 0x200000001
2 a 和 &a 的区别
- a 为数组首元素的地址
- &a 为整个数组的地址
- a 和 &a 的区别在于指针运算
a &#43; 1 表示指向下一个元素&#xff0c;a &#43; 1是越过一个元素&#xff0c;&a &#43; 1 表示指向这个数组最后一个元素后面的位置&#xff0c;&a &#43; 1 是越过一个数组&#xff0c;可以从下图清晰的看到二者的指向
下面看一个例子&#xff0c;更好的区分 a 和 &a
#include
int main()
{int a[5] &#61; {1, 2, 3, 4, 5};int* p1 &#61; (int*)(&a &#43; 1);int* p2 &#61; (int*)((long long)a &#43; 1);int* p3 &#61; (int*)(a &#43; 1);printf("%d, %d, %d\n", p1[-1], p2[0], p3[1]);return 0;
}
$ gcc 29-3.c -o 29-3
$ ./29-3
5, 33554432, 3
- p1 指向整个数组最后一个元素后面的位置&#xff0c; p1[-1] &#61;&#61;> *(p1 -1)&#xff0c;所以 p1[-1] 指向数组最后一个元素。
- (int*)((long long)a &#43; 1); 先把指针转换为 long long 再相加&#xff0c;这已经不是指针运算了&#xff0c;就是基本数据类型之间的运算&#xff0c;再次解引用&#xff0c;等于从数组首元素的第二个字节开始&#xff0c;取 4 个字节的数据&#xff0c;结果如下&#xff0c;linux 系统是小端序&#xff0c;取到的数据为 0x02000000&#xff0c;等于十进制的 33554432。
- p3 指向数组第二个元素
3 数组参数
- 数组作为函数参数时&#xff0c;编译器将其编译成对应的指针
结论&#xff1a;将数组名作为参数传递给函数时&#xff0c;数组名退化为指针&#xff0c;一般情况下&#xff0c;当定义的函数中有数组参数时&#xff0c;需要定义另一个参数来表示数组的大小
下面通过例子来验证我们的理论
#include
void func1(char a[5])
{printf("In func1: sizeof(a) &#61; %ld\n", sizeof(a));*a &#61; &#39;a&#39;;a &#61; NULL;
}
void func2(char b[])
{printf("In func2: sizeof(b) &#61; %ld\n", sizeof(b));*b &#61; &#39;b&#39;;b &#61; NULL;
}
int main()
{char array[10] &#61; {0};func1(array);printf("array[0] &#61; %c\n", array[0]);func2(array);printf("array[0] &#61; %c\n", array[0]);return 0;
}
在 func1() 函数中&#xff0c;&#xff0c;打印 sizeof(a)&#xff0c;数组退化成指针&#xff0c;所以不管数组长度如何&#xff0c;打印的都是指针的长度&#xff0c;由于退化成指针&#xff0c;所以允许 修改指针 a &#61; NULL; 我们是不能修改数组名的指向的。func1() 函数是一样的&#xff0c;只是没有指定数组长度。
编译运行结果如下&#xff1a;
$ gcc 29-4.c -o 29-4
$ ./29-4
In func1: sizeof(a) &#61; 8
array[0] &#61; a
In func2: sizeof(b) &#61; 8
array[0] &#61; b
4 小结
1、数组名和指针仅使用方式相同&#xff0c;数组名本质不是指针&#xff0c;指针也不是数组名
2、数组名并不是数组的地址&#xff0c;而是数组首元素的地址
3、函数的数组参数退化为指针