首页 最新 热门 推荐

  • 首页
  • 最新
  • 热门
  • 推荐
2025年5月12日 星期一 3:39am

C语言指针超详解——最终篇二

  • 25-03-07 13:21
  • 2935
  • 6654
blog.csdn.net

C语言指针系列文章目录

入门篇
强化篇
进阶篇
最终篇一
最终篇二

文章目录

  • C语言指针系列文章目录
  • 1. sizeof 与 strlen
    • 1.1 字符数组
    • 1.2 二维数组
  • 2. 指针运算笔试题解析


以上接指针最终篇一

1. sizeof 与 strlen

1.1 字符数组

代码三:

#include
int main()
{
	char arr[] = "abcdef";//后面有 \0
	printf("%zd\n", sizeof(arr));		//整个数组的大小				7
	printf("%zd\n", sizeof(arr + 0));	//指向第一个元素的指针		8
	printf("%zd\n", sizeof(*arr));		//第一个元素的大小			1
	printf("%zd\n", sizeof(arr[1]));	//第一个元素的大小			1
	printf("%zd\n", sizeof(&arr));		//指向整个数组的指针			8
	printf("%zd\n", sizeof(&arr + 1));	//跳过整个数组,还是指针		8
	printf("%zd\n", sizeof(&arr[0] + 1));//指向第二个元素的指针		8
	return 0;
}

代码四:

#include
int main()
{
	char arr[] = "abcdef";//后面有 \0
	printf("%d\n", strlen(arr));		//从 arr 开始找 \0			6
	printf("%d\n", strlen(arr + 0));	//从 arr 开始找 \0			6
	printf("%d\n", strlen(*arr));		//传递给 strlen 一个字符		报错
	printf("%d\n", strlen(arr[1]));		//传递给 strlen 一个字符		报错
	printf("%d\n", strlen(&arr));		//从 arr 开始找 \0			6
	printf("%d\n", strlen(&arr + 1));	//跳过整个数组开始找 \0		随机值
	printf("%d\n", strlen(&arr[0] + 1));//从第二个元素开始找 \0		5
	return 0;
}

代码五:

#include
int main()
{
	char* p = "abcdef";//字符串常量,有 \0,p 不是数组名
	printf("%zd\n", sizeof(p));			//指针变量				8
	printf("%zd\n", sizeof(p + 1));		//指向第二个元素的指针	8
	printf("%zd\n", sizeof(*p));		//第一个元素				1
	printf("%zd\n", sizeof(p[0]));		//第一个元素				1
	printf("%zd\n", sizeof(&p));		//数组的地址,指针		8
	printf("%zd\n", sizeof(&p + 1));	//跳过整个数组,指针		8
	printf("%zd\n", sizeof(&p[0] + 1));	//指向第二个元素的指针	8
	return 0;
}

代码六:

#include
int main()
{
	char* p = "abcdef";
	printf("%d\n", strlen(p));			//6
	printf("%d\n", strlen(p + 1));		//5
	printf("%d\n", strlen(*p));			//报错		
	printf("%d\n", strlen(p[0]));		//报错
	printf("%d\n", strlen(&p));			//6
	printf("%d\n", strlen(&p + 1));		//随机值
	printf("%d\n", strlen(&p[0] + 1));	//5
	return 0;
}

1.2 二维数组

#include
int main()
{
	int a[3][4] = { 0 };
	printf("%zd\n", sizeof(a));			
	//整个二维数组的大小,48
	printf("%zd\n", sizeof(a[0][0]));
	//第一行第一个的元素的大小,4
	printf("%zd\n", sizeof(a[0]));
	//第一行元素的大小,a[0]相当于第一行的数组名,16
	printf("%zd\n", sizeof(a[0] + 1));
	//指向第一行元素第二个元素的指针,8
	printf("%zd\n", sizeof(*(a[0] + 1)));
	//第一行第二个元素的大小,4
	printf("%zd\n", sizeof(a + 1));
	//跳过整个数组,指针变量,8
	printf("%zd\n", sizeof(*(a + 1)));
	//跳过第一行,只想第二行的指针变量,相当于数组名,16
	printf("%zd\n", sizeof(&a[0] + 1));
	//跳过第一行,指向第二行的指针变量,8
	printf("%zd\n", sizeof(*(&a[0] + 1)));
	//第二行所有元素的大小,16
	printf("%zd\n", sizeof(*a));
	//指向第一行的数组,相当于数组名,16
	printf("%zd\n", sizeof(a[3]));
	//越界访问,但类型为 int(*)[4],为指针变量,且相当于数组名,16
	return 0;
}

sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小。
&数组名,这里的数组名表示整个数组,取出的是整个数组的地址。
除此之外所有的数组名都表示首元素的地址。

2. 指针运算笔试题解析

注意:这些题有些难度比较高,作为初学者,没搞懂也没关系,最重要的是记下思路!
当然,同时你可能会遇见一些没听说过的概念,欢迎阅读指针系列的前几篇文章。

题目一:

#include 
int main()
{
	int a[5] = { 1, 2, 3, 4, 5 };
	int* ptr = (int*)(&a + 1);
	printf("%d,%d", *(a + 1), *(ptr - 1));
	return 0;
}
//程序的结果是什么?

ptr 是什么是本道题的关键
ptr 是跳过整个数组后,强制类型转换成 int* 的指针变量,可以将它看做 a+5 ,
那么 ptr-1 就是 a+4 ,也就是a[5].
*(a+1)很显然就是数组的第二个元素
所以这道题的输出结果为: 2,5

题目二:

//在X86环境下
//假设结构体的大小是20个字节(至于为什么是 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;
}

我们来分析:
p 这个指针是以 struct Test 的类型指向 0x100000 处的数据。

  1. 0x1 其实就是16进制的 1 ,那么第一行就是 指针变量+常数,跳过指针指向的类型的大小,这里是 20,所以第一行输出应该是 0x100014,注意是16进制的!
  2. 第二个将 p 强制类型转换为 unsigned long ,所以现在参与运算的 p 是一个整数,加上一就是:0x100001。(当然,在这里 VS 编译器会给出警告,我们只分析结果,不考虑它是否合规)
  3. 第三个将 p 强制类型转换为 unsigned int*,那么参与运算的 p 仍然是一个指针,所以依然是 指针变量+常数,跳过指针指向类型的大小 4,所以第三个代码的结果为:0x100004。

题目三:

#include 
int main()
{
	int a[3][2] = { (0, 1), (2, 3), (4, 5) };
	int* p;
	p = a[0];
	printf("%d", p[0]);
	return 0;
}

看到这个代码,你可能下意识地会觉得是 0,但实际上,如果你仔细观察,会发现在第一层大括号里的本应该是大括号的位置被小括号代替了,那这是什么意思呢?
其实这里的()是一个操作符,之前的博客介绍过,它的功能就是改变运算顺序,那这里他改变了什么顺序呢?
()里面的是什么?实际上是逗号表达式,逗号表达式的结果是最后一个操作数,所以实际上这个二维数组 a 存放的数据为:

{1 , 3
 5 , 0
 0 , 0}

而 p[0]指向的地方就是 a 的第一行第一个数据,也就是 1。

题目四:

//假设环境是x86环境,程序输出的结果是什么?
#include 
int main()
{
	int a[5][5];
	int(*p)[4];
	p = a;
	printf("%p,%d\n", &a[4][2] - &p[4][2], &a[4][2] - &p[4][2]);
	return 0;
}

这道题的关键在于:p 是什么?, p 是一个数组指针,指向一个有 4 个元素的 int 类型的数组,
当然这个 p 也可以当做一个一行有 4 个元素的二维数组来看待,(详见进阶篇)那么这就是这道题的关键就在于 a 和 p 的每行的元素个数不一样这一点上了。
a,p的对应关系
那么答案就很简单了,是 00000004,4,指针-指针得到的是指针之间的元素个数。

题目五:

#include 
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;
}

ptr1 和 ptr2 分别指向什么位置是本题的关键。

  1. ptr1 是 &aa+1,&aa 是取出整个数组的地址,指针变量+1 跳过指针指向的类型的大小,这里就是跳过了整个二维数组,指向了二维数组最后一个元素的后一个元素(无论这个位置存放的是什么),也可以理解为是 aa[2][0],那么 ptr1-1就是 arr[1][4],也就是数组的最后一个元素 10。
  2. ptr2 是 *(aa+1),aa 数组名,这里代表第一个元素的地址,二维数组的第一个元素是什么?是第一行数据,所以 aa+1就是 aa[1],aa[1]也是一个数组名,代表首元素地址,对它解引用,得到的就是 a[1][0],也就是 5。

题目六:

#include 
int main()
{
	char* a[] = { "work","at","alibaba" };
	char** pa = a;
	pa++;
	printf("%s\n", *pa);
	return 0;
}

注意这里的 a 是什么。 a 是一个指针数组,a 是一个数组名,数组中存储的元素是 char*,那么我们可以利用 typedef来简化一下这个代码,让它变得更好理解一些。

typedef char* ch;

#include 
int main()
{
	ch a[] = { "work","at","alibaba" };
	ch* pa = a;
	pa++;
	printf("%s\n", *pa);
	return 0;
}

那么这样看就很简单了, a 是一个数组名,指向首元素地址,将 a 的地址存储在 pa 中,pa++就是指向了数组的下一个元素,也就是 at 。

题目七:
本题比较复杂,由于本人讲题的实力有限,如果看不懂建议可以自己画图分析

#include 
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;
}

我们先来分析一个打印之前的内容:

  1. c 是一个指针数组,数组类型为 char* ,存放了 4 个字符串。
  2. cp 是一个指针数组,数组类型为 char**,存放了 4 个指针,分别指向FIRST,POINT,NEW,ENTER这四个字符串。
  3. cpp 是一个指针,类型为char***,指向的是cp[0],也就是 FIRST。

接下来我们分析这 4 个打印语句:

第一句:
++cpp会让它指向 cp[1],也就是 POINT,那么打印的结果就是 POINT。
注意这里 cpp 已经发生了变化,指向了 POINT。

第二句:
*-- * ++cpp + 3,我们首先分析优先级,+ 的优先级最低,最后执行,那么除了 + 外,表达式从右向左指向。
++cpp,改变 cpp 使其指向 NEW,解引用得到的是cp[2],也就是 c+1。
*--上一步得到的是 c + 1,自减得到的是 c(注意这里是把 cp 里的第三个元素修改了,使其指向 c 的第一个元素),再解引用得到就是指向 E 的指针。
+ 3,指针+常数,那么就是跳过 3 个元素,指向的就是 E ,所以打印的结果是 ER。

第三句
*cpp[-2] + 3:
cpp现在指向的是 cp[2],cpp[-2]就是从 cp[2] 的地址往前找两个元素(cpp[-2]就相当于
cpp-2),找到的是 cp[0],也就是 FIRST。
再解引用,找到的是一个指向 F 的指针,再+3,就找到的是 S ,那么打印的结果就是 ST。

第四句
cpp[-1][-1] + 1):
cpp 现在指向的是 cp[2],那么和第三句同理, cpp[-1]指向的就是 cp[1],也就是 POINT。
那么 再使用一次下标操作符,找到的是,POINT 前面的那一个元素,也就是 NEW ,再 +1 ,得到的是 E 的地址,那么打印的结果就是 EW。

谢谢你的阅读,喜欢的话来个点赞收藏评论关注吧!
C语言指针全系列已更新完毕,感谢你的支持

注:本文转载自blog.csdn.net的fhvyxyci的文章"https://blog.csdn.net/fhvyxyci/article/details/140589860"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
复制链接
复制链接
相关推荐
发表评论
登录后才能发表评论和回复 注册

/ 登录

评论记录:

未查询到任何数据!
回复评论:

分类栏目

后端 (14832) 前端 (14280) 移动开发 (3760) 编程语言 (3851) Java (3904) Python (3298) 人工智能 (10119) AIGC (2810) 大数据 (3499) 数据库 (3945) 数据结构与算法 (3757) 音视频 (2669) 云原生 (3145) 云平台 (2965) 前沿技术 (2993) 开源 (2160) 小程序 (2860) 运维 (2533) 服务器 (2698) 操作系统 (2325) 硬件开发 (2492) 嵌入式 (2955) 微软技术 (2769) 软件工程 (2056) 测试 (2865) 网络空间安全 (2948) 网络与通信 (2797) 用户体验设计 (2592) 学习和成长 (2593) 搜索 (2744) 开发工具 (7108) 游戏 (2829) HarmonyOS (2935) 区块链 (2782) 数学 (3112) 3C硬件 (2759) 资讯 (2909) Android (4709) iOS (1850) 代码人生 (3043) 阅读 (2841)

热门文章

101
推荐
关于我们 隐私政策 免责声明 联系我们
Copyright © 2020-2024 蚁人论坛 (iYenn.com) All Rights Reserved.
Scroll to Top