目录
目录
前言
神兽中的怪兽,struct!
创建、使用及理解内存。
利用上个习题的神兽,使用malloc从原始内存创建怪兽!
正文
代码:ex16
(从此篇开始,代码要用图片了。代码只有多敲,多输入,才会跟你亲近。也就是说,如果你是刚新学的,建议全程输入一遍。)
输出
struct简介
没有struct,就需要知道数据的大小、数据类型和内存块地址,才能找到相应的数据。早期的汇编代码(甚至当今的一些)就是这样做的。
使用struct可以将多个不同类型的变量组合成一个整体,使代码更加清晰、简洁和可读性更强。
破坏+附加题
如何不使用指针将结构体传递给别的函数。
1 | 试着传NULL到Person_destroy中, 看会发生什么。如果程序不终止,Makefile的CFLAGS中缺-g。(这不是告诉我会中 |
2 | 程序结尾不调用Person_destroy,看“小僵”啥反应?研究一下给“小僵”啥,它会打印内存泄露细节。(那肯定是血浆,嘿嘿……) |
3 | 忘记释放Person_destroy中的who->name,然后比较输出。使用正确的选项,让“小僵”告诉你具体那里出错。 |
4 | 恩,再将NULL给Person_print,然后看看“小僵”脸色。哦哦,它很快要化成灰了! |
5 | 将程序转换成不用指针与malloc的版本 |
5.1 | 如何在栈(stack)上创建struct,就和你创建任何别的变量一样。 |
5.2 | 如何使用x.y(句点)而非x->y语法来初始化struct。 |
5.3 | 如何不使用指针将结构体传递给别的函数 |
1答答:
虽然已经知道终止,也要知道怎么终止不是?
增加代码:
输出:
2答答:
“小僵”表面看不出来反应,然而实际波涛汹涌。找了三种方法,详见后语3
我最终用了第一种,sudo apt-get install valgrind。第二种环境变量没反应,第三种,额,超纲,了解下吧。
我的输出:
(有大神看懂没?反正我看得不太懂,就又检测个正常的比较下,也就稍微明白了点)
3答答:
同2(valgrind --leak-check=full ./ex16),不同的是我又仔细看了下报错详细步骤,貌似又懂了一点工具。如果你和我一样,请参考后语4 。
4答答:
哪里有NULL,哪里就有段错误!
难度分界线
5答答:
的确挺有难度,用了我好几个番茄时间,同时让我认识到了指针对与批量变量赋值的便利性。(以前可是做不出来的,现在用上ai工具,勉强完成了,有成就感!)
代码:
1 #include <stdio.h>
2 #include <string.h>
3
4 typedef struct
5 {
6 char name[20];
7 int age;
8 int height;
9 int weight;
10 } Stru;
11
12 void Person_print(Stru who)
13 {
14 printf("Name: %s\n", who.name);
15 printf("\tAge: %d\n", who.age);
16 printf("\tHeight: %d\n", who.height);
17 printf("\tWeight: %d\n", who.weight);
18 }
19
20 int main(int argc, char *argv[ ])
21 {
22 Stru Joe, Frank;
23 strcpy(Joe.name, "Joe Alex");
24 Joe.age = 38;
25 Joe.height = 138;
26 Joe.weight = 380;
27
28 printf("Joe is at memory location:%p\n", Joe.name);
29 Person_print(Joe);
30
31 strcpy(Frank.name, "Frank Blank");
32 printf("Frank is at memory location:%p\n", Frank.name);
33 Frank.age = 25;
34 Frank.height = 125;
35 Frank.weight = 250;
36 Person_print(Frank);
37
38 Joe.age += 25;
39 Frank.age +=25;
40
41 Person_print(Joe);
42 Person_print(Frank);
43
44 return 0;
45 }
输出:
后语
1、原始内存
(原始内存,看到这些陌生的概念,有点难受,随后又开始喜悦!因为难受,所以喜悦,大脑又要开创新的神经元了。如果从另一个角度讲,总感觉是一种知识上的受虐狂心态,嘎嘎!)
原始内存(Raw Memory)是指计算机硬件中的一块用于存储数据和指令的物理空间。它是计算机系统中的主要存储介质,用于临时存储正在运行的程序、数据和操作系统。原始内存是由一组连续的存储单元组成,每个存储单元都有一个唯一的地址。
原始内存通常是固化在计算机主板上的芯片,也称为内存芯片。它以二进制形式存储数据和指令,并且在每个存储单元中可以存储一个固定大小的数据。
原始内存的访问速度非常快,可以迅速地读取和写入数据。它是计算机系统中内存层次结构中的第一层,也是CPU直接访问的主要内存。
2、汇编代码
(大学里学过,但是映像有点淡了。)
汇编代码是一种低级语言,用于直接与计算机硬件交互。它是机器语言的文本表示形式,通常由特定的CPU架构定义。汇编代码通常以助记符(如ADD,MOV,JMP等)和操作数(寄存器,内存地址等)的形式表示。
以下是一个简单的x86汇编代码示例,将两个数字相加并将结果存储在寄存器中:
section .data
num1 db 10
num2 db 20
section .text
global _start
_start:
mov al, [num1] ; 将num1的值存储到AL寄存器
add al, [num2] ; 将num2的值加到AL寄存器上
mov [result], al ; 将结果存储到result内存地址
section .bss
result resb 1 ; 用于存储结果的内存单元
以上代码首先在.data节中定义了两个变量num1和num2,然后在.text节中使用MOV指令将num1的值存储到AL寄存器中。接下来,使用ADD指令将num2的值加到AL寄存器上。最后,使用MOV指令将结果存储在名为result的内存地址中。
请注意,以上示例代码是基于x86架构的汇编代码示例。不同的CPU架构使用不同的汇编语言和指令集。
3、打印内存泄露
要让Linux打印程序内存泄露情况,可以使用以下方法:
-
使用valgrind工具:Valgrind是一个强大的内存分析工具,可以检测程序的内存泄露情况。使用valgrind运行程序时,会自动检测内存泄露并输出相关信息。例如,使用以下命令运行程序:
valgrind --leak-check=full ./your_program
valgrind会检测程序在运行过程中的内存泄露情况,并在程序结束时输出相关信息。
-
使用glibc的内存泄露检测工具:glibc提供了一个内存泄露检测工具,可以通过设置环境变量来启用。例如,使用以下命令运行程序:
MALLOC_CHECK_=1 ./your_program
这样,程序运行时会检测内存泄露情况,并在程序结束时输出相关信息。
-
使用LD_PRELOAD技术:使用LD_PRELOAD技术可以在程序运行前加载一个动态链接库,从而拦截原始库函数的调用。可以编写一个自定义的LD_PRELOAD库,重定义malloc()、free()等与内存分配相关的函数,通过记录分配和释放的内存块,检查是否存在泄露。具体步骤比较复杂,需要深入了解LD_PRELOAD技术和动态链接库的使用。
以上是几种常用的方法,可以根据具体的情况选择适合的方法来检测程序的内存泄露情况。
4.valgrind的输出解读
Valgrind是一种用于调试和性能分析的工具,针对C、C++和Fortran程序。当该程序运行时,Valgrind会监控程序的内存使用情况,并在发现错误或潜在问题时生成报告。
Valgrind的报错信息一般包含以下几个方面的内容:
-
错误类型:Valgrind报告中会指明错误的类型,比如内存泄漏(Memory Leak),访问无效的内存(Invalid Read/Write),使用未初始化的变量(Uninitialized Variable)等。
-
出错位置:报告会告诉你代码中具体出错的位置,具体到源代码的行号,方便你后续进行修复。
-
源代码相关信息:报告中通常会包含与错误相关的源代码行的上下文信息,帮助你更好地理解错误产生的背景。
-
错误堆栈跟踪:Valgrind会提供一个错误堆栈跟踪,显示出引发问题的函数调用链,这对于定位错误非常有帮助。
-
其他建议:报告中可能会包含一些建议,用于修复错误或改进性能。
当你看到Valgrind的报错信息,首先需要关注错误类型和出错位置,然后根据报告中的其他信息来定位和修复问题。另外,你还可以根据错误堆栈跟踪和其他建议来优化程序的性能。
5、堆和栈的区别
(作者在附加任务中总是有意让我们提前了解下一个课题,咱们就跟着他的思路来吧。这两个玩意名字挺怪,理解起来也有难度。栈就叫“客栈”吧,旅馆宾管啥玩意的,都是临时居住,啥都不需要自己动手,隨住随走。堆就叫“草堆”,没钱住客栈就睡草堆,睡过了要复原,不然就暴露了。恩,我脑袋里没有想古装武侠剧,嘿嘿……)
堆和栈是计算机内存中两种不同的数据存储方式,它们有以下区别:
-
分配方式:栈是编译器自动分配和释放内存,而堆是程序员手动分配和释放内存。
-
存储内容:栈上存储的是局部变量、函数参数和函数返回地址等,而堆上存储的是动态分配的对象和数据。
-
内存管理:栈的内存管理由系统自动完成,所以分配和释放速度很快;而堆的内存管理需要程序员手动操作,容易出现内存泄漏和内存碎片问题。
-
分配大小:栈的大小是固定的,在程序运行时无法动态调整;而堆的大小可以根据需求动态调整。
-
访问速度:栈的访问速度比堆快,因为栈上的数据存储紧凑,访问起来更高效;而堆上的数据存储比较分散,访问速度相对较慢。
-
分配效率:栈的分配效率高,因为栈上的对象分配和释放只需要移动栈指针,速度很快;而堆的分配效率相对较低,因为需要在堆内存中找到足够大小的连续空间。
总结来说,栈适合存储局部变量和函数调用等临时性数据,速度快但容量有限;堆适合存储动态分配的对象和数据,容量大但分配和释放过程比较复杂。
A:006写后感
此篇可能目前是最长同时也是内容最全的了,我对此篇很满意,等下发出去我要给自己抢手赞,呵呵。后语的一些补充资料都是我产生的疑问然后添加上去,不知道大家还有没有疑问?欢迎踩踩啊 (脑子里蹦出“踩”后,为什么有孤忧伤!哦,那是夕阳下我逝去的青春!)