gcc处理过程
预处理
-
命令:gcc -E
-
生成的文件 .i
-
做的事情:
-
头文件展开:展开的时候都做了些什么?
头文件中有什么,统统把它转移到包含这个头文件的源文件里(对头文件里的默认可以出现的东西都显示出来,如果有条件编译,就要看条件,显示)
-
宏定义替换 ps 展开所有定义的宏,并删除#define
-
对宏定义的内容进行替换
-
’#‘ 将s转变成字符串
#define var(s) #s var(fasf); /*结果是"fasf"*/
-
‘##’连接两个字符串
#define var(s,e) s##e var(dsaf,fdasf); /*结果是”dsaffdsaf“*/
-
等等内容
-
-
条件编译 ps 条件编译的命令有 #ifdef #ifndef #if #endif #else
-
#ifndef __HEAD_H__ #define __HEAD_H__ #endif
作用:防止头文件被重复包含;当头文件第一次被包含时,它被正常处理,符号__HEAD_H__被定义为1,如果头文件被再次包含,通过条件编译,它的内容被忽略,符号__HEAD_H__按照被包含头文件的文件名进行取名,以避免由于其他头文件使用相同的符号引起冲突。
但是,预处理器仍会将整个头文件读入,即使这个头文件所有内容将被忽略,由于这种处理将拖慢编译速度,所以如果可能,应该避免出现多重包含
会出现类型或变量的重复定义
所以类型或变量定义与声明的区别是??
定义:会分配空间,
声明:不会分配空间,告诉编译器 变量的类型,在哪里。以及函数的(返回值类型,参数类型,个数)
变量:
int a;既是一个声明,也是一个定义;
extern int a;是一个声明;
extern int a = 10;会报错;
所以声明包含定义;
定义只能定义一个,声明可以声明多次。
所以在头文件中引用变量最好是添加extern;函数: 声明没有函数体,定义有函数体。
当使用增量编译的时候,这个文件会重新编译,增加的是编译时间,不是运行时间
在头文件中常包含的内容:函数声明,使用#define或者const定义的符号常量,类声明,结构体声明,模板声明,内联函数,
由于可能包含的头文件中有自己文件中重复定义的全局变量,所以在使用时有extern修饰。最好是不在头文件中声明变量,当使用是,一定要加extern。
-
-
删除注释 ps // /**/的内容
-
添加行号和文件名标识
-
保留#pragma命令 ps指示编译器完成特定的动作(windows平台)
文本替换,宏命令展开,不进行语法检查,以及类型检查
使用宏定义在编程的时候方便,程序执行效率高,条件编译的好处是可以更好的兼容各个平台,头文件包含体现了模块化编程的思想
为什么宏定义的程序执行效率更高?
因为调用宏比调用函数更有效率,函数的调用必须要将程序的执行顺序转移到函数所存放的内存地址中,将函数程序内容执行完后,再返回到执行该函数前的地方,这种转移操作要求执行前要保存现场并记忆执行地址,转回后要恢复现场,并按原来保存的地址继续执行,因此,函数调用有一定的时间和空间的开销,而宏只是在预处理的地方把代码展开,不需要额外的时间和空间开销,所以调用一个宏比调用一个函数更有效率
-
编译
汇编
链接
-
命令:gcc
-
生成的文件
-
做的事情:
链接库文件 静态库是在此过程中编译进程序中; 动态库在此过程中把库的地址编译进程序中 所以动态库把位置放到了哪里??或者编译的时候需要动态库的地方填的是什么??? 动态库移植的时候要放到同样的位置??? 移植库的时候为什么需要arm跟Linux的路径一致???? 静态库的后缀名是?? lib<静态库名>.a 如何生成静态编译库?? 先生成.o 文件 as 如何生成动态编译库?? -L是指定库文件所在的路径 -l是指定要用哪一个库, -I是头文件???
C语言的可移植性是在哪一方面体现的,可以用同一个.o文件吗?
还是用同一个源文件?
C语言的可移植性体现在代码上,可移植性并不代表程序不用改,而是做很少的改变,就可以在另一个平台上使用。或者可以让其他人很好的理解自己写的代码,可移植性本质上是有一个既简单又统一的标准去做某件事,
实现可移植性的方法,一是调用标准库函数,二是把自己写的代码尽可能的适应其他的编译器,而不是只对自己的编译器起作用,所以,当测试的时候,不可以只在一种编译器实现,可以让其他人帮忙测试。三是把不可移植的代码剔除出来。