问题:
数组名可以当作常量指针使用,那么指针是否也可以当作数组名使用?
下标形式访问元素
int a[10] = {0};
a[1] = 3;
a[2] = 5;
指针形式访问元素
int a[10] = {0};
*(a+1) = 3;
*(a+2) = 5;
下标形式 VS 指针形式
- 指针以固定增量在数组中移动,效率高于下标形式
- 指针增量为 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;
}
运算结果:
a[0] = 1
a[1] = 2
a[2] = 3
a[3] = 4
a[4] = 5
p[0] = 11
p[1] = 12
p[2] = 13
p[3] = 14
p[4] = 15
分析:
指针可以当作数组名使用,能否成功的用上取决与这个指针是否指向合法的数组,上述代码指针就是指向合法的数组
数组和指针不同案例分析
// flie1.c
#include <stdio.h>
int main()
{
//extern int* a;
extern int a[];
printf("&a = %p\n", &a);
printf("a = %p\n", a);
printf("*a = %d\n", *a);
return 0;
}
// file2.c
int a[] = {1, 2, 3, 4, 5};
&a = 0x601040
a = 0x601040
*a = 1
上述输出了期望的结果,下面对程序稍作修改
// flie1.c
#include <stdio.h>
int main()
{
extern int* a;
// extern int a[];
printf("&a = %p\n", &a);
printf("a = %p\n", a);
printf("*a = %d\n", *a);
return 0;
}
// file2.c
int a[] = {1, 2, 3, 4, 5};
在64位机器运行结果:
&a = 0x601040
a = 0x200000001
Segmentation fault (core dumped)
原因分析
- 声明一个标识符a,已经在 file2.c 中定义了,此时标识符a 有了内存地址值了(即标识符a在编译后转化为地址值(0x601040 )
- 在(0x601040 )对应内存上去取指针的内容一个字节,对应的值为0x1
- 取指针的内容上的值,即访问0x1的地址值上的内容,所以会产生错误。
小小结:
在某些场景下数组名可以看作指针,指针又可以当作数组使用。但有些场景(数组就是数组,指针就是指针)
补充:
内存中是没有变量名和标识符的,所以编译之后变量名和表示符都变成对应的地址值
a 和 &a 的区别
- a为数组首元素的地址
- &a为整个数组的地址
- a 和 &a 的区别在于指针运输
a+1 >>>>>>>>> (unsigned int)a+sizeof(*a)
&a+1 >>>>>>>>> (unsigned int )a +sizeof(*&a)
>>>>>>>>> (unsigned int)a + sizeof(a)
siezeof(a) 表示整个数组的长度,所以递增的长度不一样
指针运算经典题目
#include <stdio.h>
int main()
{
int a[5] = {1, 2, 3, 4, 5};
int* p1 = (int*)(&a + 1); // p1[-1] >>> *(p1 - 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;
}
数组参数
数组作为函数参数时,编译器讲其编译成对应的指针(函数的的数组参数退化为指针
void f(int a[]) --->>> void f(int* a)
void f(int a[5]) --->>> void f(int* a)
一般情况下,当定义的函数中有数组参数时,需要定义另一个参数来标示数组的大小
虚幻的数组参数
#include <stdio.h>
void func1(char a[5])
{
printf("In func1: sizeof(a) = %d\n", sizeof(a));
// 如果为数组参数时,这边的大小应该为 5
*a = 'a';
a = NULL; // 如果参数函数数组参数的时候这边会错误
}
void func2(char b[])
{
printf("In func2: sizeof(b) = %d\n", sizeof(b));
*b = 'b';
b = NULL; // 如果参数函数数组参数的时候这边会错误
}
int main()
{
char array[10] = {0};
func1(array);
printf("array[0] = %c\n", array[0]);
func2(array);
printf("array[0] = %c\n", array[0]);
return 0;
}
输出结果
In func1: sizeof(a) = 8
array[0] = a
In func2: sizeof(b) = 8
array[0] = b
小结
- 数组名和指针在一些情况下使用方式相同,但是始终要明确
- 数组名的本质不是指针
- 指针的本质不是数组
- 数组名并不是数组的地址,而是数组首元素的地址
- 函数的数组参数退化为指针