【C语言】指针和数组的内存使用详解

news2024/11/23 19:48:12

目录

一、sizeof操作符

二、一维数组的练习

三、字符数组的练习

四、字符串数组

五、指针指向字符串

六、二维数组


一、sizeof操作符

在深入学习指针和数组的内存使用详情前,我们需要深入了解一下sizeof操作符的使用

1.1 sizeof操作符是计算括号内最终结果的类型大小

                详细可看不才详细写的笔记:【C语言】基础操作符大全

int main() {
	int a = 0;
	int sz1 = sizeof(a);
	int sz2 = sizeof(int);

	printf("sz1 = %d\n", sz1);
	printf("sz2 = %d\n", sz2);

	return 0;
}

运行结果:

  • sz1计算的是a变量 占用空间的大小,得出的结果是4
  • sz2计算的是 int类型 占用空间的大小,得出的结果也是4

由此可以证明 sizeof 计算的就是 类型大小

1.2 sizeof操作符括号内如果是复杂表达式,sizeof计算的就是表达式最终的变量类型占用空间的大小

int main() {
	int a = 0;
	char b = 12;
	short c = 0;

	int sz1 = sizeof(short);
	int sz2 = sizeof(c = a + b);

	printf("sz1 = %d\n", sz1);
	printf("sz2 = %d\n", sz2);

	return 0;
}

 运行结果:

  • 在sz2中,我们看表达式: c = a + b。在正常计算中,我们会涉及整形提升,a + b会提升成为4个字节运算,然后赋值给c,c也需要进行整形提升,提升到4个字节来接收,但是sizeof的结果还是占用2个字节,说明在复杂表达式中sizeof计算的就是表达式最终的变量类型占用空间的大小

sizeof操作符内不会进行运算赋值不管是多复杂或多简单的表达式sizeof内只会判断其最终类型占用空间大小后,返回结果

 举个栗子:

int main() {
	int a = 10;
	int b = 20;
	int c = 0;

	printf("%d\n", sizeof(c = a + b));
	printf("%d\n\n", c);

	printf("%d\n", sizeof(c = 123));
	printf("%d\n", c);

	return 0;
}

运行结果:

sizeof操作符具体实现方法是使用宏实现的,所以在编译阶段sizeof处理内容时,并不知道变量a、b等的值是什么,只会找到其最终变量判断这个变量类型占用空间多少个字节


二、一维数组的练习

int main() {
	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));

	return 0;
}

解析:

  1. 计算的是整个数组,大小为16
  2.  a+0其实是数组第一个元素的地址,是地址就是4/8字节,在指针笔记中有详细介绍
  3. *a是数组首元素,计算的是数组首元素的大小,单位是字节,结果:4
  4. a+1是第二个元素的地址,是地址大小就是4/8
  5. a[1]是第二个元素,计算的是第二个元素的大小-4-单位是字节
  6. &a是整个数组的地址,整个数组的地址也是地址,地址的大小就是4/8字节(&a--->类型:int(*)[4])
  7. &a是数组的地址,*&a就是拿到了数组,*&a-->a , a就是数组名,sizeof(*&a)-->sizeof(a),计算的是整个数组的大小,单位是字节-16 //计算的是整个数组的大小,单位是字节-16
  8. &.a是整个数组的地址,&a+1,跳过整个数组,指向数组后边的空间,是一个地址,大小是4/8字节
  9. &a[0]是首元素的地址,计算的是首元素地址的大小,4/8字节
  10. &a[0] + 1是第二个元素的地址,地址的大小就是4/8字节

三、字符数组的练习

int main() {

	char arr[] = { 'a','b','c','d','e','f' };
	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));

	return 0;
}

解析:

  1. arr单独放在sizeof内部,计算的是整个数组的大小,单位是字节,结果是:6
  2. arr+0是数组首元素的地址,结果:4/8
  3. arr 是是首元素的地址,*arr是数组的首元素,首元素的大小1字节,结果:1
  4. 同上,结果:1
  5. &arr虽然是数组的地址,但还是地址。结果:4/8
  6. &arr + 1是跳过了整个数组后的地址。地址的大小依然是4/8。
  7. 第二个元素地址。 结果:4/8
int main() {

	char arr[] = { 'a','b','c','d','e','f' };
	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));

	return 0;
}
  1. strlen计算的是 '0' 前的有多少值。在上面数组arr中并未有 '\0' ,则strlen一直越界访问直到遇到 '\0' 后停止计算。结果为:随机值
  2. 同上。结果为:随机值
  3. 计算的是字符'a'的长度。'a'在ASCII码值中为97,此时strlen是把 97作为起始地址,而编号为97的地址是禁止访问的。所以结果是:报错。
  4. 同上。结果为:报错。
  5. 随机值
  6. 随机值
  7. 随机值

四、字符串数组

int main() {

	char arr[] = "abcdef";
	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));

	return 0;
}
  1.  计算的是数组的大小。单位是字节。结果为:7
  2. 计算的是地址的大小。arr+0是首元素的地址。结果为:4/8
  3. *arr是首元素。sizeof(*arr)计算首元素的大小。结果为:1
  4. arr[1]是第二个元素,计算的是第二个元素的大小。
  5. &arr虽然是数组的地址,但也是地址。结果为:4/8
  6. &arr+1虽然跳过了整个数组后的地址,但也是地址。结果为:4/8
  7. &arr[0] + 1第二个元素的地址。结果为:4/8
int main() {

	char arr[] = "abcdef";
	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));

	return 0;
}
  1. 计算字符串的总体长度,结果为:6。
  2. 同上
  3. 计算的是字符'a'的长度。'a'在ASCII码值中为97,此时strlen是把 97作为起始地址,而编号为97的地址是禁止访问的。所以结果是:报错。
  4. 同上
  5. &arr取出的是数组的地址,随着数组的地址开始计算直到 '\0'。结果为:6
  6. &arr + 1跳过的是整个数组,结果为:随机值。
  7. (&arr[0] + 1) ==> (&*(arr + 0) + 1) ==> (arr + 0) + 1 ==> arr + 1,跳过首元素, 从第二个元素开始计算,直到 '\0'。结果为:5。

五、指针指向字符串

int main() {

	char* p = "abcdef";
	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));

	return 0;
}
  1.  计算的是字符串长度,结果为:6。
  2. 从第二个元素开始计算字符串的长度,结果为:5。
  3. 计算的是字符'a'的长度。'a'在ASCII码值中为97,此时strlen是把 97作为起始地址,而编号为97的地址是禁止访问的。所以结果是:报错。
  4. 同上
  5. &p取出的是指针变量的地址。从指针变量的地址开始向后计算长度。结果为:随机值。
  6. 同上。
  7. (&arr[0] + 1) ==> (&*(arr + 0) + 1) ==> (arr + 0) + 1 ==> arr + 1,跳过首元素, 从第二个元素开始计算,直到 '\0'。结果为:5。
int main() {

	char* p = "abcdef";
	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));

	return 0;
}
  1. 计算的是指针变量p的大小。结果为:4/8
  2. p+1是字符b的地址。结果为:4/8
  3. *p就是字符串的第一个字符 'a'。结果为:1
  4. 同上
  5. 计算的是地址。结果为:4/8
  6. 计算的是地址。结果为:4/8
  7. 计算的是地址。结果为:4/8

六、二维数组

int main() {

	int a[3][4] = { 0 };
	printf("%d\n", sizeof(a));
	printf("%d\n", sizeof(a[0][0]));
	printf("%d\n", sizeof(a[0]));
	printf("%d\n", sizeof(a[0] + 1));
	printf("%d\n", sizeof(*(a[0] + 1)));
	printf("%d\n", sizeof(a + 1));
	printf("%d\n", sizeof(*(a + 1)));
	printf("%d\n", sizeof(&a[0] + 1));
	printf("%d\n", sizeof(*(&a[0] + 1)));
	printf("%d\n", sizeof(*a));
	printf("%d\n", sizeof(a[3]));

	return 0;
}
  1.  计算的是整个二维数组的大小。结果为:48
  2. 计算的是第一个元素的大小。结果为:4
  3. a[0]相当于第一行做为一维数组的数组名,sizeof(arr[0])把数组名单独放在sizeof()内,计算的是第一行的大小。例如int a[] = {0}.sizeof(a)计算的是a数组的大小。
  4. a[0]是第一行数组名,数组名此时是首元素的地址。a[0]其实就是第一行第一个元素的地址,所以a[0] + 1就是第一行第二个元素地址。地址大小是4/8字节。
  5. (*(a[0] + 1))是第一行第二个元素,大小是4个字节。
  6. a是二维数组的数组名。没有sizeof(a),也没有&a,所以a是首元素地址,而把二维数组看成一维数组时,二维数组的首元素是它第一行。 a就是第一行首元素的地址。a+1这是第二行的地址。结果为:4
  7. sizeof(a[1])计算第二行的大小,单位是字节。 a是首元素地址,a+1是第二个元素的地址。结果为:4/8
  8. 计算的是第二行,大小单位是字节。结果为:16
  9. a是首元素地址也就是第一行的地址。*a就是第一行,sizeof(*a)就是计算第一行的大小。

以上就是本章所有内容。若有勘误请私信不才。万分感激💖💖 若有帮助不要吝啬点赞哟~~💖💖

ps:表情包来自网络,侵删🌹

若是看了这篇笔记彻底拿捏指针可以在评论区打出:小小指针!拿捏!😎

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

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

相关文章

python中zip()与zip(*)的用法解析

zip在英文中有拉链的意思,我们由此可以形象的理解它的作用:将可迭代的对象作为参数,将对象中对应的元素打包成一个个元组,然后返回由这些元组组成的列表。如果各个迭代器的元素个数不一致,则返回列表长度与最短的对象相…

Sharding-JDBC笔记04-分库分表实战

文章目录 前言一、需求描述二、数据库设计三、环境说明四、环境准备4.1.mysql主从同步(windows)4.2.初始化数据库 五、实现步骤5.1 搭建maven工程引入maven依赖 5.2 实体类5.3 dao层5.4 服务类5.5 测试类总结 5.6 查询商品DaoService单元测试输出小结 5.7 统计商品Dao单元测试统…

力扣 中等 46.全排列

文章目录 题目介绍题解 题目介绍 题解 代码如下&#xff1a; class Solution {List<List<Integer>> res new ArrayList<>();// 存放符合条件结果的集合List<Integer> path new ArrayList<>();// 用来存放符合条件结果boolean[] used; // 标记…

计算机毕业设计 基于Django的在线考试系统的设计与实现 Python+Django+Vue 前后端分离 附源码 讲解 文档

&#x1f34a;作者&#xff1a;计算机编程-吉哥 &#x1f34a;简介&#xff1a;专业从事JavaWeb程序开发&#xff0c;微信小程序开发&#xff0c;定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事&#xff0c;生活就是快乐的。 &#x1f34a;心愿&#xff1a;点…

[SAP ABAP] INCLUDE程序创建

在ABAP中&#xff0c;INCLUDE是一种结构化编程技术&#xff0c;它允许将一段程序代码片段包含到其他程序段中&#xff0c;以便复用和维护 INCLUDE程序创建的好处 ① 代码模块化 将常用的功能或通用的子程序存放到单独的文件中&#xff0c;使得主程序更简洁、易于理解和管理 ② …

一个为分布式环境设计的任务调度与重试平台,高灵活高效率,系统安全便捷,分布式重试杀器!(附源码)

背景 近日挖掘到一款名为“SnailJob”的分布式重试开源项目,它旨在解决微服务架构中常见的重试问题。在微服务大行其道的今天&#xff0c;我们经常需要对某个数据请求进行多次尝试。然而&#xff0c;当遇到网络不稳定、外部服务更新或下游服务负载过高等情况时&#xff0c;请求…

YOLO11改进|注意力机制篇|引入MSCA注意力机制

目录 一、【MSCA】注意力机制1.1【MSCA】注意力介绍1.2【MSCA】核心代码 二、添加【MSCA】注意力机制2.1STEP12.2STEP22.3STEP32.4STEP4 三、yaml文件与运行3.1yaml文件3.2运行成功截图 一、【MSCA】注意力机制 1.1【MSCA】注意力介绍 下图是【MSCA】的结构图&#xff0c;让我…

硬件SPI解析-基于江科大的源码

一、SPI基本介绍 SPI&#xff08;Serial Peripheral Interface&#xff09;通信协议是由摩托罗拉公司&#xff08;现为NXP Semiconductors的一部分&#xff09;在20世纪80年代中期开发的。SPI是一种同步串行通信接口&#xff0c;设计用于短距离通信&#xff0c;特别是嵌入式系统…

图片怎么转换成pdf格式?这5种转换方法一看就会

在工作学习中&#xff0c;PDF格式因其跨平台兼容性和安全性成为了工作和学习中不可或缺的文件格式。然而&#xff0c;很多时候我们需要将图片转换为PDF&#xff0c;以便更好地整理、分享和保存。今天&#xff0c;就为大家介绍5种高效的图片转PDF方法&#xff0c;一起来学习下吧…

让机器来洞察他的内心!

本文所涉及所有资源均在传知代码平台可获取。 目录 洞察你的内心&#xff1a;你真的这么认为吗&#xff1f; 一、研究背景 二、模型结构和代码 D. 不一致性学习网络 E. 多模态讽刺分类 三、数据集介绍 四、性能展示 五、实现过程 1. 下载预训练的 GloVe 词向量&#xff08;Comm…

端口被占用问题的解决方案

一、问题描述 如图&#xff0c;启动服务失败&#xff0c;失败原因是8080端口被占用 二、解决方案 1.更换端口为其它&#xff0c;例如8002 9001等 2.关闭占用端口的进程&#xff0c;推荐这种解决方案 步骤一&#xff1a;在win命令行查询占用该端口号的进程 命令如下 netsta…

使用浏览器这么多年,你真的了解DevTools吗?

Devtools是Web测试时每天都要用的工具&#xff0c;它提供了很多调试功能&#xff0c;可以帮助我们更好的定位问题。而我们平时使用的功能只是它全部功能的子集&#xff0c;很多功能并没用到过。 作为高频使用的工具&#xff0c;还是有必要好好掌握的。测试时在日常工作中提BUG…

项目前置知识

目录 std::bind 定时器 timerfd 时间轮设计 C11正则库 日志打印宏 通用类型ANY std::bind std::bind是C11提供的一个接口&#xff0c;他的功能&#xff1a;传递一个原始函数对象&#xff0c;对其绑定某些参数&#xff0c;生成一个新的函数对象。 其实简单来说&#xff…

YOLO--前置基础词-学习总结(上)

RFBNet是什么意思 RFBNet 是一种用于目标检测的深度学习网络&#xff0c;它的名字来源于 "Receptive Field Block Network"&#xff08;感受野块网络&#xff09;。简单来说&#xff0c;RFBNet 是一种可以让计算机更好地“看”图像中不同大小的物体的方法。 在图像处…

混凝土裂缝检测分割系统源码&数据集分享

混凝土裂缝检测分割系统源码&#xff06;数据集分享 [yolov8-seg-RCSOSA&#xff06;yolov8-seg-C2f-REPVGGOREPA等50全套改进创新点发刊_一键训练教程_Web前端展示] 1.研究背景与意义 项目参考ILSVRC ImageNet Large Scale Visual Recognition Challenge 项目来源AAAI Glo…

时间序列+Transformer席卷而来,性能秒杀传统,创新性拉满,引爆顶会!

时间序列分析与Transformer模型的结合&#xff0c;已成为深度学习领域的一大趋势。这种结合能够高效捕捉序列中的长期依赖关系&#xff0c;提升时间序列分析和预测的准确性。 时间序列Transformer技术在股票价格预测、气候预测、交通流量预测、设备故障预测、自然语言处理等多…

43页PPT | 大健康行业BI解决方案

药企应用现状与挑战 大健康行业中的药企在数据分析方面仍面临诸多挑战。传统的数据分析模式依赖于纸质记录和线下手动更新&#xff0c;导致数据时效性低、人力成本高&#xff0c;难以快速发挥数据价值。随着数据量的激增&#xff0c;多源数据的归集和整合成为药企数据分析的难点…

Python画笔案例-080 绘制 颜色亮度测试

1、绘制 颜色亮度测试 通过 python 的turtle 库绘制 颜色亮度测试,如下图: 2、实现代码 绘制 颜色亮度测试,以下为实现代码: """颜色亮度测试.py本程序需要coloradd模块支持,请在cmd窗口,即命令提示符下输入pip install coloradd进行安装。本程序演示brig…

JavaSE——面向对象6.1:继承知识点补充(虚方法表等)

目录 一、子类到底能继承父类中的哪些内容&#xff1f; 二、继承内存图 三、继承中&#xff1a;成员变量和成员方法的访问特点 (一)成员变量的访问特点 (二)成员方法的访问特点 1.this与super访问成员方法的特点 2.方法重写 2.1方法重写的本质&#xff1a;子类覆盖了从…

社区交流礼仪 | 提问的艺术

唠唠闲话 2021 年通过 Julia 社区了解到开源&#xff0c;自此开始融入开源社区&#xff0c;学习和体验这种独特的协作模式与交流文化&#xff0c;受益良多。本篇文章为开源新手必读&#xff0c;文章中探讨的交流模式&#xff0c;不仅对参与开源项目的协作有所帮助&#xff0c;…