一.gcc编译器
流程:(1)预处理:处理头文件的展开,宏替换等工作,不会进行语法的检查.
(2)编译:使用C语言编译工具,将处理后的C代码编译为汇编语言,同时会进行语法的检查.
(3)汇编:使用汇编器,将汇编代码编译为二进制目标文件,不能单独进行运行.
(4)链接:将多个二进制目标文件与库链接,生成可执行的二进制文件.
1.编译报错:
(1)头文件错误:在编译阶段出现,通常为头文件路径错误或文件名写错
(2)语法错误:在编译阶段出现,通常为标点符号或中文问题.
(3)未定义标识符:变量/函数没有定义或声明就进行使用.
(4)归档错误/链接错误:引用了其他文件或库中的函数,但是没有声明或函数名字错误.
2.逻辑错误:
存在bug,程序却可以运行,但没有达到预期效果甚至是出现错误.
调试工具:软件工具 和 硬件工具(需借助相应的硬件)
(1)printf()打印法测试
(2)GDB调试器: 软件测试工具
二.程序
本质:操作内存
指针:一个存放内存地址的变量
野指针:指针所指向的内存不可知或不确定或非法(不可访问)
空指针:指针所指的内存中存的值为0,0地址不允许访问
void* 类型:表示万能指针,可以指向任意类型;在进行* 操作前需要进行强制类型转换
指针的访问: *(指针取值运算符)
1.* 只能对有具体类型的指针进行操作
2.指针必须有建立指向关系,不能对野指针或空指针进行操作
指针的运算操作:
指针 + / - 一个整数,结果任然是一个指针
本质:地址的移动, + 向大地址, - 向小地址
1.大地址 - 小地址 :结果为一个整数,其大小为两个地址之间相隔的类型个数
注:(1)两个地址必须在同一段连续的内存空间上
(2)两个地址的类型必须要一致
2.关系运算:本质是判断两个指针在内存上的地址位置关系
p > q p地址 在 q地址的 大方向
p < q p地址 在 q地址的 小方向
p == q p和q指针指向同一个内存
p != q p和q指针指向不同的内存
p >= q p地址 在 q地址的 大方向 或 相同
p <= q p地址 在 q地址的 小方向 或 相同
指针数组:
数组的元素是指针
定义: 存储类型 元素类型 数组名[元素个数] = {};
指针与函数:
指针函数:本质是一个函数,返回值是一个指针
函数指针:本质是一个指针,返回值是一个函数
代码段:存储代码
函数名:1.代指函数本身
2.代指函数的首地址
函数指针数组:
本质是一个数组 数组中元素是 函数指针
递归函数:
函数中 存在 自己调用自己 (递推关系)
每次自己调用自己 问题规模在逐渐减小 , 向临界条件趋近
临界条件: 退出自己调用自己 的条件
结构体:
是一个构造数据类型
结构:
struct 类型名
{
成员类型 成员名;
........
};
使用类型定义变量 : 用结构体类型 定义变量 才会分配内存空间
结构体变量 成员引用:
. 成员引用运算符
结构体变量名.成员名 ;
结构体变量 类型相同可以直接赋值;
结构体指针
一个指针指向一个结构体
通过结构体指针 访问结构体
-> 结构体指针成员引用运算符
类型别名 给类型取别名
typedef 原类型名 别名;
结构体存储模型:
1.结构体类型 成员顺序 在内存中 按同样顺序排列
2.遵循紧凑排列
3.默认遵循 自动字节对齐
共用体:
跟结构体类似, 区别在于 共用体成员 共用 同一块内存空间
其占用内存大小由 最大空间的那个成员决定
定义 共用体类型 :
union 共用体类型名
{
成员类型 成员名;
....
};
动态内存分配:
就是向操作系统 申请堆区内存
可以在程序运行中 动态的 申请你想要的大小
内存的申请 与 释放:
#include <stdlib.h>
void *malloc(size_t size); //申请内存
void free(void *ptr); //释放内存
size: 要申请的内存大小 单位字节
void *: 返回值 是一个无类型指针
申请成功时 该指针指向 你申请的堆区内存
申请失败 返回 NULL指针
使用堆区内存时, 必须先将堆区内存地址 强制转换为 你要的内存 ,然后才能访问
ptr: 你申请时 系统给的堆区地址, 释放是需要作为free的参数
free(ptr); //释放地址
关于申请与释放 注意事项:
1. 申请时 给的堆区地址, 在没有释放前,不能丢失
丢失堆区指针,将导致 内存泄漏
生命周期 从申请开始 到释放结束
2. 已经释放的 堆区内存 不能再访问了 继续访问 则段错误
3. 不能重复释放
4. 不能部分释放