前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >指针必刷题(C语言指针就该这么学)【数据结构基础】【C语言指针必刷题】

指针必刷题(C语言指针就该这么学)【数据结构基础】【C语言指针必刷题】

作者头像
see.
发布2024-06-04 12:52:06
1230
发布2024-06-04 12:52:06
举报
文章被收录于专栏:C++破军之路C++破军之路

前言:必备知识回忆

1.数组名的意义    i.sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小

   ii.&数组名,这里的数组名表示整个数组,取出的是整个数组的地址

   iii.除此之外,所有的数组名都表示首元素的地址。

2.数组与指针的关系

对于数组和指针操作来说,a[i] 和 *(a+i) 是等效的,也就是说,a[i] 其实就是 *(a+i) 的简写形式。因此,p[0] 实际上就相当于 *(p+0),它表示 p 所指向的数组的第一个元素的值。

常见的数组和指针用法如下:

  • a[i]:表示数组 a 中的第 i 个元素的值。
  • *(a+i):表示 a+i 所指向的地址中存储的值(也就是 a 数组中第 i 个元素的值)。
  • p[i]:表示指针变量 p 所指向的数组中的第 i 个元素的值。
  • *(p+i):表示 p+i 所指向的地址中存储的值(也就是指针 p 所指向的数组中第 i 个元素的值)。

因此,p[0] 表示的就是指针变量 p 所指向的数组中的第一个元素的值,也就是 *(p+0)。

question one

代码语言:javascript
复制
#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 

 分析:&a取出的是整个数组的地址,&a+1指向了数组后面的地址,(int *)代表强制类型转换,把本身访问权限为20个字节强制转换为只能访问4个字节,因此后面的ptr-1指针指向了5的地址,解引用后得到的结果为5。对于*(a+1),a并没有与&操作符结合,也没有单独放在sizeof内部,因此只是代表首元素的地址,所以解引用后得到2。

question two

代码语言:javascript
复制
#include<stdio.h>
//由于还没学习结构体,这里告知结构体的大小是20个字节
struct Test
{
	int Num;
	char* pcName;
	short sDate;
	char chap[2];
	short sBa[4];
}*p;
//假设p 的值为0x100000。如下表达式的值分别为多少?
//已知,结构体Test类型的变量大小是20个字节
int main()
{
	printf("%p\n",p + 0x1);
	printf("%p\n",(unsigned long)p + 0x1);
	printf("%p\n", (unsigned int *)p + 0x1);
}

打印结果:0x100014                   0x100001 (在 32 位系统中)                   0x100004

分析: 对于表达式p+0x1,指针p 加上0x1 后,会将指针向前移动 20 个字节,即一个Test 类型的大小。因此,表达式的值为 0x100014。对于表达式 (unsigned long)+0x1,指针 p 首先会强制转换为unsigned long类型,然后再加上 0x1,即将指针地址加上 1。因为unsigned long类型大小为 4 字节或 8 字节(取决于系统),所以结果可能有所不同。在 32 位系统中,该表达式的值为 0x100001。对于表达式(unsigned int *)p+ 0x1,指针 p 先转换成unsigned int *类型的指针,然后再加上 0x1,即将指针地址加上 4 个字节(因为unsigned int 类型的大小为 4 个字节)。因此,表达式的值为 0x100004

question three

代码语言:javascript
复制
#include<stdio.h>
int main()
{
	int a[4] = { 1,2,3,4 };
	int* ptr1 = (int*)(&a + 1);
	int* ptr2 = (int*)((int)a + 1);
	printf("%x,%x", ptr1[-1], *ptr2);//注:%x是以十六进制的形式打印 
	return 0;
}

 打印结果:4,2000000

分析: ptr1[-1]可以转化为*(ptr1-1),&a+1取出的是整个数组的地址加一跳过了这个数组,前面的(int*)把这个数组指针强制类型转化为整形指针,所以ptr[-1]后退了四个字节,指针指向了4的前面,解引用得到4。                      地址a前面加上了(int)从一个指针强制转化为了整形,加1跳过了一个字节,而一个整形占四个字节,所以我们要在内存中展开再进行计算。最终得到结果0x02000000。

question four

代码语言:javascript
复制
#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

分析:这里其实存在陷阱,在初始化该二维数组时,花括号里面采用了逗号表达式进行初始化,而不是直接赋值,因此第一行代码可以转化为:int a[3][2]={ 1 , 3 , 5 };  题目中a[0]代表数组第一行的数组名,既没有&,也没有直接放在sizeof内部,因此这里就代表首元素1的地址,即把1的地址交给了指针变量p。p[0]可以转换为*(p+0),因此结果为1。

question five

代码语言:javascript
复制
#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的首元素地址放在了指针数组p中,也就是第一行的地址a[0],但是第一行如果完整存储在指针数组中需要的是int (*p)[5],虽然虽然类型有差异,但是还是可以赋过去,地址都是指向数组的起始位置。它的访问权限是16个字节,所以p+1跳过4个整形,以此类推……

p[4]可以转化为*(p+4),p[4][2]可以转化为*(*(p+4)+2).

由前面指针相减的知识可以知道,指针减指针结果为指针之间元素的个数,通过画图我们清晰的可以看到,两指针之间相差四个元素。又因为随着数组下标增长,地址由低到高变化,题目中是低地址减高地址,所以结果为负,得到-4,所以通过%d打印得到-4.    -4在内存中以补码的形式存储,二进制位表示为:11111111111111111111111111111100,以%p形式打印时为十六进制形式,转化为十六进制就是:FFFFFFFC

question six

代码语言:javascript
复制
#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

分析:&arr取出的是整个数组的地址,加一就是跳过了整个数组,通过(int *)强制类型转换,使其只能访问四个字节,所以*(ptr1-1)结果为10。        *(aa+1)可以看作aa[1],也就是第二行数组的数组名,数组名既没有直接&,又没有放在sizeof内部,所以代表数组首元素的地址,就算没有前面的强制类型转换,其也是(int *)类型,所以*(ptr2-1)最终结果为5。

question seven

代码语言:javascript
复制
#include<stdio.h>
int main()
{
	char* a[] = { "work","at","alibaba" };
	char** pa = a;
	pa++;
	printf("%s\n", *pa);
	return 0;
}

运行结果:at

分析:数组a是一个指针数组,数组的每个元素都是char* ,这里pa = a ;把数组a首元素的地址(即“work”的首字母w)放在了二级指针pa中,pa++后,指针指向了数组a的第二个元素(即“at”的首字母a)的地址,最后通过解引用pa,找到了a的地址,当使用 printf("%s", *pa) 打印 *pa 的内容时,它会从a的地址开始输出字符,直到遇到字符串结束符\0。因此最终结果为“at”。

question eight

代码语言:javascript
复制
#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

分析:解决这道题的方法就是画图,通过画图梳理表达式之间的关系,关系理清了,注意不要混淆,答案也就出来了。 ++操作符的优先级比*高,所以++cpp后,cpp就指向cp的第二个元素通过两次解引用,得到单词“POINT”的首字母P的地址,通过打印可以直接打印出来POINT,第一问得解。                      第二问:++cpp后,cpp指向了数组cp的第三个元素,通过解引用找到了指向字符"NEW"首字母的指针,又通过--操作,该指针又指向了"ENTER"首字符的地址,解引用后得到了“ENTER”首字符的地址,然后又+3,指针往后跳了三个字符,得到打印结果:ER。                        后面两问也是同样的道理,依次计算即可。


通过这部分的指针练习,你对指针的理解肯定更加深刻了,快去做个思维导图总结一下指针的内容吧,指针是数据结构的重要基础,一定不能草草放过去~

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2023-12-17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • question one
  • question two
  • question three
  • question four
  • question five
  • question six
  • question seven
  • question eight
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档