先举三反一,再举一反三,学习就应该是这样,先模仿,再改进,最后实现自己的创意! –小甲鱼论坛
递归
- 调用函数本身
- 设置递归结束条件
|
|
汉罗塔
|
|
快速排序
|
|
动态内存管理
- malloc
- 申请动态内存空间
- free
- 释放动态内存空间
- calloc
- 申请并初始化一系列内存空间
- realloc
- 重新分配内训空间
malloc函数
函数原型
- void *malloc(size_t size)
malloc函数向系统申请分配size个字节的内存空间,并返回一个指向这块空间的地址。
如果函数调用成功,返回一个指向申请的内存空间的指针,由于返回类型是void 指针(void * ), 所以它是可以被转换成任何类型的数据;如果函数调用失败,返回值是NULL。另外,如果size的参数设置为0,返回值也可能是NULL,但这并不意味着函数调用失败。
free函数
- 函数原型
- void free(void *ptr)
- free函数释放ptr参数指向的内存空间。该内存空间必须是有malloc,calloc或realloc函数申请的。否则,该函数将导致未定义行为。如果ptr参数是NULL,则不执行任何操作。注意:该函数并不会修改ptr参数的值,所以调用后仍然指向原来的地方(变为非法空间)。
|
|
内存泄漏
- 隐式内存泄漏
- 用完内存块没有及时使用free函数释放
- 丢失内存块的地址
初始化内存空间
- mem开头的函数被编入字符串标准库,函数的声明包含在string.h这个头文件中:
- memset – 使用一个常量字节填充空间
- memcpy – 拷贝内存空间
- memmove – 拷贝内存空间
- memcmp – 比较内存空间
- memchr – 在内存空间中搜索一个字符
|
|
calloc函数
- 函数原型
- void *calloc(size_t nmemb, sizet_t size);
- calloc函数在内存中动态地申请nmemb个长度为size的连续内存空间(即申请的总空间尺寸为nmemb * size),这些内存空间全部被初始化为 0.
- calloc函数与malloc函数的一个重要区别是:
- calloc函数在申请完内存后,自动初始化该内存空间为零
- malloc函数不进行初始化操作,里面数据是随机的
|
|
realloc函数
- 如果ptr参数为NULL,那么调用该函数就相当于调用malloc(size)
- 如果size参数为0,并且ptr参数不为NULL,那么调用该函数就相当于调用free(ptr)
- 除非ptr参数为NULL,否则ptr的值必须由先前调用malloc,calloc或realloc函数返回。
|
|
c语言的内存布局
代码段
- 代码段(Text segment)通常是指用来存放在程序执行代码的一块内存区域。这部分区域的大小在程序运行前就已经确定,并且内存区域通常属于只读。在代码段中,也有可能包含一些只读的常熟变量,例如字符串常量等。
BSS段
- BSS段(BSS segment/Uninitialized data segment)通常是指用来存放程序中未初始化的全局变量的一块内存区域。BBS是英文Block Started Symbol的简称,这个区段中的数据在程序运行前将被自动初始化为数字0。
堆
- 堆是用于存放进程中被动态分配的内存段,它的大小并不固定,可动态扩展或缩小。当进程调用malloc等函数分配内存时,新分配的内存就被动态调价到对上;当利用free等函数释放内存时,被释放的内存从堆中被剔除。
栈
- 栈是函数执行的内存区域,通常和堆共享同一片区域。
堆和栈的区别
- 申请方式
- 堆是由程序员手动申请
- 栈时系统自动分配
- 释放方式
- 堆是由程序员手动释放
- 栈由系统自动释放
- 生存周期
- 堆的生存周期由动态申请到程序员主动释放位置,不同函数之间均可自由访问。
- 栈的生存周期由函数调用开始到函数返回时结束,函数之间的局部变量不能互相访问。
高级宏定义
- 本质就是替换
内联函数
在程序函数调用时,直接展开,而不是去查找调用。
内联函数嵌入调用者代码中的操作是一种优化操作,因此只有进行优化编译时才会执行代码嵌入处理。若编译过程中没有使用优化选项**‘-o’**,那么内联函数的代码就不会真正地嵌入到函数调用者代码中,而是只作为普通函数调用来处理。
内联函数虽然节省了函数调用的时间消耗,但由于每一个函数出现的地方都要进行替换,因此增加了代码的编译时间。另外,并不是所有的函数都能变成内联函数
现在的编译器也很智能,就算你不写inline,它也会自动将一些函数优化成内联函数
不带参数的宏定义
为了和普通的变量进行区分,宏的名字通常我们约定是由大写字母组成
宏定义只是简单地进行替换,并且由于预处理是在编译之前进行,而编译工作的任务之一就是语法检查,所以编译器不会对宏定义进行语法检查
宏定义不是说明或语句,在末尾不必加分号
宏定义的作用是从定义的位置开始到整个程序的结束
可以用 #undef 来终止宏定义的作用域
宏定义允许嵌套
|
|
|
|
带参数的宏定义
|
|
‘#’ 和 ‘##’
- ‘#’ 和 ‘##‘是两个预处理运算符
- 在带参数的宏定义中, #运算符后面应该跟着一个参数,预处理器会把这个参数转换为一个字符串。
|
|
- ‘##‘运算符被称为记号连接运算符,我们可以使用’##‘运算符连接两个参数。
|
|
可变参数
之前学习了如何让函数支持可变参数,带参数的宏定义是使用可变参数的:
1
#define SHOWLIST(...) printf(#__VA_ARGS__)
其中**…**表示可变参数,
1
#__VA_ARGS__
在预处理中被实际的参数集所替代。
|
|
|
|