c语言进阶部分详解(《高质量C-C++编程》经典例题讲解及柔性数组)

news2025/1/18 18:52:43

上篇文章我介绍了介绍动态内存管理 的相关内容:c语言进阶部分详解(详细解析动态内存管理)-CSDN博客

各种源码大家可以去我的github主页进行查找:唔姆/比特学习过程2 (gitee.com)

今天便接“上回书所言”,来介绍《高质量C-C++编程》经典例题讲解及柔性数组


目录

一.几个经典例题

1.1题目一

注意

 

改进

 

1.2问题二

1.3问题三

1.4问题四

二.柔性数组

2.1柔性数组特点

2.2柔性数组的使用

2.3柔性数组的优势 


一.几个经典例题

1.1题目一

void ToMalloc(char* p)
{
	p = (char*)malloc(100);
}
void test1(void)
{
	char* str = NULL;
	ToMalloc(str);
	strcpy(str, "hello");
	printf(str);//就是printf("%s",str);
    free(str);
    str=NULL;
}
int main()
{
	test1();


	return 0;
}

运行结果是程序崩溃了:

  •  对一个NULL进行解引用操作(想对一个指针内容更改必然有解引用操作)
  • p动态开辟后没有进行free,内存泄露了

注意

有些读者可能遇到这样的情况

int main()
{
	char* ar = "abdldsaf";
	strcpy(ar,"hello");
	printf(ar);

	return 0;
}

编译器都会报错,这是因为:ar其实是一个字符串常量 ,我们怎么能对常量进行修改呢?应该使用字符数组来存储可修改的字符串

 所以我们可以用数组或者动态开辟进行改正问题

 

改进

void ToMalloc(char** p)
{
	*p = (char*)malloc(100);
}
void test1(void)
{
	char* str = NULL; 
	ToMalloc(&str);
	strcpy(str, "hello");
	printf(str);//就是printf("%s",str);
}

 

1.2问题二

char* ToMalloc(void)
{
	char p[] = "hello world";
	return p;
}
void test2(void)
{
	char* str = NULL;
	str = ToMalloc();
	printf(str);
}

int main()
{
	test2();
	return 0;
}

结果:

 大家可以看到是乱码:这是因为我们返回了局部变量的地址。当出了ToMalloc函数后,p在栈空间上面被销毁了。此时返回的指针将指向无效的内存(内存已经还给操作系统了)

1.3问题三

void ToMalloc(char** p, int num)
{
	*p = (char*)malloc(num);
}
void test3(void)
{
	char* str = NULL;
	ToMalloc(&str, 100);
	strcpy(str, "hello");
	printf(str);
}

int main()
{
	test3();
	return 0;
}

大家可以看到跟问题一我们改进后的代码几乎是是一样的 ,也确实输出hello

问题便是存在内存泄漏 ,我们没有对malloc开辟的空间进行free

1.4问题四

void test4()
{
	char* str = (char*)malloc(100);
	strcpy(str, "hello");
	free(str);
	if (str != NULL)
	{
		strcpy(str, "world");
		printf(str);
	}
}

int main()
{
	test4();
	return 0;
}

str已经被释放了,str成为了野指针,又对野指针进行操作(非法访问内存 ) 


二.柔性数组

C99 中,结构中的最后一个元素允许是未知大小的数组,这就叫做『柔性数组』成员 

 基本形式如下:

typedef struct st_type
{
        int i ;
        int a [ 0 ]; // 柔性数组成员 部分编译器不能识别时换成:int a[];
} type_a ;

2.1柔性数组特点

  1. 结构中的柔性数组成员前面必须至少一个其他成员
  2. sizeof 返回的这种结构大小不包括柔性数组的内存
  3. 包含柔性数组成员的结构一般使用malloc ()函数进行内存的动态分配,并且分配的内存应该大于结构的大小,以适应柔性数组的预期大小(多的一部分要给柔性数组)
typedef struct s
{
	char a;
	int b;
	int c[0];//柔性数组成员
};

int main()
{
	printf("%d", sizeof(struct s));
	return 0;
}

 

2.2柔性数组的使用

struct s
{
	char a;
	int b;
	int c[0];//柔性数组成员
};

int main()
{
	struct s* s1 = (struct s*)malloc(sizeof(struct s)+20);
	if (s1 == NULL)
	{
		perror("malloc");
		return 1;
	}
	//赋值
	s1->a = 'a';
	s1->b = 6;
	for (int i = 0; i < 5; i++)
	{
		s1->c[i] = i;
	}
	//打印
	for (int i = 0; i < 5; i++)
	{
		printf("%d ",s1->c[i]);
	}

	//如果不够,就扩容
	struct s* s2 = (struct s*)realloc(s1, sizeof(struct s) + 40);
	if (s1 != NULL)
	{
		s1 = s2;
	}
	else
	{
		return 1;
	}
	//释放
	free(s1);
	s1 = NULL;
	return 0;
}

2.3柔性数组的优势 

 也许我们会想,下面的代码也有相同的作用啊,为什么还要用柔性数组呢?

struct S
{
	char a;
	int b;
	int* c;
};

int main()
{
	struct S* s1 = (struct s*)malloc(sizeof(struct s));
	if (s1 == NULL)
	{
		perror("malloc");
		return 1;
	}
	//赋值
	s1->a = 'a';
	s1->b = 6;
	s1->c = (int*)malloc(20);
	for (int i = 0; i < 5; i++)
	{
		s1->c[i] = i;
	}
	//打印
	for (int i = 0; i < 5; i++)
	{
		printf("%d ", s1->c[i]);
	}

	//如果不够,就扩容
	int p = (struct s*)realloc(s1->c,40);
	if (s1 != NULL)
	{
		s1->c = p; 
	}
	else
	{
		return 1;
	}
	//释放
	free(s1->c); //先释放后部分,如果先释放前面的就找不到后面的了
	s1->c = NULL;
	free(s1);
	s1 = NULL;
	return 0;
}

我们可以知道还是柔性数组的代码更好:

优点一:方便内存释放 

如果结构体里面做了二次内存分配,有时可能只针对结构体进行一次释放,这样就造成内存泄漏了。
如果我们把结构体的内存以及其成员要的内存一次性分配好了,并返回给用户一个结构体指针,用户做 一次free 就可以把所有的内存也给释放掉
 
优点二: 这样有利于访问速度
连续的内存有益于提高访问速度,也有益于减少内存碎片

好嘞!这次的内容就先到这里了,感谢大家支持!!! 

 

 

 

 

 

 

 

 

 

 

 

 

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

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

相关文章

CANoe新建XML自动化Test Modules

文章目录 1.打开Test Modules2.新建Environment3.新建XML Test Modules4.新建.can文件5.打开XML Test Modules6.新建xml脚本并保存7.编译8.在.can文件写个测试用例9.修改报告格式为HTML10.运行查看报告后面介绍的文章会重复用到这部分,这里单独介绍下,后面不做重复介绍。 1.…

Envoy XDS协议学习

Envoy xds学习 资料地址 envoy官网资料连接 接口说明 xds分为增量接口和全量接口SotW&#xff1a;state of the world 即全量的数据Incremental&#xff1a; 增量的数据 具体接口 Listener: Listener Discovery Service (LDS) SotW: ListenerDiscoveryService.StreamList…

一文搞懂设计模式之工厂模式

大家好&#xff0c;我是晴天&#xff0c;本周将同大家一起学习设计模式系列的第二篇文章——工厂模式&#xff0c;我们将依次学习简单工厂模式&#xff0c;工厂方法模式和抽象工厂模式。拿好纸和笔&#xff0c;我们现在开始啦~ 前言 我们在进行软件开发的时候&#xff0c;虽然…

vector类模拟实现(c++)(学习笔记)

vector 构造函数析构函数[]push_backsize()capacity()reserve()push_back() 迭代器实现非const和const版本 pop_back()resize()insert()***重点erase()***重点再谈构造函数&#xff01;拷贝构造函数****&#xff08;重点&#xff09;运算符重载***&#xff08;重点&#xff09;…

详解RSA加密算法 | Java模拟实现RSA算法

目录 一.什么是RSA算法 二.RSA算法的算法原理 算法描述 三.RSA算法安全性 四.RSA算法的速度 五.用java实现RSA算法 一.什么是RSA算法 1976年&#xff0c;Diffie和Hellman在文章“密码学新方向&#xff08;New Direction in Cryptography&#xff09;”中首次提出了公开…

arduino - NUCLEO-H723ZG - test

文章目录 arduino - NUCLEO-H723ZG - test概述笔记物理串口软串口备注END arduino - NUCLEO-H723ZG - test 概述 准备向NUCLEO-H723ZG上移植西门子飞达控制的Arduino程序. 先确认一下知识点和效果. 笔记 物理串口 NUCLEO-H723ZG在STM32 Arduino 库中, 只提供了一个串口 Se…

快速了解推荐引擎检索技术

目录 一、推荐引擎和其检索技术 二、推荐引擎的整体架构和工作过程 &#xff08;一&#xff09;用户画像 &#xff08;二&#xff09;文章画像 &#xff08;三&#xff09;推荐算法召回 三、基于内容的召回 &#xff08;一&#xff09;召回算法 &#xff08;二&#xf…

uni-app---- 点击按钮拨打电话功能点击按钮调用高德地图进行导航的功能【安卓app端】

uniapp---- 点击按钮拨打电话功能&&点击按钮调用高德地图进行导航的功能【安卓app端】 先上效果图&#xff1a; 1. 在封装方法的文件夹下新建一个js文件&#xff0c;然后把这些功能进行封装 // 点击按钮拨打电话 export function getActionSheet(phone) {uni.showAct…

【雷达原理】雷达杂波抑制方法

目录 一、杂波及其特点 1.1 什么是杂波&#xff1f; 1.2 杂波的频谱特性 二、动目标显示(MTI)技术 2.1 对消原理 2.2 数字对消器设计 三、MATLAB仿真 3.1 对消效果验证 3.2 代码 一、杂波及其特点 1.1 什么是杂波&#xff1f; 杂波是相对目标回波而言的&#xff0c;…

【Python工具】Panoply介绍及安装步骤

Panoply介绍及安装步骤 1 Panoply介绍2 Panoply安装步骤&#xff08;Windows&#xff09;2.1 下载并安装JAVA环境2.2 下载Panoply报错&#xff1a;Error: A JNI error has occurred, please check your installation and try again. 参考 1 Panoply介绍 Panoply是一款由美国国…

【大数据】Apache NiFi 数据同步流程实践

Apache NiFi 数据同步流程实践 1.环境2.Apache NIFI 部署2.1 获取安装包2.2 部署 Apache NIFI 3.NIFI 在手&#xff0c;跟我走&#xff01;3.1 准备表结构和数据3.2 新建一个 Process Group3.3 新建一个 GenerateTableFetch 组件3.4 配置 GenerateTableFetch 组件3.5 配置 DBCP…

selenium自动化测试入门 —— 设置等待时间

time.sleep(3) 固定等待3秒 driver.implicitly_wait(10) 隐性的等待&#xff0c;对应全局 WebDriverWait( driver, timeout).until(‘有返回值的__call__()方法或函数’) 显性的等待&#xff0c;对应到元素 一、time.sleep(seconds) 固定等待 import time time.sleep(3) #…

【C++那些事儿】类与对象(1)

君兮_的个人主页 即使走的再远&#xff0c;也勿忘启程时的初心 C/C 游戏开发 Hello,米娜桑们&#xff0c;这里是君兮_&#xff0c;我之前看过一套书叫做《明朝那些事儿》&#xff0c;把本来枯燥的历史讲的生动有趣。而C作为一门接近底层的语言&#xff0c;无疑是抽象且难度颇…

10.16nginx负载均衡

nginx正向代理 反向代理 负载均衡 nginx当中有两种代理方式&#xff1a; 七层代理&#xff08;http协议&#xff09; 四层代理&#xff08;基于tcp或udp的流量转发&#xff09; *七层代理&#xff1a;代理的是http的请求和响应 客户端请求代理服务器&#xff0c;由代理服务…

curl(五)与shell结合的细节

一 curl与shell结合的细节 ① 问题引入 需求&#xff1a; 传递变量以json数据给curl ② 方式1 反斜杠\转义 1、转义内层双引号 --> 了解即可 特点&#xff1a; 可读性低,并且很复杂 2、转义外层单引号 --> 推荐另一种方式&#xff1a; 只转义外层单引号 实质&am…

【马蹄集】—— 百度之星 2023

百度之星 2023 目录 BD202301 公园⭐BD202302 蛋糕划分⭐⭐⭐BD202303 第五维度⭐⭐ BD202301 公园⭐ 难度&#xff1a;钻石    时间限制&#xff1a;1秒    占用内存&#xff1a;64M 题目描述 今天是六一节&#xff0c;小度去公园玩&#xff0c;公园一共 N N N 个景点&am…

使用Gorm进行高级查询

深入探讨GORM的高级查询功能&#xff0c;轻松实现Go中的数据检索 高效的数据检索是每个应用程序性能的核心。GORM&#xff0c;强大的Go对象关系映射库&#xff0c;不仅扩展到基本的CRUD操作&#xff0c;还提供了高级的查询功能。本文是您掌握使用GORM进行高级查询的综合指南。…

计算虚拟化3——I/O设备虚拟化

目录 I/O基本概念 I/O设备与CPU连接图 CPU与I/O设备的交互 访问I/O设备&#xff08;IO Access&#xff09; 数据传输&#xff08;Data Tronhsfer&#xff09; I/O设备虚拟化技术 软件辅助全虚拟化 半虚拟化 Virtio协议基本概念 Virtqueue讲解 硬件辅助全虚拟化 I/O…

美国航空公司飞行员工会遭受勒索软件攻击

导语&#xff1a;近日&#xff0c;美国航空公司的飞行员工会遭受了一次勒索软件攻击。这次攻击对于全球最大的独立飞行员工会——美国航空公司飞行员协会&#xff08;APA&#xff09;造成了一定影响。让我们一起来了解详情。 背景介绍 美国航空公司飞行员协会成立于1963年&…

Bytedance揭秘OpenAI大模型: GPT-3到GPT-4进化路径

文章目录 探秘GPT-3到GPT-4进化之路1、SFT&#xff1a;早期GPT进化的推动者2、RLHF和SFT&#xff1a;编码能力提升的功臣3、代码加入预训练&#xff0c;对推理帮助最大4、“跷跷板”现象 论文地址项目链接Reference GPT-Fathom: Benchmarking Large Language Models to Deciphe…