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

发布时间:2023年12月17日

前言:必备知识回忆

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

#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

#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

#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

#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

#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

#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

#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

#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。? ? ? ? ? ? ? ? ? ? ? ? 后面两问也是同样的道理,依次计算即可。


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

文章来源:https://blog.csdn.net/2301_79241364/article/details/135041638
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。