1 回顾–数组的本质
在之前的文章,已经学习了数组的本质分析。下面再来回顾一下:
- 数组是一段连续的内存空间
- 数组名可以看做是指向数组第一个元素的常量指针
那么数组名加1的意义是什么?指针运算呢?
看一下代码:
- 28-1.c
#include <stdio.h>
int main()
{
int a[5] = {0};
int* p = NULL;
printf("a = 0x%X\n", (unsigned int)(a));
printf("a + 1 = 0x%X\n", (unsigned int)(a + 1));
printf("p = 0x%X\n", (unsigned int)(p));
printf("p + 1 = 0x%X\n", (unsigned int)(p + 1));
return 0;
}
- 编译运行的结果如下:
从以上的运行结果来看:
- 数组名加1得到的结果是数组元素首地址的值,加上一个元素类型的大小。如上代码是数组首元素地址加上int类型的大小4
- 对于指针的运算:指针是一种特殊的变量,与整数的运算规则与数组名很类似,如下图公式(指针p):
当指针p指向一个同类型的数组时:
- p+1 将指向当前元素的下一个元素
- p-1 将指向当前元素的上一个元素
2 指针的运算
下面两点非常重要,务必记住;
- 指针之间只支持减法运算。其他运算例如加法是不支持的
- 参与减法运算的指针类型必须相同
注意,指针虽然支持减法运算,但是:
- 只有当两个指针指向同一个数组中元素的时候,指针相减才有意义。其意义在于指针所指向元素的下标差
- 当两个指针所指向元素不在同一个数组中时,指针相减结果未定义。
2.1 指针运算代码案例分析
- 代码28-2.c
#include <stdio.h>
int main(){
char s1[] = {'H','e','l','l','o'};
int i = 0;
char* p0 = s1; //相当于 cgar* p0 = &s1[0];
char* p1 = &s1[3];
char s2[] = {'W','o','r','l','d'};
char* p2 = s2;
int* p = &i;
printf("%d\n", p0 - p1);
//printf("%d\n", p0 + p2); //error
printf("%d\n", p0 - p2);
//printf("%d\n", p0 - p); //error
//printf("%d\n", p0 * p2); //error
//printf("%d\n", p0 / p2); //error
return 0;
}
- 编译运行结果为:
-3
5
- 上述代码注释掉的部分编译会出错。说明指针不支持除减法以外的运算
- 减法运算的意义在于指针所指向元素的下标差
- 不是指向同一个数组元素的指针相减,结果未定义。如上面的p0-p2=5,这的结果是未定义的.
3 指针的比较
- 指针也可以进行关系运算(<,<=,>,>=)
- 指针的关系运算,只有在指针指向的数组元素在同一个数组内才有意义。否则虽然编译不会报错,但是没有什么意义。
- 参与比较运算的指针类型必须相同,否则编译会给出警告。且这种比较也没有什么意义。
3.1 指针运算的应用代码案例分析
- 28-3.c
#include <stdio.h>
#define DIM(a) sizeof(a) / sizeof(*a)
int main(){
char s[] = {'H','e','l','l','o'};
char* pBegin = s;
char* pEnd = s + DIM(s);
char* p = NULL;
printf("pBegin = %p\n", pBegin);
printf("pEnd = %p\n", pEnd);
printf("Size = %d\n", pEnd - pBegin);
for(p=pBegin; p<pEnd; p++){
printf("%c", *p);
}
printf("\n");
return 0;
}
- 编译运行结果为:
以上代码比较简单,这里就不再分析了。
4 总结
- 数组声明时,编译器自动为其分配一片连续的内存空间
- 指针声明时,只为指针分配了容纳地址值的4字节空间(32位系统)。这足以看出数组并不等于指针,两者差异很大
- 指针可以和整数进行运算,其结果仍然为指针
- 两个指向同一个数组内元素的指针的减法运算,代表数组元素的下标差
- 指针之间的比较运算需要指针的类型相同。