C++的编译系统,都包括了,预处理、编译、连接等部分,在编译时一气呵成。
一、关于预处理;预处理命令是c++统一规定的但不是c++语言的本身的组成部分,不能直接对它进行编译,因此在编译之前先要对源文件进行预处理;预处理命令以#开头,不包括";"
预处理功能主要有三种方式,宏定义、文件包含、条件编译
1.1、宏定义:
#define SIZE 100
#define INT int
带参数的宏定义,宏函数
#define REGISTER(TYPE)\
void show##TYPE()\ //'##'字符串黏贴
{\
printf(#TYPE"123");\'#'变量字符串化
}
注:因为宏定义只是简单的字符替换没有类型检测的概念,因此使用是需谨慎;
1.2、文件包含:
#include <>、#include " "、两者之间就是搜索路径的不同。
1.3、关于条件编译:预处理变量有两种状态,通过#ifndef、#ifdef 来决定到#endif 之间的代码是否有效
#ifndef TEST
#define TEST
#endif
/----------------------------
#ifdef TEST
#else
#endif
/_________________
#if 表达式
#error " 字符串"
#elif 表达式
#else
#endif
/_________________
#pragma once 头文件只被包含一次,编译器相关
#pragma warning (disable :4786) 取消使用STL容器时,因模版展开后名字太长引起;
#pragma pack(8) 更改编译器缺省字节对齐方式
#line 100 从这个定义开始是改行是100行,更改行号
二、关于编译;
一个较大的程序不可能由一个人从头到尾完成,更不可能将所有的处理都放进主函数中,因此需要根据功能进行模块的划分,把不同的模块放进不同的文件中,在编译时以文件为单位,从上之下分别编译,分别通过编译之后,才会将各个目标模块和系统文件连接成为一个可执行文件;
编译一般分为两次进行,语法分析和代码生成;程序可以由多个文件组成,当一个文件中的函数可能要访问另外一个文件中的函数和变量,C++编译器需要知道在另一个文件中的这些函数,和变量特别是它的名字和基本用法,这就用到了函数和变量的声明,声明就是告诉编译器这个标识符可以在某处找到;无论定义的是函数还是变量,编译器都要为它们在定义点分配存储空间;
三、关于链接;
连接器,就是把一组目标模块连接成为一个可执行程序,某个目标模块中的函数需要引用另外一个目标模块中的函数或变量时,由连接器处理这些引用。
连接器,能搜索称为库的文件,来处理它的引用,库将一组目标模块包含在一个文件中;
连接时如果某个函数或者变量的定义还没有遇到,就把它的标识符加到“为解析的引用”列表中,如果连接器在目标模块列表中没有找到函数或者变量的定义,就去查找库;找到之后仅将包含该定义的目标模块连接到可执行文件中去;
连接时,只需要告诉连接器目标模块的和要连接的库的名称以及可执行文件的名称,连接器就可以开始连接的任务;
当创建可执行文件的时候,连接器会秘密链接某些模块,之一有启动模块,用来初始化例程;
连接器总是从标准库中查找经过编译的函数,由于标准库总是可以被找到,所以只需要包含头文件就可以,并不需要高速编译器去链接标准库,当使用附加库的时候,就必须要把该库的文件名称添加到连接器处理的列表文件中去;
注:在一个执行程序中,标识符代表存放变量或者被编译过的函数体的存储空间。连接用连接器所见的方式描述存储空间,连接的方式就有,内部连接和外部连接;
内部连接意味着只对正在编译的文件创建存储空间,这样别的文件就可以用相同的标识符或全局变量;内部连接是由static关键字指定的;
外部连接指,为所有编译过的文件创建一片单独的存储空间,连接器需要处理所有对这片空间的引用;全局变量和函数名的外部连接通过extern声明。函数之外的定义的所有变量和函数默认为外部连接,也可以用static强制其有内部连接;const 定义的变量不是外部连接(有例外情况下文详见)
四、内部连接的一些案例;
1、下面对,内部变量与外部变量,内部函数跟外部函数
全局的变量为外部变量int a,要想别的文件中访问需要extern int a;声明,但可以在前面加上static int a;使其成为内部变量,这样即使在别的文件中extern int a;声明也不能引用内部变量;
C++中可以用为命名的命名空间定义局部与文件的实体,相当于用static声明的文件静态变量;
namespace
{
int a ;
} 如果在头文中定义了未命名的命名空间,那么在每个包含该头文件的文件中,该命名空间中的名字将定义不同的实体
函数默认是外部函数,可以在别的文件汇总extern void show();声明函数来使用,extern可加可不加;也可以在函数前面加上static 使其成为内部函数,文件静态;这样在别的函数中用声明之后使用是不可以的;
2.基于static可以使函数成为内部连接,所以可以将static修饰的函数的定义放进头文件,而不会产生重定义;
3.在函数外定义的const 常量默认是内部连接,const int a =5;但是可以用extern const int a=5;来显示的将其改为外部连接,这样就可以使用extern const int a=5;来引用这个常量,所有将const 常来那个放入头文件中不会出现重复定义;
对于匿名的enum和命名的enum中的常量可以直接访问;这样可以代替const 定义的常量;
4、inline 函数;
在类内部定义的函数,都会自动的转化为内联函数,内联函数会在适当的地方像宏一样展开,将函数调用替换为函数体,所以不需要函数调用的开销。在类外定义的函数不会转换为内联函数。
可以在非类的函数前面加上inline 使之成为内联函数,为了使之有效必须函数的声明在一起,否则编译器做为普通函数处理;内联函数的定义和声明放在头文件中不会产生重定义的情况;
5、头文件中定义模版
即使在创建非内联函数的时候,我们还是把模版的声明和定义都放进一个头文件中,在模版定义template<>之后的任何东西都意味着编译器当时不为它分配空间,而是一直出于等待状态直到被一个模版实例告知;
6、类中的静态成员变量和静态成员函数;
在类中定义的内部类型的static const 常量可以看作是编译期间的常量,必须在static const 定义的地方对它进行初始化;静态常量数组则需要类外的定义;