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 段则用来存储系统给每一个可

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

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

相关推荐
无敌昊哥战神1 分钟前
【LeetCode 257】二叉树的所有路径(回溯法/深度优先遍历)- Python/C/C++详细题解
c语言·c++·python·leetcode·深度优先
m0_7269659835 分钟前
面面面,面面(1)
java·开发语言
2401_831920741 小时前
分布式系统安全通信
开发语言·c++·算法
~无忧花开~1 小时前
React状态管理完全指南
开发语言·前端·javascript·react.js·前端框架
m0_488633322 小时前
C语言中枚举类型变量的定义、赋值及使用方法全解析
c语言·枚举类型·实例分析·变量定义·赋值使用
老鱼说AI2 小时前
大规模并发处理器程序设计(PMPP)讲解(CUDA架构):第四期:计算架构与调度
c语言·深度学习·算法·架构·cuda
阿贵---2 小时前
C++中的RAII技术深入
开发语言·c++·算法
Traced back2 小时前
怎么用 Modbus 让两个设备互相通信**,包含硬件接线、协议原理、读写步骤,以及 C# 实操示例。
开发语言·c#
娇娇yyyyyy3 小时前
QT编程(17): Qt 实现自定义列表模型
开发语言·qt