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

news2024/11/15 17:53:48

C语言指针系列文章目录

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

文章目录

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


以上接指针最终篇一

1. sizeof 与 strlen

1.1 字符数组

代码三:

#include<stdio.h>
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<stdio.h>
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<stdio.h>
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<stdio.h>
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<stdio.h>
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 <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;
}
//程序的结果是什么?

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

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

{1 , 3
 5 , 0
 0 , 0}

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

题目四:

//假设环境是x86环境,程序输出的结果是什么?
#include <stdio.h>
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 <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;
}

ptr1ptr2 分别指向什么位置是本题的关键。

  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 <stdio.h>
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 <stdio.h>
int main()
{
	ch a[] = { "work","at","alibaba" };
	ch* pa = a;
	pa++;
	printf("%s\n", *pa);
	return 0;
}

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

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

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

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

  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语言指针全系列已更新完毕,感谢你的支持

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1943122.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

『 Linux 』System V共享内存

文章目录 System V IPCSystem V 共享内存的直接原理System V共享内存的创建挂接共享内存取消挂接共享内存释放共享内存利用System V共享内存进行进程间通信共享内存的特性共享内存的属性通过命名管道为共享内存添加同步互斥机制 System V IPC System v 是一种操作系统和相关技术…

不懂这些,面试都不敢说自己熟悉Redis

点赞再看&#xff0c;Java进阶一大半 下面这位就是Redis的创始人&#xff0c;他叫antirez&#xff0c;让我们Java开发者又要多学一门Redis的始作俑者。 我们肯定很难想象Redis创始人竟然学的是是建筑专业&#xff0c;而当年antirez是为了帮网站管理员监控访问者的实时行为才开发…

22集 如何minimax密钥和groupid-《MCU嵌入式AI开发笔记》

22集 如何获取minimax密钥和groupid-《MCU嵌入式AI开发笔记》 minimax密钥获取 https://www.minimaxi.com/platform 进入minimax网站&#xff0c;注册登录后&#xff0c;进入“账户管理”&#xff0c; 然后再点击“接口密钥”&#xff0c;然后再点击“创建新的密钥”。 之…

linux系统安装python3和pip

一、安装python 1、安装依赖环境 yum install gcc -y yum -y install zlib-devel bzip2-devel openssl-devel ncurses-devel sqlite-devel readline-devel tk-devel gdbm-devel db4-devel libpcap-devel xz-devel yum install zlib zlib-devel openssl -y yum install openssl…

什么是信创沙箱?信创沙箱的原理是什么?

在这个数字化高速发展的时代&#xff0c;信息安全问题愈发显得重要。我们每天都在为数据的安全性、隐私性和完整性操心。有时候&#xff0c;感觉就像是一场没有终点的马拉松。而在这场马拉松中&#xff0c;深信达信创沙箱&#xff08;Trusted Computing Sandbox&#xff09;无疑…

谷粒商城实战笔记-52~53-商品服务-API-三级分类-新增-修改

文章目录 一&#xff0c;52-商品服务-API-三级分类-新增-新增效果完成1&#xff0c;点击Append按钮&#xff0c;显示弹窗2&#xff0c;测试完整代码 二&#xff0c;53-商品服务-API-三级分类-修改-修改效果完成1&#xff0c;添加Edit按钮并绑定事件2&#xff0c;修改弹窗确定按…

Windows 11 家庭中文版 安装 VMWare 报 安装程序检测到主机启用了Hyper-V或Device

1、问题 我的操作系统信息如下&#xff1a; 我在安装 VMWare 的时候&#xff0c;报&#xff1a; 因为我之前安装了 docker 桌面版&#xff0c;所以才报这个提示。 安装程序检测到主机启用了 Hyper-v或 Device/credential Guard。要在启用了Hyper-或 Device/Credential Guard …

如何防止热插拔烧坏单片机

大家都知道一般USB接口属于热插拔&#xff0c;实际任意带电进行连接的操作都可以属于热插拔。我们前面讲过芯片烧坏的原理&#xff0c;那么热插拔就是导致芯片烧坏的一个主要原因之一。 在电子产品的整个装配过程、以及产品使用过程经常会面临接口热插拔或者类似热插拔的过程。…

领夹麦克风哪个品牌好,电脑麦克风哪个品牌好,热门麦克风推荐

​在信息快速传播的时代&#xff0c;直播和视频创作成为了表达与交流的重要方式。对于追求卓越声音品质的创作者而言&#xff0c;一款性能卓越的无线麦克风宛如一把利剑。接下来&#xff0c;我要为大家介绍几款备受好评的无线麦克风&#xff0c;这些都是我在实际使用中体验良好…

emr部署hive并适配达梦数据库

作者&#xff1a;振鹭 一、达梦 用户、数据库初始化 1、创建hive的元数据库 create tablespace hive_meta datafile /dm8/data/DAMENG/hive_meta.dbf size 100 autoextend on next 1 maxsize 2048;2、创建数据库的用户 create user hive identified by "hive12345&quo…

Covalent(CXT)运营商网络规模扩大 42%,以满足激增的需求

Covalent Network&#xff08;CXT&#xff09;是领先的人工智能模块化数据基础设施&#xff0c;网络集成了超过 230 条链并积累了数千名客户&#xff0c;目前 Covalent Network&#xff08;CXT&#xff09;网络迎来了五位新运营商的加入&#xff0c;包括 Graphyte Labs、PierTw…

【BUG】已解决:ModuleNotFoundError: No module named ‘tensorboard‘

ModuleNotFoundError: No module named ‘tensorboard‘ 目录 ModuleNotFoundError: No module named ‘tensorboard‘ 【常见模块错误】 【解决方案】 欢迎来到英杰社区https://bbs.csdn.net/topics/617804998https://bbs.csdn.net/topics/617804998 欢迎来到我的主页&#…

【状态机动态规划 状态压缩】1434. 每个人戴不同帽子的方案数

本文涉及知识点 位运算、状态压缩、枚举子集汇总 动态规划汇总 LeetCode 1434. 每个人戴不同帽子的方案数 总共有 n 个人和 40 种不同的帽子&#xff0c;帽子编号从 1 到 40 。 给你一个整数列表的列表 hats &#xff0c;其中 hats[i] 是第 i 个人所有喜欢帽子的列表。 请你…

SQL实战宝典:快速上手数据库查询与优化

文章目录 SQL 速成手册SQL 的主要功能1、基本查询语句2、表操作语句3、数据操作语句4、函数与聚合操作5、子查询与联接6、高级操作7、性能优化与安全性 基本查询语句表操作语句数据操作语句函数与聚合操作子查询与联接高级操作性能优化与安全性 SQL 速成手册 SQL&#xff08;S…

DataX 本地调试配置

简要说明 根据自己的开发需求&#xff0c;完成了reader、writer、transformer开发后&#xff0c;在ide内通过Engine入口&#xff0c;调试自己的插件和job的json。 前置条件 已在系统安装了datax&#xff0c;本例子是在windows环境下&#xff0c;安装包地址https://github.co…

204、【动态规划】牛客网 ——DP3 跳台阶扩展问题(Python版本)

题目描述 原题链接&#xff1a;DP3 跳台阶扩展问题 解题思路 一个DP问题&#xff0c;相比于普通爬楼&#xff08;只能爬一层或者两层&#xff09;对应的状态函数为 d p [ i ] d p [ i − 1 ] d p [ i − 2 ] dp[i] dp[i - 1] dp[i - 2] dp[i]dp[i−1]dp[i−2]。本题的dp…

什么是倾斜45度的火山图?

一位老师聊起火山图&#xff08;Volcano plot | 别再问我这为什么是火山图 &#xff08;在线轻松绘制&#xff09;&#xff09;&#xff0c;说见过倾斜45度的类似图&#xff0c;可否演示怎么画&#xff1f;想了下&#xff0c;可能是下面这种图&#xff0c;绘起来看看。 检查和安…

零代码GIS场景视效升级:支持TMS/WMS/WMTS协议

首先和大家聊聊为什么现在很多人都在追求GIS场景的视效提升。第一是因为GIS场景本身需要包含多种自然信息&#xff0c;越是优秀的视觉效果&#xff0c;就越能直观反应出真实的地理信息&#xff0c;增强系统的实用性&#xff1b;第二则是因为能够极大降低系统的使用门槛&#xf…

MinIO使用基础教程

MinIO使用基础教程 一、背景二、快速安装2.1 虚拟机安装2.2 Windows安装2.2.1 下载MinIO服务器2.2.2 启动 MinIO Server2.2.3 通过浏览器访问MinIO服务控制台 三、使用介绍3.1 创建存储桶3.2 上传和下载文件3.3 设置文件公开访问 四、实战SpringBoot Minio实现文件上传和查询五…

2024春秋杯网络安全联赛夏季赛Crypto(AK)解题思路及用到的软件

2024春秋杯网络安全联赛夏季赛Crypto(AK) 2024春秋杯网络安全联赛夏季赛Crypto解题思路以及用到的软件 所有题用到的软件 1.vm(虚拟机kali)和Ubuntu&#xff0c;正常配置即可B站有很多。 2.Visual Studio Code(里面要配置python&#xff0c;crypto库和Sagemath数学软件系统S…