【C语言】指针练习篇(上),深入理解指针---指针和数组练习题和sizeof,strlen的对比【图文讲解,详细解答】
欢迎来CILMY23的博客喔,本期系列为【C语言】指针练习篇(上),深入理解指针---指针数组练习题和sizeof,strlen的对比【图文讲解,详细解答】,图文讲解指针和数组练习题,带大家更深刻理解指针的应用,感谢观看,支持的可以给个赞哇。
前言
作为指针系列的番外练习篇,本篇主要以数组练习题为主,本期博客将用sizeof和strlen的对比开头,并且以做题的视角带入,进行深刻理解指针练习题中不同用法区别。
目录
一、sizeof和strlen的对比
1.1sizeof操作符
1.2strlen
1.3表格总结
1.4sizeof题目
二、指针,数组笔试题
2.1一维数组
2.2 字符数组
一、sizeof和strlen的对比
1.1sizeof操作符
sizeof 计算变量所占内存内存空间大小的,单位是字节,如果操作数是类型的话,计算的是使用类型创建的变量所占内存空间的大小。sizeof 只关注占用内存空间的大小,不在乎内存中存放什么数据,并且在计算变量的时候,括号可以省略。额外说明size_t就是为sizeof设计的。
就比如以下代码:
#include<stdio.h>int main()
{int a = 23;printf("%d\n", sizeof(a));printf("%d\n", sizeof a);printf("%d\n", sizeof(int));return 0;
}
我们可以看到,结果都是4。
1.2strlen
strlen是C语言库函数,函数功能原型如下:
size_t strlen ( const char * str );
strlen字符串长度统计的是从形参str中这个地址开始向后,\0之前字符串中字符的个数。如果没有找到\0,那么strlen函数会在内存中⼀直向后找\0字符,直到找到\0为止,所以可能存在越界查找。
就比如下面这段代码:
#include<stdio.h>
#include<string.h>int main()
{char arr[] = "SILMY23";char arr1[] = { 'S','I','L','M','Y' };printf("%d\n", strlen(arr));printf("%d\n", strlen(arr1));return 0;
}
由于前面有\0,后面没有\0,那么第二个原本的长度应该是五,但存在越界查找,所以长度必然不为5.
结果如下:
1.3表格总结
sizeof | strlen | |
性质 | 操作符 | 是库函数,使用需要包括头文件#include<string.h> |
功能 | 计算操作数所占内存的大小,单位是字节 | 求字符串长度 |
特点 | 不关注内存中存放什么数据 | 关注内存中是否有\0,如果没有\0 ,就会持续往后找,可能会越界查找 |
1.4sizeof题目
请思考以下代码,L和S的值:
#include<stdio.h>int main()
{short S = 4;int I = 2;int L = sizeof(S = I + 23);printf("%d \n", L);printf("%d \n", S);return 0;
}
结果如下:
在上述代码中,S=I+23其实是没有参与计算的,因为在sizeof中的操作数如果是一个表达式,表达式就不参与计算,所以最后的S还是4。其次,sizeof计算其实是按照操作类型来推理的,比如上述中S是short两个字节的短整型,所以L是两个字节。
二、指针,数组笔试题
2.1一维数组
如果你不理解数组名可以跳转至http://t.csdnimg.cn/6zjW4,观看第二点
int a[] = { 1,2,3,4 };printf("%d\n", sizeof(a));printf("%d\n", sizeof(a + 0));printf("%d\n", sizeof(*a));printf("%d\n", sizeof(a + 1));printf("%d\n", sizeof(a[1]));printf("%d\n", sizeof(&a));printf("%d\n", sizeof(*&a)); printf("%d\n", sizeof(&a + 1));printf("%d\n", sizeof(&a[0]));printf("%d\n", sizeof(&a[0] + 1));
这是在32位环境和64位环境下的结果
2.1解析:
printf("%d\n", sizeof(a));
a单独放在sizeof后面是整个数组,计算的是整个数组的大小,所以大小为16
printf("%d\n", sizeof(a + 0));a+0,a没有单独放在sizeof里面,也没有&,所以是数组首元素地址,既然是数组首元素地址那么,a+0 == a,是地址,大小就为4/8字节。
printf("%d\n", sizeof(*a));a没有单独存放在sizeof后面,所以是数组首元素地址,解引用,*a == a[0],所以计算的是数组第一个元素的大小,即为4个字节
printf("%d\n", sizeof(a + 1));a + 1同理上面的 a+0 即为4/8字节
printf("%d\n", sizeof(a[1]));a[1] == *(a + 1),计算的是数组第二个元素的大小,即为4个字节
printf("%d\n", sizeof(&a));&a, 因为a前面有&操作符,所以这里取出的是整个数组的地址,既然为地址,那么就为4/8字节
printf("%d\n", sizeof(*&a));把整个数组的地址解引用后,sizeof计算的是整个数组的大小,即为16个字节
printf("%d\n", sizeof(&a + 1));&a + 1,计算的是跳过数组大小的地址,是地址就为4/8字节
printf("%d\n", sizeof(&a[0]));&a[0],表示取出的是数组首元素的地址,大小为4/8字节
printf("%d\n", sizeof(&a[0] + 1));&a[0] + 1表示的是数组第二个元素的地址,大小为4/8字节
2.2 字符数组
2.2.1
char arr[] = {'S','I','L','M','Y'};printf("%d\n", sizeof(arr));printf("%d\n", sizeof(arr + 0));printf("%d\n", sizeof(*arr));printf("%d\n", sizeof(arr[1]));printf("%d\n", sizeof(&arr));printf("%d\n", sizeof(&arr + 1));printf("%d\n", sizeof(&arr[0] + 1));
结果如下:
2.2.1解析:
printf("%d\n", sizeof(arr));
arr单独放在sizeof后面,计算整个数组大小,所以为5个字节
printf("%d\n", sizeof(arr + 0));arr+0表示是数组首元素地址,是地址就为4/8字节
printf("%d\n", sizeof(*arr));*arr将数组首元素地址解引用,sizeof计算首元素的大小,大小为1字节
printf("%d\n", sizeof(arr[1]));arr[1]表示数组第二个元素,sizeof计算第二个元素大小,大小为1字节
printf("%d\n", sizeof(&arr));&arr表示取出整个数组的地址,是地址,大小为4/8字节
printf("%d\n", sizeof(&arr + 1));&arr + 1 表示跳过数组大小,指向数组末尾后面的地址,大小为4/8字节
printf("%d\n", sizeof(&arr[0] + 1));&arr[0] + 1 表示数组第二个元素的地址,是地址,大小为4/8字节
2.2.2
char arr[] = { 'S','I','L','M','Y' };printf("%d\n", strlen(arr));printf("%d\n", strlen(arr + 0));printf("%d\n", strlen(*arr));printf("%d\n", strlen(arr[1]));printf("%d\n", strlen(&arr));printf("%d\n", strlen(&arr + 1));printf("%d\n", strlen(&arr[0] + 1));
32位结果如下:
64位结果如下:
2.2.2解析
printf("%d\n", strlen(arr));
arr表示数组首元素地址,因为数组中没有明确给出字符\0,所以会导致strlen在内存中不断寻找,直到找到第一个\0 为止,因此是随机值
printf("%d\n", strlen(arr + 0));arr+0也表示数组首元素地址,因为没有\0,所以和第一种情况一样,也是随机值
printf("%d\n", strlen(*arr));
*arr,arr是数组首元素地址,解引用后得到是S,S对应的ASCII码值是83,strlen把八十三当成一个地址,造成非法访问,所以错误
printf("%d\n", strlen(arr[1]));arr[1]表示数组第二个元素,I对应的ASCII码值是73,同样的也是非法访问。
printf("%d\n", strlen(&arr));&arr表示取出整个数组的地址,从arr的首元素开始算起,直到找到第一个\0为止,所以是随机值
printf("%d\n", strlen(&arr + 1));&arr +1 表示跳过数组大小的地址,也就是从Y后面开始算起,同样因为没有\0,所以会是一个随机值,但它和&arr会相差五个元素,一个数组大小。
printf("%d\n", strlen(&arr[0] + 1));&arr[0] + 1 表示从数组第二个元素开始,因为没有\0,所以也会是一个随机值,但它和&arr相比,会差一个元素
2.2.3
char arr[] = "SILMY23";printf("%d\n", sizeof(arr));printf("%d\n", sizeof(arr + 0));printf("%d\n", sizeof(*arr));printf("%d\n", sizeof(arr[1]));printf("%d\n", sizeof(&arr));printf("%d\n", sizeof(&arr + 1));printf("%d\n", sizeof(&arr[0] + 1));
结果如下:
2.2.3解析:
printf("%d\n", sizeof(arr));
arr单独放在sizeof后面,所以计算的是整个数组的大小,因为字符串后面自带一个'\0'字符,所以sizeof在计算大小的时候会把'\0'算进去,也就是八个字节。
printf("%d\n", sizeof(arr + 0));arr + 0 ,表示数组首元素的地址,那是地址大小就是4/8
printf("%d\n", sizeof(*arr));*arr是数组首元素地址解引用,得到的是S,sizeof单独计算一个S的大小,字节为1
printf("%d\n", sizeof(arr[1]));arr[1],表示数组的第二个元素,计算大小是为1字节
printf("%d\n", sizeof(&arr));&arr,表示取出整个数组的地址,是地址,大小为4/8字节
printf("%d\n", sizeof(&arr + 1));&arr + 1 ,表示跳过整个数组,指向数组末尾的地址,是地址,大小为4/8字节
printf("%d\n", sizeof(&arr[0] + 1));&arr[0] + 1 表示数组第二个元素的地址,是地址大小就为4/8字节。
2.2.4
char arr[] = "SILMY23";printf("%d\n", strlen(arr));printf("%d\n", strlen(arr + 0));printf("%d\n", strlen(*arr));printf("%d\n", strlen(arr[1]));printf("%d\n", strlen(&arr));printf("%d\n", strlen(&arr + 1));printf("%d\n", strlen(&arr[0] + 1));
32位结果如下:
64位结果如下:
2.2.4解析
printf("%d\n", strlen(arr));
arr表示数组首元素地址,strlen计算的是到\0之前的字符,所以是7个字节
printf("%d\n", strlen(arr + 0));arr+0表示数组首元素地址,7个字节
printf("%d\n", strlen(*arr));arr表示数组首元素地址,但是解引用后访问的是S的ASCII码,地址83,非法访问
printf("%d\n", strlen(arr[1]));arr[1] 表示数组第二个元素,解引用后访问的是I的ASCII码,地址73,非法访问
printf("%d\n", strlen(&arr));&arr,表示整个数组的地址,也是指向数组起始位置的,那因为有\0,所以计算的是到\0之前的地址,所以是7个字节
printf("%d\n", strlen(&arr + 1));&arr + 1 ,表示跳过整个数组,指向数组末尾的地址,由于找不到\0,所以会在内存中寻找\0,因此是随机值
printf("%d\n", strlen(&arr[0] + 1));&arr[0] + 1,表示数组第二个元素的地址,计算后的大小为6.
2.2.5
char* p = "SILMY23";printf("%d\n", sizeof(p));printf("%d\n", sizeof(p + 1));printf("%d\n", sizeof(*p));printf("%d\n", sizeof(p[0]));printf("%d\n", sizeof(&p));printf("%d\n", sizeof(&p + 1));printf("%d\n", sizeof(&p[0] + 1));
32位结果如下图:
64位结果如下图:
2.2.5解析:
printf("%d\n", sizeof(p));
p是个char* 的指针变量,指向的对象数据类型是char,是指针变量,大小就为4/8字节
printf("%d\n", sizeof(p + 1));p + 1,表示字符I的地址,本身还是指针变量,大小就为4/8字节
printf("%d\n", sizeof(*p));*p,p本身存储的是S的地址,解引用后获得S,那么S对应的类型是char,所以大小为1字节
printf("%d\n", sizeof(p[0]));p[0],表示*(p+0)== *p,所以大小为1字节
printf("%d\n", sizeof(&p));&p,表示的是指针变量p的地址,所以大小为4/8字节
printf("%d\n", sizeof(&p + 1));&p +1 ,表示跳过一个指针变量的大小的地址,是地址,大小为4/8
printf("%d\n", sizeof(&p[0] + 1));&p[0] + 1,表示指向I的地址,是地址,大小为4/8
2.2.6
char* p = "SILMY23";printf("%d\n", strlen(p));printf("%d\n", strlen(p + 1));printf("%d\n", strlen(*p));printf("%d\n", strlen(p[0]));printf("%d\n", strlen(&p));printf("%d\n", strlen(&p + 1));printf("%d\n", strlen(&p[0] + 1));
32位结果如下:
64位结果如下:
2.2.6解析:
printf("%d\n", strlen(p));
p是char* 的指针变量,指向的是S这个起始位置,末尾有\0,计算大小为7
printf("%d\n", strlen(p + 1));p + 1 指向的是I这个起始位置,末尾有\0,计算大小为6
printf("%d\n", strlen(*p));*p,p表示字符串S地址,但是解引用后访问的是S的ASCII码,地址83,非法访问
printf("%d\n", strlen(p[0]));p[0] == *(p+0) == *p, 同样是非法访问
printf("%d\n", strlen(&p));&p,表示的是p的地址,那从p的起始地址开始往后找\0,因为不确定\0在哪,所以是随机值
printf("%d\n", strlen(&p + 1));&p +1 ,表示跳过一个指针变量的大小的地址,从p的末尾开始查找,因为不确定\0在哪,所以是随机值
printf("%d\n", strlen(&p[0] + 1));&p[0] + 1,表示指向I的地址,计算的大小为6
感谢各位同伴的支持,本期指针练习篇(上)就讲解到这啦,还有指针练习篇(下),如果你觉得写的不错的话,可以给个赞,若有不足,欢迎各位在评论区讨论。
相关文章:

【C语言】指针练习篇(上),深入理解指针---指针和数组练习题和sizeof,strlen的对比【图文讲解,详细解答】
欢迎来CILMY23的博客喔,本期系列为【C语言】指针练习篇(上),深入理解指针---指针数组练习题和sizeof,strlen的对比【图文讲解,详细解答】,图文讲解指针和数组练习题,带大家更深刻理解指针的应用…...

2048游戏C++板来啦!
个人主页:PingdiGuo_guo 收录专栏:C干货专栏 大家好呀,我是PingdiGuo_guo,今天我们来学习如何用C编写一个2048小游戏。 文章目录 1.2048的规则 2.步骤实现 2.1: 初始化游戏界面 2.1.1知识点 2.1.2: 创建游戏界面 2.2: 随机…...

2000-2021年县域指标统计数据库
2000-2021年县域统计数据库 1、时间:2000-2021年 2、来源:县域统计年鉴 3、范围:2500县 5、指标: 地区名称、年份、行政区域代码、所属城市、所属省份、行政区域土地面积平方公里、乡及镇个数个、乡个数个、镇个数个、街道办…...

Hive on Spark配置
前提条件 1、安装好Hive,参考:Hive安装部署-CSDN博客 2、下载好Spark安装包,链接:https://pan.baidu.com/s/1plIBKPUAv79WJxBSbdPODw?pwd6666 3、将Spark安装包通过xftp上传到/opt/software 安装部署Spark 1、解压spark-3.3…...

计算机网络——11EMail
EMail 电子邮件(EMail) 3个主要组成部分 用户代理邮件服务器简单邮件传输协议:SMTP 用户代理 又名“邮件阅读器”撰写、编辑和阅读邮件输入和输出邮件保存在服务器上 邮件服务器 邮箱中管理和维护发送给用户的邮件输出报文队列保持待发…...

第13讲创建图文投票
创建图文投票实现 图文投票和文字投票基本一样,就是在投票选项里面,多了一个选项图片;、 <view class"option_item" v-for"(item,index) in options" :key"item.id"><view class"option_input&…...

Vulnhub靶机:DC3
一、介绍 运行环境:Virtualbox 攻击机:kali(10.0.2.15) 靶机:DC3(10.0.2.56) 目标:获取靶机root权限和flag 靶机下载地址:https://www.vulnhub.com/entry/dc-32,312…...
代码随想录算法训练营第三十一天|● 理论基础 ● 455.分发饼干 ● 376. 摆动序列 ● 53. 最大子序和
仅做学习笔记,详细请访问代码随想录 ● 理论基础 ● 455.分发饼干 ● 376. 摆动序列 ● 53. 最大子序和 ● 理论基础 有同学问了如何验证可不可以用贪心算法呢? 最好用的策略就是举反例,如果想不到反例,那么就试一试贪心吧。 …...

【光学】学习记录1-几何光学的近轴理论
课程来源:b站资源-光学-中科大-崔宏滨老师(感谢),本系列仅为自学笔记 【光学 中科大 崔宏滨老师 1080p高清修复(全集)】https://www.bilibili.com/video/BV1NG4y1C7T9?p2&vd_source7ba37b2cff2a1b783…...

【51单片机】AT24C02(江科大、爱上半导体)
一、AT24C02 1.AT24C02介绍 AT24C02是一种可以实现掉电不丢失的存储器,可用于保存单片机运行时想要永久保存的数据信息 存储介质:E2PROM 通讯接口:12C总线 容量:256字节 2.引脚即应用电路 本开发板AT24C02原理图 12C地址全接地,即全为0 WE接地,没有写使能 SCL接P21 S…...
nohup基本使用
在Linux终端命令中经常要使用到在关闭终端界面的情况下需要后台挂起执行的进程,也就是关闭终端后台任务的进程还是会常驻,下面就简单介绍下 nohup 命令 1. nohup nohup 英文全称 no hang up(不挂起),默认情况下&#x…...

postgresql 手动清理wal日志的101个坑
新年的第一天,总结下去年遇到的关于WAL日志清理的101个坑,以及如何相对安全地进行清理。前面是关于WAL日志堆积的原因分析,清理相关可以直接看第三部分。 首先说明,手动清理wal日志是一个高风险的操作,尤其对于带主从的…...
【开源训练数据集3】Top3人脸数据集及其使用方法-计算机视觉应用
目录 什么是人脸数据集? Top 3 人脸数据集 CelebFaces Attributes (CelebA)数据集 Flickr-Faces-HQ (FFHQ) 数据集 野外标记面孔 (LFW) 使用先进的人脸数据集 CelebA 访问数据集 在 Pytorch 中使用 CelebA 在 Tensorflow 中使用 CelebA Flickr-Faces-HQ 数据集 (FFH…...

精灵图,字体图标,CSS3三角
精灵图 1.1为什么需要精灵图 一个网页中往往会应用很多小的背景图像作为修饰,当网页中的图像过多时,服务器就会频繁的接受和发送请求图片,造成服务器请求压力过大,这将大大降低页面的加载速度。 因此,为了有效地减少…...
.NET Core性能优化技巧
.NET Core作为一个跨平台的开源框架,以其高效、灵活和可扩展的特性受到了广大开发者的青睐。但在实际开发中,如何确保应用程序的性能始终是一个关键的问题。本文将介绍十大.NET Core性能优化技巧,帮助开发者提升应用程序的性能。 1. 使用异步…...
人类智能远远超越了物理与数理范畴
德国哲学家黑格尔曾这样写道,我们越是熟悉的东西,就越不清楚它。这或许意味着当我们对某个事物非常熟悉时,可能会陷入一种思维定势,导致我们无法客观地认识和理解它。这种思维定势可能来自于习惯、传统观念或者个人经验࿰…...

数据库管理-第149期 Oracle Vector DB AI-01(20240210)
数据库管理149期 2024-02-10 数据库管理-第149期 Oracle Vector DB & AI-01(20240210)1 机器学习2 向量3 向量嵌入4 向量检索5 向量数据库5 专用向量数据库的问题总结 数据库管理-第149期 Oracle Vector DB & AI-01(20240210…...

FlinkSql通用调优策略
历史文章迁移,稍后整理 使用DataGenerator 提前进行压测,了解数据的处理瓶颈、性能测试和消费能力 开启minibatch:"table.exec.mini-batch.enabled", "true" 开启LocalGlobal 两阶段聚合:"table.exec.m…...
Linux在云计算领域的重要作用
在云计算领域,Linux扮演着至关重要的角色。以下是Linux在云计算领域中的重要作用: 稳定性和安全性:Linux操作系统具有稳定性和安全性,可以有效地保护用户的数据安全。它具有各种安全功能,可以防止未经授权的访问&…...
sqlserver2012 解决日志大的问题 bat脚本
要解决SQL Server 2012中事务日志过大的问题,你可以创建一个批处理脚本(.bat)来定期备份事务日志。下面是一个示例批处理脚本,该脚本使用SQLCMD工具来执行事务日志备份: echo off set "DBNAMEYourDatabaseName&qu…...
浅谈 React Hooks
React Hooks 是 React 16.8 引入的一组 API,用于在函数组件中使用 state 和其他 React 特性(例如生命周期方法、context 等)。Hooks 通过简洁的函数接口,解决了状态与 UI 的高度解耦,通过函数式编程范式实现更灵活 Rea…...
CVPR 2025 MIMO: 支持视觉指代和像素grounding 的医学视觉语言模型
CVPR 2025 | MIMO:支持视觉指代和像素对齐的医学视觉语言模型 论文信息 标题:MIMO: A medical vision language model with visual referring multimodal input and pixel grounding multimodal output作者:Yanyuan Chen, Dexuan Xu, Yu Hu…...

循环冗余码校验CRC码 算法步骤+详细实例计算
通信过程:(白话解释) 我们将原始待发送的消息称为 M M M,依据发送接收消息双方约定的生成多项式 G ( x ) G(x) G(x)(意思就是 G ( x ) G(x) G(x) 是已知的)࿰…...

如何在看板中体现优先级变化
在看板中有效体现优先级变化的关键措施包括:采用颜色或标签标识优先级、设置任务排序规则、使用独立的优先级列或泳道、结合自动化规则同步优先级变化、建立定期的优先级审查流程。其中,设置任务排序规则尤其重要,因为它让看板视觉上直观地体…...

Unsafe Fileupload篇补充-木马的详细教程与木马分享(中国蚁剑方式)
在之前的皮卡丘靶场第九期Unsafe Fileupload篇中我们学习了木马的原理并且学了一个简单的木马文件 本期内容是为了更好的为大家解释木马(服务器方面的)的原理,连接,以及各种木马及连接工具的分享 文件木马:https://w…...
Go 语言并发编程基础:无缓冲与有缓冲通道
在上一章节中,我们了解了 Channel 的基本用法。本章将重点分析 Go 中通道的两种类型 —— 无缓冲通道与有缓冲通道,它们在并发编程中各具特点和应用场景。 一、通道的基本分类 类型定义形式特点无缓冲通道make(chan T)发送和接收都必须准备好࿰…...
【SSH疑难排查】轻松解决新版OpenSSH连接旧服务器的“no matching...“系列算法协商失败问题
【SSH疑难排查】轻松解决新版OpenSSH连接旧服务器的"no matching..."系列算法协商失败问题 摘要: 近期,在使用较新版本的OpenSSH客户端连接老旧SSH服务器时,会遇到 "no matching key exchange method found", "n…...
虚拟电厂发展三大趋势:市场化、技术主导、车网互联
市场化:从政策驱动到多元盈利 政策全面赋能 2025年4月,国家发改委、能源局发布《关于加快推进虚拟电厂发展的指导意见》,首次明确虚拟电厂为“独立市场主体”,提出硬性目标:2027年全国调节能力≥2000万千瓦࿰…...
NPOI Excel用OLE对象的形式插入文件附件以及插入图片
static void Main(string[] args) {XlsWithObjData();Console.WriteLine("输出完成"); }static void XlsWithObjData() {// 创建工作簿和单元格,只有HSSFWorkbook,XSSFWorkbook不可以HSSFWorkbook workbook new HSSFWorkbook();HSSFSheet sheet (HSSFSheet)workboo…...

(一)单例模式
一、前言 单例模式属于六大创建型模式,即在软件设计过程中,主要关注创建对象的结果,并不关心创建对象的过程及细节。创建型设计模式将类对象的实例化过程进行抽象化接口设计,从而隐藏了类对象的实例是如何被创建的,封装了软件系统使用的具体对象类型。 六大创建型模式包括…...