一、动态内存管理
1、为什么存在动态内存管理?
因为常规的内存分配只能分配固定大小的空间,比如数组和变量的定义,只会分配固定大小的空间。而有时候需要的空间在运行时才能确定。
2、常见动态内存函数
(1)malloc和free
malloc: void* malloc(size_t size);
成功返回指向开辟好空间的指针,否则返回NULL指针。(返回的指针需要进行强转为所需类型)
free: void free(void* ptr);
释放动态内存
(2)calloc
void * calloc(size_t num,size_t size);
为num个大小为size的元素开辟空间,并把空间的每个字节初始化为0;
(3)relloc
void* relloc(void*ptr,size_t size);
对动态开辟的空间的大小进行调整,其中ptr是需要调整的内存地址,size是调整后的大小,返回值是调整后内存的起始位置。
注:relloc函数有可能会开辟一段新的内存,将所有的元素搬移过去(当原空间后面没有足够大的空间的时候)
3、常见动态内存错误
(1)对NULL指针的解引用操作
(2)对动态开辟的内存越界访问
(3)非动态开辟的内存用free释放
(4)使用free释放一块动态内存的一部分
(5)对同一块动态内存多次释放
(6)忘记释放动态开辟的内存
4、c/c++中程序内存区域划分
二、文件操作
1、什么是文件?
一般情况下谈的文件有两种:程序文件和数据文件;
程序文件:包括源文件(.c)、目标文件(.obj)、可执行文件(.exe)
数据文件:文件的内容是程序运行时读写的数据。
2、文件名、文件类型、文件缓冲区、文件指针
(1)文件名
文件名包含三部分:文件路径+文件名主干+文件后缀
(2)文件类型
文本文件(ASCII形式存储的)、二进制文件(二进制方式进行存储)
(3)文件缓冲区
系统会自动在内存中为程序中的每一个正在使用的文件开辟一块“文件按缓冲区”,从内存向磁盘输出的数据会先送到内存中的缓冲区,装满缓冲区后才一起送到磁盘上。
(4)文件指针
文件类型的指针称为文件指针
每个被使用的文件都在内存中开辟了一个相应文件信息区,来存放文件相关信息,这些信息保存在一个结构体变量中,取名FILE;
struct _iobuf {
char *_ptr;
int _cnt;
char *_base;
int _flag;
int _file;
int _charbuf;
int _bufsiz;
char* _tmpfname;
};
typedef struct _iobuf FILE
FILE* pf;
3、文件打开和关闭
FILE * fopen ( const char * filename, const char * mode );
int fclose ( FILE * stream );
mode是打开方式,只读®、只写(w)……
三、程序环境和预处理
1、预编译、编译、汇编阶段的主要工作
(1)预编译(.i):预处理指令(1.宏定义; 2.文件包含; 3.条件编译。 预处理命令以符号“#”开头)
(2)编译(.s):语法分析、词法分析、语义分析、符号汇总
(3)汇编(.o):生成目标文件
(4)链接:合并段表、符号的合并和符号的重定义。
2、宏定义的常见错误
#define SQUARE(x) x*x
int a=5;
printf("%d\n",SQUARE(a+1));
此时会打印25+1=26而不是6*6=36;
#define DOUBLE(x) (x) + (x)
#define SQUARE(x) x*x
int a=5;
printf("%d\n",10*DOUBLE(a));
会打印55而不是100,因为10*(5)+(5)
3、比较函数和宏
宏优于函数的地方:
(1)函数比宏的计算时间要长
(2)函数必须声明为特定类型,而宏是和类型无关的。
宏不如函数的地方:
(1)每次使用宏时,宏定义代码会插入到程序中,当宏定义比较长时,可能会大幅度的增加程序的长度;
(2)宏没有办法调试且会带来符号优先级的问题,导致容易出错;
(3)宏不可以递归