前言(C语言与指针,指针与题?)
提示:这里可以添加本文要记录的大概内容:
指针,是C语言的灵魂所在,而题目,是掌握指针的关键,下面通过精选几道题目,带同学深入理解指针精妙之处~
提示:以下是本篇文章正文内容,下面案例可供参考
T1:
#include <stdio.h> int main() { int a[5] = { 1, 2, 3, 4, 5 }; int* ptr = (int*)(&a + 1); printf("%d,%d", *(a + 1), *(ptr - 1)); return 0; } //程序的结果是什么?
题解:(下面通过图文解析来刨析本题目)
答案:2,5
为了便于大家理解,特地给大家作了图为大家参考:
1.首先,代码创建了一个每个元素为int类型的,名字叫做a,的一维数组,共计5个元素
2.之后代码(取地址数组名+1)这个取地址数组名怎么理解?这是整个数组的地址,这个取地址数组名的类型是int ✳[5],加一会跳过整个数组,地址来到数字5后面的内存空间,之后再将这个地址强制类型转换为int*类型
3.最后打印:第一个✳(a+1)是数组名加1,数组名是数组首元素的地址,其类型是int类型,加一会跳过一个int类型的长度空间,来到指向数字2的地址,解引用这个指向数字2的地址,会得到数字2,同理,ptr因为被强制转换为int类型,所以str-1会跳过一个int类型大小的空间,指向数字5的空间,解引用拿到数字5
T2:
//在X86环境下 //假设结构体的⼤⼩是20个字节 //程序输出的结构是啥? struct Test { int Num; char* pcName; short sDate; char cha[2]; short sBa[4]; }*p = (struct Test*)0x100000; int main() { printf("%p\n", p + 0x1); printf("%p\n", (unsigned long)p + 0x1); printf("%p\n", (unsigned int*)p + 0x1); return 0; }
答案:0x100014,0x10001,0x10004;
同样,为了便于大家理解,我们以图的形式对main函数之前的代码加以解析,再以文字的形式简单介绍一下后面的代码含义:
之后,我们再来分析main函数中的打印函数:
1.p+0x1,0x1是一个十六进制数字,其值还是数字1,p是一个指针变量,是一个地址,一个指针加整数,跳过多大内存取决于变量p的类型的内存大小,我们从题目中可以看到,该结构体大小为20,所以跳过20个字节,得到地址:0x100014;
2.剩下两个的话,还是同样的道理,,但是需要注意:指针p加一个数字1跳过的字节取决于p类型的大小,但是一个数字加上一个数字是几就是几… p被强转为无符号longlong类型,,属于整形类型,不是指针!!!所以跳过1个字节到答案0x10001;
3.第三个跟第一个同理,指针加整数跳过几个字节取决于p指针的类型,int是4个字节,加一就是加一个int类型大小,跳过4个字节,得到答案0x10004。
T3:
代码如下(示例):
#include <stdio.h> int main() { int a[3][2] = { (0, 1), (2, 3), (4, 5) }; int* p; p = a[0]; printf("%d", p[0]); return 0; }
答案:1
解析:
这个题目,不知道有没有同学进坑呢?以为二维数组的存储方式是这样的:
但是,因为里面是逗号表达式,小括号里面的数字取最后一个,所以是这样滴:
1.int*p;这是创建了一个指向int类型的指针变量p;
2.之后有同学就奇怪了,怎么好像把第一行给p了???我们把二维数组首元素a【0】也就等同于一维数组a【0】第一行的名称,数组名称又等同于一维数组a【0】首元素的地址,也就是把数字1的地址给了p!!!
3.我们打印的时候,p【0】可以理解为✳(p+0),所以说顺利解引用拿到了数字1并进行打印~
T4:
//假设环境是x86环境,程序输出的结果是啥? #include <stdio.h> int main() { int a[5][5]; int(*p)[4]; p = a; printf("%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]); return 0; }
答案:FFFFFFFC,-4
解析:
本题的主要奇怪的地方呢是,数组a是由5个一维数组int【5】组成的,但是创建变量接收的时候,变量的类型是int*【4】的类型,我们下面通过画图来进一步理解:
我们知道:
&p[4][2]可以等价于&✳(✳(p+4)+2)
&a[4][2]可以理解为&✳(✳(a+4)+2)
那么放在咱们数组中,可以得出以下图表:
1.指针减指针,得到的是两者间的元素个数,所以说呢,第二个空答案是-4(因为是小地址-大地址)
2.至于第一个空,因为要打印地址,是按照16进制形式进行打印,并且编译器先把-4转变成为补码,-4变成11111111111111111111111111111100,而对于地址没有反码补码的概念,编译器将这个-4的补码直接转换为地址,变成FFFFFFFC,并打印出来~
T5:
#include <stdio.h> int main() { int aa[2][5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; int* ptr1 = (int*)(&aa + 1); int* ptr2 = (int*)(*(aa + 1)); printf("%d,%d", *(ptr1 - 1), *(ptr2 - 1)); return 0; }
答案:10,5
解析:
我们通过画图的形式,来进一步展示本题目中的逻辑关系,请仔细看图:
为什么ptr1-1往前走一个元素大小呢?因为被强转为了int*类型; str2也是一样的道理。
T6:
#include <stdio.h> int main() { char* a[] = { "work","at","alibaba" }; char** pa = a; pa++; printf("%s\n", *pa); return 0; }
答案:at
解析:
首先,
我们解引用pa,拿到的是a+1的内容,a+1的内容是一个地址,我们打印字符串的时候就是需要字符串首字母的地址,遇到\0停止打印,这样顺利打印at;
T7:
代码如下:
#include <stdio.h> int main() { char* c[] = { "ENTER","NEW","POINT","FIRST" }; char** cp[] = { c + 3,c + 2,c + 1,c }; char*** cpp = cp; printf("%s\n", **++cpp); printf("%s\n", *-- * ++cpp + 3); printf("%s\n", *cpp[-2] + 3); printf("%s\n", cpp[-1][-1] + 1); return 0; }
答案:POINT ER ST EW
解析:前面已经做过了六道指针题目,相比大家对于指针以及指针类型理解已经有了一定的认知,不妨最后一题我稍作解释,留下一部分空间供大家发挥:
总结(指针的理解)
本文以题目为切入点,举了7道典型题目,分别考究了对于指针的不同角度理解。
希望同学们对指针的理解能够有更深入的理解,加深理解~