C/C++代码编程可执行程序的过程:
?? ?????????1、预处理 ?? ? ?? ?????????2、编译 ?? ? ?? ?????????3、汇编? ?? ? ?? ?????????4、链接?
什么是makefile脚本:?
?? ?Makefile脚本集合了程序的编译指令的文件,make是一个命令工具,当执行make命令时,它会自动读取MakeFile中的编译指令并执行,会自动完成整个项目的自动化编译工作。 ?? ? ?? ?为什么需要Makefile脚本: ?? ??? ?项目中如果有很多.c文件,他们的编译指令会有很多,需要的编译时间比较长,依赖关系非常复杂。 ?? ??? ?当项目中的.h文件被修改时,.c文件,我们无法人为分辨出哪些文件需要重新编译,只能全部重新编译一下,但这项操作非常耗时。 ?? ??? ?所以使用makefile脚本编译项目:(按重要性排序) ?? ??? ??? ?1、节约时间 ?? ??? ??? ?2、记录文件之间的依赖关系 ?? ??? ??? ?3、自动化执行编译过程 ?? ??? ??? ??
Makefile脚本的原理:?
?? ?Makefile脚本的原理就是基于文件的最后修改时间,被依赖文件的最后修改时间晚于目标文件,该文件就需要重新编译。 ?? ??? ?如果hello.c > hello.c 那么gcc -c hello.c ?? ??? ??? ?hello.h > hello.o ? ? ? ? ? ? gcc - c hello.c?
Makefile脚本的格式:?
?? ?由若干个编译目标组成,它类似C语言中的函数,就是若干个编译指令组成的编译模块,默认只执行排在第一个的编译目标,也叫入口目标。 ?? ??? ? ?? ?target ...: prerequisites ... ?? ??? ?command ?? ??? ?... ?? ??? ?... ?? ? ?? ?target:编译目标,它如果不存在,或者早于它依赖的文件,那么就执行该下面的编译指令。 ?? ?prerequisites:被依赖的编译目标、文件,相当于c语言的函数调用。 ?? ?command: 编译指令?
在makefile脚本中可以使用变量:?
?? ?变量名=value 定义变量并给变量赋初值 ?? ? ?? ?$(变量名) 使用变量名,获取出变量的值。 ?? ?常用变量名: ?? ??? ?CC=编译器 ?? ??? ?STD=语法标准 ?? ??? ?FLAG=检查标准, -wall、 -werror ?? ??? ?TARGE=最终可执行文件名字 ?? ??? ?OBJECT=所有的目标文件名?
负责清理的编译目标:?
?? ?该编译目标一般负责删除目标文件、头文件的编译结果、可执行文件。 ?? ?一般它不会被依赖,也就是不会执行,而是当需要时,在命令行通过make 目标名,手动执行。 ?? ?什么时候需要它执行: ?? ??? ?1、刚修改的内容,并没有发生变化,有可能是依赖规则有问题,代码并没有重新编译,那么执行它就可以删除所有目标文件,可执行文件,重新编译 ?? ??? ?2、更换了执行平台后,那么之前编译出的目标文件就全部不能再继续使用。 ?? ??? ?3、项目最终上线时,会把所有的编译结果删除,重新编译。
?? ?clean:
?? ??? ?rm -rf $(BOJECT) $(TARGE)
?? ??? ?rm -rf .h.gch
?? ??? ?
?? ? Makefile 里有什么? Makefile 里主要包含了五个东西:显式规则、隐晦规则、变量定义、文件指示和注释。?
通用的MakeFile脚本?
?? ?CC=gcc ?? ?STD=-std=gnu89 ?? ?FLAG=-wall -werror ?? ?TARGE=最终可执行文件的名字,不同项目灵活修改 ?? ?OJECT=main.o tools.o manager_uesr.o ?? ? ?? ?all: $(OBJECT) ?? ??? ?$(CC) $(OBJECT) -o $(TARGE) ?? ??? ? ?? ?%.o:%.c ?? ??? ?$(CC) $(STD) $(FLAG) -c $< ?? ??? ? ?? ?name.o:name.c name.h(没有这里的话则依赖规则不完整) ?? ??? ?$(CC) $(STD) $(FLAG) -c name.c ?? ?.... ?? ? ?? ?clean: ?? ??? ?rm -rf $(OBJECT) $(TARGE) ?? ??? ?rm -rf .h.gch ?? ??? ??
?常见的问题:?
?? ?1、使用makefile的好处 ?? ??? ?节约编译时间 ?? ??? ?记录文件的依赖关系 ?? ??? ?自动执行编译过程 ?? ?2、Makefile的原理 ?? ??? ?1、在命令行执行make命令(make是安装在操作系统上的读取、解析、执行makefile的工具) ?? ??? ?2、make命令会读取当前目录下的makefile、makefile文件中的内容,解析里面的编译目标。 ?? ??? ?3、根据依赖关系和文件的最后修改时间,来判断是否需要执行编译命令以达到节约时间的目的。 ?? ?3、makefile脚本中有哪些内容 ?? ??? ?变量 ?? ??? ?入口目标 ?? ??? ?被依赖的目标 ?? ??? ?伪目标 ?? ??? ?注释 ?? ??? ?可执行编译指令 ?? ??? ?系统命令?
文件分类:?
?? ?int num = 2048; ?? ?二进制文件:把数据的=补码直接写入文件,这种文件叫二进制文件 ?? ?优点: ? ? 1、读写和写入时不需要进行转换,所以读写速度快 ?? ??? ??? ?2、数据安全性高 ?? ?缺点: ? ? 1、不能使用文本编辑器打开,无法阅读 ?? ? 0000 0000 0000 0000 0000 1000 0000 0000 ?? ?文本文件:把数据转换成字符串写入文件,这种文件叫文本文件, ?? ?优点:1、能够被文本编辑器打开,人类能看得懂,能看出数据是否出错。 ?? ?缺点:1、读写时需要转换,读写速度慢,数据有被修改的风险。 ?? ?“2048” ?? ?2’‘0’‘4’‘8’ ?? ?50 ? ? 48 ? ?52 ? ?56 ?? ?00110010 00110000 00110100 00111000
?? ?总结:二进制文件的大小时确定的,文本文件会根据数据的内容而变化,大小不确定?
?
打开关闭文件:?
?? ?
FILE *foen(const char *path,const char *mode);
?? ?功能:打开文件 ?? ?path:文件的路径 ?? ?mode:文件的打开模式 ?? ?返回值:文件结构指针,是后续操作文件的凭证,失败的话会返回NULL ?? ? ?? ?文件的打开模式: ?? ??? ?“r”以只读方式打开文本文件,如果文件不存在,或文件没有读权限则打开失败 ?? ??? ?“w”以只写方式打开文本时,如果文件不存在,则创建,如果文件存在则清空文件的内容,如果文件存在且没有写权限,则打开失败 ?? ??? ?“a”以只写方式打开文本时,如果不存在则创建,如果文件存在则新写入的内容追加到文件末尾,如果文件存在且没有写权限,则打开失败 ?? ??? ? ?? ??? ?“r+”以读、写方式打开文本文件,如果文件不存在,或文件没有读写权限则打开失败。 ?? ??? ?“w+”以读、写方式打开文本文件,如果文件不存在,则创建,如果文件存在则清空文件的内容,如果文件存在且没有读、写权限,则打开失败 ?? ??? ?“a+”以读、写方式打开文本时,如果不存在则创建,如果文件存在则新写入的内容追加到文件末尾,如果文件存在且没有读、写权限,则打开失败 ?? ??? ? ?? ??? ?以文本方式打开文件时,如果要写入的字符是'\n'时,系统会写'\n'和'\r',两个字符,在windows系统中以'\n'+'\r'表示一个换行,在读取数据时,遇到'\n''\r'只会读取一个'\n'。 ?? ??? ?也就是当写入数字0x0a时,会写入0x0a0d,在Linux、Unix系统下则只会写入'\n'一个字符 ?? ??? ?,操作是不需要加b
?? ??? ?注意:如果要操作二进制文件,则在以上模式的基础上增加b,eg:“rb+”
?? ??? ? ?? ?
int fclose(FILE *stream);
?? ?功能:关闭文件 ?? ?返回值:成功返回0,失败返回-1
?? ?注意:不能重复关闭,否则会出现double free的错误,为了防止出现野指针,文件关闭后最后把文件指针赋值为NULL,及时关闭文件可以把缓冲区中的数据写入到文件中。?
?文本文件的读写:
??
?int fprintf(FILE *stream, const char *format, ...);
?? ?功能:把若干个变量以文本格式写入到指定文件中 ?? ?stream:要写入的文件 ?? ?format:占位符+转义字符+ ?? ?...:若干个变量 ?? ?返回值:写入字符的数量 ?? ? ?? ?下面两个函数的功能fprintf也能实现
?? ?
int fputc(int c, FILE *stream);
?? ?功能:写入一个字符到文件中 ?? ?
int fputs(const char *s, FILE *stream);
?? ?功能,写入一个字符串到文件中 ?? ? ??
?int fscanf(FILE *stream, const char *format, ...);
?? ?功能:从文件中读取数据 ?? ?stream:要读取的文件 ?? ?format:占位符 ?? ?...:若干个变量的地址 ?? ?返回值:成功读取的变量个数 ?? ?
?? ?char *fgets(char *s, int size, FILE *stream);
?? ?int getc(FILE *stream);?
?
二进制文件的读写:?
?? ?注意:以二进制格式读写文件时,最好加上mode最好包含b ?? ?
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
?? ?功能:把一块内存当做数组,然后数组中的内容写入到文件中 ?? ?ptr:写入的内容首地址(数组首地址) ?? ?size:一次写入的数字数(数组元素的字节数) ?? ?nmemb:写入的次数(数组的长度) ?? ?stream:要写入的文件 ?? ?返回值:实际写入的次数
?? ?
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
?? ?功能:把二进制文件中的内容读取的数组中 ?? ?ptr:要存储数据的数组首地址 ?? ?size:数组元素的字节数 ?? ?nmemb:数组的容量 ?? ?返回值:成功读取的次数 ?? ?
?? ?注意:如果以fwrite/fread读写的字符串,那么我们操作的依然是文本文件
文件位置指针:?
??? ?文件位置指针它记录着读写文件时的位置,读取数据时从文件位置指针处读取,写入数据时也会写入到文件位置指针所指向的位置,并且它会随着读写操作自动移动。 ?? ?以“r”、“r+”方式打开文件,文件位置指针指向文件的开头。 ?? ?以“a”、“a+”方式打开文件,文件位置指针指向文件的末尾。 ?? ? ?? ?
int ?fseek(FILE ?*stream, ?long ? offset, ? int whence);
?? ?功能:设置文件的位置指针 ?? ?stream:要设置的文件 ?? ?offset:偏移值 ?? ?whence:基础位置 ?? ??? ?SEEK_SET?? ?文件开头 ?? ??? ?SEEK_CU?? ?当前位置 ?? ??? ?SEEK_END?? ?文件末尾 ?? ?whence + offset 就是文件指针最终设置的位置 ?? ?返回值:成功返回0,失败返回1 ?? ? ??
?long ftell(FILE *stream);
?? ?功能:返回文件位置指针指向第几个字节 ?? ? ??
?void rewind(FILE *stream);
?? ?功能:把文件位置指针调整到文件开头。
?
文件操作时的局限性:?
?? ?文件的内容是连续存储在硬磁盘上的,所以就导致以下操作: ?? ??? ?向文件中插入数据: ?? ??? ??? ?1、文件位置指针调整到要插入的位置。 ?? ??? ??? ?2、把后续的数据整体向后拷贝n个字节(要插入的数据字节数) ?? ??? ??? ?3、文件位置指针调整到要插入的位置,写入数据 ?? ??? ?从文件中删除数据: ?? ??? ??? ?1、文件位置指针调整到要删除的数据末尾; ?? ??? ??? ?2、把后续的数据整体向前拷贝n个字节(要删除的数据字节数) ?? ??? ??? ?3、修改文件的大小。 ?? ?所以,程序在运行时,先把数据从文件中加载到内存中,程序在运行期间只针对内存进行增删改查,程序结束时再把数据从内存写入到文件?
文件管理:?
?? ?
int remove(const char *pathname);
?? ?功能:删除文件 ?? ? ??
?int rename(const char *oldpath, const char *newpath);
?? ?功能:重命名文件 ?? ? ??
?int truncate(const char *path, off_t length);
?? ?功能:把文件的内容设置成length字节数 ?? ? ??
?char *tmpnam(char *s);
?? ?功能:生成一个与当前文件系统不重名的文件名。(整个磁盘的范围) ?? ? ?? ?
int access(const char *pathname, int mode);
?? ?功能:检查文件的权限 ?? ?R_OK 读权限 ?? ?W_OK 写权限 ?? ?X_OK 执行权限 ?? ?F_OK 文件是否存在 ?? ?返回值: ?? ??? ?检查的权限如果存在则返回0,不存在则返回-1。?
结构体序列化:?
?? ?把结构体变量转换成字符串的过程中序列化,反序列化就是把字符串转换成结构变量的过程。 ?? ?为什么把结构变量转换成字符串: ?? ??? ?1、网络通信时传输的是大端序列,而个人计算机使用的是小端序列,为了避免传输时各成员的大小端转换,所以把结构转换成字符串传递更方便。 ?? ??? ?2、在使用SQL语言操作数据库时,SQL是以字符串形式操作数据库的。 ?? ?方法一:使用sprintf/scanf对简单的结构变量进行序列化和反序列化。 ?? ??? ?sprintf(arr,“提示信息+占位符”,若干个结构.成员变量); ?? ??? ?sscanf(str,“提示信息+占位符”,&若干个结构.成员变量); ?? ??? ? ?? ?方法二:把结构变量转换成json格式的字符串,了解cjson库的使用 ?
经典开源项目的学习步骤:?
?? ?1、阅读README文件 ?? ??? ?版权信息介绍 ?? ??? ?使用方法介绍 ?? ??? ?编译方法介绍 ?? ??? ?原理、示例、局限性 ?? ??? ?依赖的工具、库 ?? ??? ?使用时的注意事项 ?? ? ?? ?2、编译项目,执行测试程序,阅读测试代码 ?? ?3、查看示例代码,学习如何使用 ?? ?4、查看源码,进行学习,上网寻找一些对源解析的文章、博客 ?? ?5、重新造个轮子?
main函数参数:?
??? ?完整的main函数格式: ?? ?
int main(int argc,const char* argv[ ])
?? ? ?? ?我们在命令行执行程序时,可以附加一些参数,这些参数会以字符串形式传递给main函数 ?? ?argc:代表字符串个数,也就是指针数组的长度 ?? ?argv:指针数组,里面存储着每个字符串的首地址。 ?? ?
?? ?for(int i=0; i<argc; i++)
?? ?{
?? ??? ?
?? ?}
?? ? ?? ?./a.out 执行程序时,argc的值是1
?数据加密:
?? ?对称加密: ?? ??? ?明文 经过处理 得到密文 ?? ??? ?密文 经过解密 得到明文 ?? ??? ?最简单的对称加密: ?? ??? ??? ?A ^ B = C ?? ??? ??? ?C ^ B = A ?? ??? ??? ?A ^ C = B ?? ??? ? ?? ?非对称加密: ?? ??? ?数据经过加密处理能得到独一无二的密文,而密文无法还原出明文,这种算法一般用于用户登录、验证文件的完整性 ?? ??? ?md5算法是一种非对称加密?
|