目录
指针和数组分析(上)
1.数组的本质
- 数组是一段连续的内存空间
- 数组空间大小sizeof(arr_type)*arr_size
- 数组名可看做指向数组第一个元素的常量指针
2.指针的运算
- 指针之间只支持减法运算
- 参与减法运算的指针类型必须相同
p1 - p2 <---> ((unsigend int)p1 - (unsigned int)p2)/sizeof(type);
注意:
1.只有当两个指针指向同一个数组中的元素时,指针相减才有意义,其意义为指针所指元素的下标
2.当两个指针的元素在不同一个数组中时,结果未定义
p + n ===p + n*sizeof(type)
指针和数组分析(下)
1.数组的访问方式
- 以下标的形式访问数组中的元素
- 以指针的形式访问数组中的元素
2.下标形式VS指针形式
- 指针以固定的增量在数组中移动时,效率高于下标形式
- 指针增量为1且具有硬件增量模型时,效率更高
- 下标形式与指针形式的转换
a[n] <-->*(a+n)<-->*(n+a)<-->n[a]
注意:
现代编译器的生成代码优化率已提高,在固定增量时,下标形式效率和指针形式相当;但从可读性和代码维护的角度来看,下标形式更优。
示例:
main.c
#include <stdio.h>
int main()
{
//extern int a[];//编译运行没问题
extern int* a;
printf("&a = %p\n", &a);//取四个字节的地址
printf("a = %p\n", a);//取a本身的地址,0x01其实就是a[1]的值
printf("*a = %d\n", *a);//0x01是操作系统使用的地址,段错误。
return 0;
}
ext.c
int a[] = {1, 2, 3, 4, 5};
3.a和&a的区别
a为数组首元素的地址
&a为整个数组的地址
a和&a的区别在于指针运算
4.经典面试题
#include <stdio.h>
int main()
{
int a[5] = {1, 2, 3, 4, 5};
int* p1 = (int*)(&a + 1);
int* p2 = (int*)((int)a + 1);//指向的是a[0]的第二个字节
int* p3 = (int*)(a + 1);
printf("%d, %d, %d\n", p1[-1], p2[0], p3[1]);//5,随机数,3
//p2[0]就是a[0]的后三个字节+a[1]的第一个字节(小端模式)
return 0;
}
5.数组参数
数组作为函数参数时,编译器将其编译成对应的指针
结论:一般情况下,当定义的函数中有数组参数时,需要定义另一个参数来标识数组的大小。
数组作函数参数时,会退化为一个指针。
void func1(char a[5])
{
printf("In func1: sizeof(a) = %d\n", sizeof(a));//打印出来的是4而不是5,说明会退化为一个指针
*a = 'a';
a = NULL;
}
小结:
- 数组和指针仅使用方式相同
- 数组名本质不是指针
- 指针本质不是数组
- 数组名并不是数组的地址,而是数组首元素的地址
- 函数的数组参数退化为指针