目录
一、指针和数组分析-上1.数组的本质2.指针的运算3.指针的比较4.小结二、指针与数组分析-下1.数组的访问方式2.下标形式 VS 指针形式3.a 和 &a 的区别4.数组参数5.小结一、指针和数组分析-上
1.数组的本质
数组是一段连续的内存空间数组的空间大小为 sizeof(array_type) * array_size数组名可看做指向数组第一个元素的常量指针下面看一段代码:
#includeint 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; }
输出结果如下:
通过这段代码说明指针运算是合法的。
2.指针的运算
指针是一种特殊的变量,与整数的运算规则为
p + n; <-->(unsigned int)p + n*sizeof(*p);
结论∶
当指针 p 指向一个同类型的数组的元素时:p+1 将指向当前元素的下一个元素;p-1 将指向当前元素的上一个元素。
指针之间只支持减法运算参与减法运算的指针类型必须相同p1- p2; <--> ((unsigned int)p1 - (unsigned int)p2) / sizeof(type);
注意:
只有当两个指针指向同一个数组中的元素时,指针相减才有意义,其意义为指针所指元素的下标差当两个指针指向的元素不在同一个数组中时,结果未定义下面看一段简单的指针运算代码:
#includeint 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); //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; }
输出结果如下:
注意两个指针指向不同的数组,虽然它们两相减符合语法,但是最后的结果肯定没有意义。
再来看一段指针运算的应用代码:
#include#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 输出结果如下:
注意以下几点:
数组大小的计算方法:#define DIM(a) (sizeof(a) / sizeof(*a))
char* pEnd = s + DIM(s); // Key point ==> pEnd 指向 "o" 后面的地址 ==>
这在 C 语言中是一个擦边球的边界位置,也是一个技巧,在这个边界位置可以认为该指针是合法的,可以和其他指针进行比较运算和减法运算等,在 C++ 标准库里面也合法3.指针的比较
指针也可以进行关系运算 (<,<=,>,>=)指针关系运算的前提是同时指向同一个数组中的元素任意两个指针之间的比较运算(==,!=)无限制参与比较运算的指针类型必须相同4.小结
数组声明时编译器自动分配一片连续的内存空间指针声明时只分配了用于容纳地址值的 4 字节空间指针和整数可以进行运算,其结果为指针指针之间只支持减法运算,其结果为数组元素下标差指针之间支持比较运算,其类型必须相同二、指针与数组分析-下
1.数组的访问方式
以下标的形式访问数组中的元素
以指针的形式访问数组中的元素
2.下标形式 VS 指针形式
指针以固定增量在数组中移动时,效率高于下标形式指针增量为1且硬件具有硬件增量模型时,效率更高下标形式与指针形式的转换a[n] <--> *(a +n) <--> *(n + a) <--> n[a]
注意:现代编译器的生成代码优化率已大大提高,在固定增量时,下标形式的效率已经和指针形式相当;但从可读性和代码维护的角度来看,下标形式更优。
下面看一个数组的访问方式代码:
#includeint 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; } 输出结果如下:
注意这个奇怪的写法:
i[a] = i + 10; ==> a[i] = i + 10;
下面通过一个实例,说明数组和指针的不同:
ext.c:
int a[] = {1, 2, 3, 4, 5};test.c:
#includeint main() { extern int a[]; printf("&a = %p\n", &a); printf("a = %p\n", a); printf("*a = %d\n", *a); return 0; } 输出结果如下:
下面来验证一下数组名究竟是不是指针,将 test.c 改成:
#includeint main() { extern int* a; printf("&a = %p\n", &a); printf("a = %p\n", a); printf("*a = %d\n", *a); return 0; } 输出结果如下:
ext.c 中 a[ ] 的地址为 0x804a014,test.c 中的extern int* a; 只是申明标识符 a,编译器会认为在这之前就已经给了地址值,就是 0x804a014,所以printf("a = %p\n", a); 就是打印0x804a014 地址中的 4 个字节的数,也就是 a[ ] 数组中的第一个元素 1,所以打印 0x1,*a 就是取 0x1 地址中的数,但是这个地址值是留给操作系统的,不可访问,访问就会产生段错误。
3.a 和 &a 的区别
a 为数组首元素的地址&a 为整个数组的地址a 和 &a 的区别在于指针运算这个就能看出 a + 1 和 &a + 1 的不同,a + 1 增加的步长是一个元素的大小,&a + 1 则是增加的步长是整个数组的大小。
下面看一个指针运算的经典问题:
#includeint 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; } 输出结果如下:
p1[-1] 就是 *(p1 - 1),由于 p1 指向的元素是 5 后面的位置,减 1 之后就指向了 5;p2 的地址是 0x804a015(注意 linux 系统为小端系统),*p2 就是 0x02000000,对应十进制的值就是 33554432;p3 的地址为 &a[1],所以 p3[1] 就是 3 了。
4.数组参数
数组作为函数参数时,编译器将其编译成对应的指针
结论:一般情况下,当定义的函数中有数组参数时,需要定义另一个参数来标示数组的大小。
下面看一段代码:
#includevoid 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; } 输出结果如下:
这段代码就说明数组参数退化成指针,因为 sizeof(a) 为 4 个字节,而不是 5 个字节。
5.小结
数组名和指针仅使用方式相同
数组名的本质不是指针指针的本质不是数组数组名并不是数组的地址,而是数组首元素的地址
函数的数组参数退化为指针
到此这篇关于C语言零基础讲解指针和数组的文章就介绍到这了,更多相关C语言 指针和数组内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
下一篇:C#实现多线程编程的简单案例
X 关闭
X 关闭
- 15G资费不大降!三大运营商谁提供的5G网速最快?中国信通院给出答案
- 2联想拯救者Y70发布最新预告:售价2970元起 迄今最便宜的骁龙8+旗舰
- 3亚马逊开始大规模推广掌纹支付技术 顾客可使用“挥手付”结账
- 4现代和起亚上半年出口20万辆新能源汽车同比增长30.6%
- 5如何让居民5分钟使用到各种设施?沙特“线性城市”来了
- 6AMD实现连续8个季度的增长 季度营收首次突破60亿美元利润更是翻倍
- 7转转集团发布2022年二季度手机行情报告:二手市场“飘香”
- 8充电宝100Wh等于多少毫安?铁路旅客禁止、限制携带和托运物品目录
- 9好消息!京东与腾讯续签三年战略合作协议 加强技术创新与供应链服务
- 10名创优品拟通过香港IPO全球发售4100万股 全球发售所得款项有什么用处?