(1)从会敲代码开始,我们就知道编译器的重要性,没有这东西,我们的代码就是一堆字符而已。而编译器编译程序的步骤
主要有四个——预处理、编译、汇编和链接,最后得到可执行的目标文件。
四个步骤各自的工作会是怎么样的呢?不知为何,对这个东西有了点兴趣。以GCC为例,主要工作是这样的:
预处理阶段会调用cpp,
编译阶段会调用cc得到汇编程序,
汇编阶段调用as得到目标代码,
链接时调用链接程序ld 得到可执行文件。
由于四个步骤(其实本质上就是四个可执行代码)早就被编译器打包,一并替我们打理了,我们的工作是轻松了,
对于这几个过程究竟是怎样进行的,恐怕还是有个问号在脑中打转。
后面3个阶段的具体工作机制,我没有深入了解过,但还是可以说说预处理的。
(2)预处理器是在真正的编译开始之前由编译器调用的独立程序。预处理器可以删除注释、包含其他文件以及执行宏(宏macro是一段重复文字的简短描写)替代。在探究预处理的操作机制之前,了解预处理器是个什么东西还是很有必要的。
预处理器不止一种,而C/C++的预处理器就是其中最低端的一种——词法预处理器。
这种预处理器做的主要是进行文本替换、宏展开、删除注释这类简单工作。再具体一点就是,预处理器cpp就是负责展开源文件中的宏,并把”#include ”的内容插入这类的工作。
C/C++预处理器什么都不会做,只是做宏替换和文本替换。
★ C/C++预处理是不会做任何语法检查的,不仅是因为它不具备语法检查功能,也因为预处理命令不属于C/C++语句(这也是定义宏时不要加分号的原因),语法检查是编译器要做的事情。
★ 通过预处理之后,我们得到的是也仅仅是真正的源代码。
★ 还有就是,GCC这个东西确实很强大。如果是用VC这种IDE,恐怕就不能看到,预处理原来是这么个好玩的东西了。
(3)C语言提供的三种预处理功能的其中一种,这三种预处理包括:宏定义、文件包含、条件编译。宏定义和操作符的区别是:宏定义是替换,不做计算,也不做表达式求解。一,宏定义
A:不带参数
格式:#define 标识符 字符串,其中的标识符就是所谓的符号常量,也称为“宏名”。
(1)宏名一般用大写
(2)使用宏可提高程序的通用性和易读性,减少不一致性,减少输入错误和便于修改。例如:数组大小常用宏定义
(3)预处理是在编译之前的处理,而编译工作的任务之一就是语法检查,预处理不做语法检查。
(4)宏定义末尾不加分号;
(5)宏定义写在函数的花括号外边,作用域为其后的程序,通常在文件的最开头。
(6)可以用#undef命令终止宏定义的作用域
(7)宏定义允许嵌套
(8)字符串( " " )中永远不包含宏
(9)宏定义不分配内存,变量定义分配内存。
(10)宏定义不存在类型问题,它的参数也是无类型的。
B:带参数
除了一般的字符串替换,还要做参数代换
格式:
#define宏名(参数表) 字符串
例如:#define S(a,b) a*b
area=S(3,2);第一步被换为area=a*b; ,第二步被换为area=3*2;
类似于函数调用,有一个哑实结合的过程:
(1)实参如果是表达式容易出问题
#define S(r) r*r
area=S(a+b) ; 第一步换为area=r*r ; 第二步被换为area=a+b*a+b ;
正确的宏定义是#define S(r) ((r)*(r))
(2)宏名和参数的括号间不能有空格
(3)宏替换只作替换,不做计算,不做表达式求解
(4)函数调用在编译后程序运行时进行,并且分配内存。宏替换在编译前进行,不分配内存
(5)宏的哑实结合不存在类型,也没有类型转换。
(6)宏展开使源程序变长,函数调用不会
(7)宏展开不占运行时间,只占编译时间,函数调用占运行时间(分配内存、保留现场、值传递、返回值)
记住:#define第一位置第二位置
(1) 不替换程序中字符串“ ”里的东西。
(2) 第一位置只能是合法的标识符(可以是关键字)
(3) 第二位置如果有字符串,必须把""配对。
(4) 只替换与第一位置完全相同的标识符(一个标识符要整体识别不能拆分开分别识别)
特殊例子:
#define FUN(a) "a"
" "内的字符不被当成形参,即使它和参数一模一样,也不会替换成实参对应字符串。
那么,你会问了,我要是想让这里输入FUN(345)它就替换成"345"该怎么实现呢?用下面2)知识:#define FUN(a) #a
C预处理器提供了以下,以帮助您创建宏:
1)Macro Continuation (\)
宏通常必须被包含在一个单一的线。宏延续运算符用于继续宏一行太长。例如:
#define message_for(a, b) \
printf(#a " and " #b ": We love you!\n")
2)Stringize (#)
stringize或数字符号运算符('#'),使用时,在宏定义,宏参数转换成一个字符串常量。这个操作符只可用于在一个宏,有一个指定的参数或参数列表。例如:
#include
#define message_for(a, b) \
printf(#a " and " #b ": We love you!\n")
int main(void)
{
message_for(Carole, Debra);
return 0;
}
When the above code is compiled and executed, it produces the following result:
Carole and Debra: We love you!
3)Token Pasting (##)
在宏定义令牌粘贴运算符(##)结合两个参数。它允许两个单独的宏定义中的令牌(参数)被加入到一个单独的标记
如果有#defineFUN(a,b) vo##a##b()
那么FUN(id ma,in)会被替换成void main()
二,条件编译
#if、#else、#elif和#endif指令
1
2
3
4
5
|
#if表达式
//语句段1
#else
//语句段2]
#endif
|
1
2
3
4
5
6
7
|
#if表达式1
//语句段1
#elif表达式2
//语句段2
#else
//语句段3
#endif
|
#ifdef和#ifndef
1
2
3
|
#ifdef宏名
//语句段
#endif
|
1
2
3
|
#ifndef宏名
//语句段
#endif
|
#error
#line
#pragma
举例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
#include<stdio.h>
#define LETTER1
int
main(
int
argc,
char
*argv[])
{
char
str[20]=
"CLanguage"
,c;
int
i;
i=0;
while
((c=str[i])!=
'\0'
)
{
i++;
#ifdef LETTER1
if
(c>=
'a'
&&c<=
'z'
)
c=c-32;
#else
if
(c>=
'A'
&&c<=
'Z'
)
c=c+32;
#endif
printf
(
"%c"
,c);
}
return0;
}
|
三,文件包含
(1)文件包含的两种格式区别编辑
文件包含有两种格式,分别是:#include "file" 和 #include <file>
这两格式的区别在于:
1.使用双引号,系统首先到当前目录下查找被包含的文件,如果没找到,再到系统指定的"包含文件目录"(由用户在配置环境时设置)去找。
2.使用尖括号:直接到系统指定的"包含文件目录"去查找。
通常使用双引号比较保险
(2)文件包含的特点编辑
文件包含的特点:
① 编译预处理时,预处理程序将查找指定的被包含文件,并将其复制插入到#include命令出现的位置上
② 常用在文件头部的被包含文件,称为“标题文件”或“头部文件”,常以“h”(head)作为后缀,简称头文件。在头文件中,除可包含宏定义外,还可包含外部变量定义、结构类型定义等。
③ 一条包含命令,只能指定一个被包含文件。如果要包含多个文件,则要用多条包含命令。例如,文件f1.h中要使用到文件f2.h和文件f3.h的内容,则可在文件f1.h中用两个文件包含命令分别包含文件f2.h和文件f3.h,即在文件f1.h中定义:
#include "f2.h"
#include "f3.h"
在使用多个#include命令时,顺序是一个值得注意的问题。上例中,如果文件f1.h包含文件f2.h,而文件2要用到文件f3.h,则在f1.h中#include定义的顺序应该是:
#include "f3.h"
#include "f2.h"
这样文件f1.c和文件f2.h都可以使用文件f3.h的内容。
④ 文件包含可以嵌套,即被包含文件中又包含另一个文件。例如,文件f2.h中要使用到文件f1.h的内容,文件f3.h要使用到文件f2.h的内容,则可在文件f2.h中用#include "f1.h"命令,在文件f3.h中用#include "f2.h"命令,即定义如下:
文件f1.h:
{
… …
}
文件f2.h:
#include "f1.h"
int max()
{
… …
}
文件f3.h:
#include "f2.h"
main
{
… …
}
#include命令一般用来把C语言提供的标准库头文件(如stdio.h、math.h)包含到程序中。程序员也可以自己定义一个头文件,写入一些常用的函数原型、宏定义、结构和联合类型定义等,然后将它包含到程序中。例如:
#include "stdio.h" (标准输入/输出函数库)
#include "math.h" (数学函数库)
#include "stdlib.h" (常用函数库)
#include "string.h" (字符串处理函数库)