c语言更进一步

递归函数的代码的特点是短小精悍,但是递归函数也有致命的弱点:效率低且容易

使得栈溢出。8M

回调函数用到的最基本的语法是函数指针

实现了典型的回调机制,你定制了一位做任何菜都死命放辣椒

的大厨,然后你在调用前台小妹的同时,将大厨的电话号码也给到她,小妹在需要做饭的时

候,就会根据你提供的参数(大厨的电话)回过去调用(回调!)那位大厨,做好饭后小妹

再细心地捯饬捯饬,端给你吃。

大厨就是一个回调函数------一个不被设计者(你)直接调用,而

是被其他人(小妹)回过来调用的函数。你传递给小妹的电话号码,相当于一个能找到大厨

的指针,被称之为函数指针,回调机制就是靠传递函数指针来告知回调函数的位置的。

复制代码
9 // 定义一个回调函数,以后内核需要信号响应函数的时候会调用该函数
10 void sighandler(int sig)
11 {
12 printf("sig : %d\n", sig);
13 }
14
15 int main(void)
16 {
17 // 传递函数指针,预先告知内核回调函数 sighandler 的位置
18 signal(SIGINT, sighandler);
19
20 // 将信号 SIGINT 传递给内核,要求内核开始处理信号 SIGINT
21 pid_t myPID = getpid( );
22 kill(myPID, SIGINT);
23
24 return 0;
25 }

内存这栋大楼的房间数多得惊人!他们的编号从 0x00000000 开始,到 0xFFFFFFFF,

总计达 2的32次方个房间!每一个房间(字节/byte)包含 8 个比特/bit,每个比特可以存放一个

1 或者 0,一图顶万言,附上一张内存的性感照片:

显示的是一个占据了 4 个字节的 int 型数据(值为 0x00000007),每一个字节都有其对应的地址,编译器会将最小的地址 0x0A33FF04 作为整个 int 型数据的地址,这个最小的地址就是所谓的基地址。以后当我们提到一个变量的地址的时候,指的都是基地址。

在 32 位系统中,任何地址都是 4 字节的。

但有些时候,我们也许无法在定义指针的同时,立即给他一个有效的地址,而是在程序

运行到下一个恰当的某个时候再赋值,这期间就有个空档期,此空档期内指针 p 的值将是

一个随机值,而这个随机值是一个地址,如果在此种状况下不小心对指针 p 进行了解引用,

程序就会访问非法内存,导致本程序崩溃甚至更严重的后果,这样的未初始化的指针我们称

之为野指针:

一般而言,对于一个暂时无法让其指向一块合法内存的指针而言,我们最好将其初

始化为"空指针",即给他赋一个空值(零),让他指向零地址:

NULL 实际上这是一个宏:#define NULL (void *)0,从值上看,NULL 就是地

址 0x0000 0000,从类型上看,空指针是一种通用型指针,关于 void 型指针,有以

下总结:

1,当无法确定一个地址所对应的内存的数据类型时,将该地址类型定为 void 型,

即通用型。比如:

void *p = malloc(8); // 申请了一块未知用途的内存(8 个字节)

2,void 型指针在解引用时,必须转化为某种具体的数据类型指针,否则无法解引

用。比如:

double f = 3.14;

*p = f; // 这是错误的,因为指针 p 是通用类型指针

*(double *)p = f; // 这是正确的,因为进行了相应的类型转化

const 在 C 语言中的主要用法不是用来定义普通变量,而是用来定义指针

内存管理

第一,栈内存。

栈内存(以下简称栈)指的是从 0xC000 0000 往下增长的这部分内存区域,之所以

被称为"栈"是因为进程在使用这块内存的时候是严格按照"后进先出"的原则来操作的,

而这种后进先出的逻辑,就被称为栈。

栈的全称是"运行时栈(run-time stack)",顾名思义栈会随着进程的运行而不断

发生变化:一旦有新的函数被调用,就会立即在栈顶分配一帧内存,专门用于存放该函数内

定义的局部变量(包括所有的形参),当一个函数执行完毕返回之后,他所占用的那帧内存

将被立即释放),在上图中用一根虚线和箭头来表示栈

的这种动态特征。

栈主要就是用来存储进程执行过程中所产生的局部变量的,当然为了可以实现函数的嵌

套调用和返回,栈还必须包含函数切换时当下的代码地址和相关寄存器的值,这个过程被称

为"保存现场",等被调函数执行结束之后,再"恢复现场"。因此,如果进程嵌套调用了

很多函数,就会导致栈不断增长,但是栈的大小又是有一个最大限度的,这个限度一般是

8MB,超过了这个最大值将会产生所谓的"栈溢出"导致程序崩溃,所以我们在进程中不

宜嵌套调用太深的函数,也不要定义太多太大的局部变量。

第二,堆内存。

堆内存(以下简称堆)是一块自由内存,原因是在这个区域定义和释放变量完全由你来

决定,即所谓的自由区。堆跟栈的最大区别在于堆是不设大小限制的,最大值取决于系统的

物理内存。

堆的全称是"运行时堆(run-time heap)",跟栈一样,会随着进程的运行而不断

地增大或缩小,由于对堆的操作非常重要,下一小节用来专门讨论堆的相关细节。

第三,数据段。

数据段实际上分为三部分,地址从高到底分别是.bss 段、.data 段和.rodata 段,三个

数据段各司其职:.bss 专门用来存放未初始化的静态数据,它们都将被初始化为 0,.data

段专门存放已经初始化的静态数据,这么初始值从程序文件中拷贝而来,而.rodata 段用来

存放只读数据,即常量,比如进程中所有的字符串、字符常量、整型浮点型常量等。

第四,代码段。

代码段实际上也至少分为两部分:.text 段和.init 段。.text 段用来存放用户程序代码,

也就是包括 main 函数在内的所有用户自定义函数,而.init 段则用来存储系统给每一个可

执行程序自动添加的"初始化"代码,这部分代码功能包括环境变量的准备、命令行参数的

组织和传递等,并且这部分数据被放置在了栈底。

相关推荐
csbysj20201 小时前
W3C XML 活动
开发语言
Max_uuc1 小时前
【C++ 硬核】消灭 void*:用 std::variant 实现嵌入式“类型安全”的多态 (Type-Safe Union)
开发语言·c++
枫叶丹42 小时前
【Qt开发】Qt系统(十)-> Qt HTTP Client
c语言·开发语言·网络·c++·qt·http
Allen_LVyingbo2 小时前
医疗大模型预训练:从硬件选型到合规落地实战(2025总结版)
开发语言·git·python·github·知识图谱·健康医疗
范纹杉想快点毕业2 小时前
自学嵌入式系统架构设计:有限状态机入门完全指南,C语言,嵌入式,单片机,微控制器,CPU,微机原理,计算机组成原理
c语言·开发语言·单片机·算法·microsoft
九皇叔叔2 小时前
【07】SpringBoot3 MybatisPlus 删除(Mapper)
java·开发语言·mybatis·mybatis plus
只是懒得想了2 小时前
Go服务限流实战:基于golang.org/x/time/rate与uber-go/ratelimit的深度解析
开发语言·后端·golang
星火开发设计5 小时前
枚举类 enum class:强类型枚举的优势
linux·开发语言·c++·学习·算法·知识
喜欢吃燃面10 小时前
Linux:环境变量
linux·开发语言·学习