原文首发于同名微信公号「Allen5G」,欢迎大家搜索关注,欢迎转发!
数组的本质:
- 数组是一段连续的内存空间
- 数组的空间大小为sizeof(array_type)*array_size
- 数组名可以看做指向数组的第一个元素的常量指针(只是理解,其实是错的,参见《征服C指针》)
实验1:a+1的结果是什么?
#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; }
理解:
当指针p指向一个同类型的数组的元素时:p+1将指向当前元素的下一个元素:p-1指向当前元素的上一个元素
指针的运算:
- 指针之间只支持减法运算
- 参与运算的指针类型必须相同
理解:
只有当两个指针指向同一个数组中的元素时,指针相减才有意义,其意义是指针所指元素的下标差
当两个指针指向的元素不在同一个数组中时,结果未定义
指针的比较:
指针可以进行关系运算(< , <= , > , >=)
指针关系运算的前提是同时指向同一个数组中的元素
任意两个指针之间的比较运算(==,!=)无限制
参与比较运算的指针类型必须相同
实验2:指针运算
#include <stdio.h> int main() { char s1[] = {'H', 'e', 'l', 'l', 'o'}; int i = 0; char s2[] = {'W', 'o', 'r', 'l', 'd'}; char* p0 = s1; char* p1 = &s1[3]; char* p2 = s2; int* p = &i; printf("%d\n", p0 - p1); printf("%d\n", p0 + p2); printf("%d\n", p0 - p2); printf("%d\n", p0 - p); printf("%d\n", p0 * p2); printf("%d\n", p0 / p2); return 0; }
实验3:指针运算的应用
#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); // Key point 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; }
数组的访问方式有两种
- 下标的方式
- 指针的形式
分析下两种方式的不同:(下标形式对于阅读代码更优)
- 指针一固定增量在数组中移动时,效率高于下边形式
- 指针增量为1且硬件具有硬件增量模型时,效率更高
- 下标形式与指针形式的转换:
实例4:数组的访问方式
#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; }
实验5:数组与指针的差别
#include <stdio.h> int main() { //extern int* a; int a[] = {1, 2, 3, 4, 5}; printf("&a = %p\n", &a); printf("a = %p\n", a); printf("*a = %d\n", *a); return 0; } 、、、、、、、、、、、、、、、、、 int a[] = {1, 2, 3, 4, 5};
a和&a的区别
- a为数组首元素的地址
- &a为整个数组的地址
- a和&a的区别在于指针运算
实例6:指针运算的经典问题
#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
数组函数:数组作为函数参数时,编译器将其编译为对应的指针
实例7:数组参数探究
#include <stdio.h> void func1(char a[5]) { printf("In func1: sizeof(a) = %d\n", sizeof(a)); *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; }
小结: