非典型性C语言教程-0.2 预处理

当你执行cl -o hello.exe hello.c或者 cc -o hello hello.c时,到底发生了一些什么呢?其实cl或是cc实际上调用了另外两个程序。首先调用预处理程序(UNIX下是cpp程序,在Windows下不太清楚),预处理源文件。然后自己编译之,最后调用link.exe或是ld程序来完成连接成生可执行代码。这3个操作是靠管道连在一起的。要是不知道什么是管道就姑且认为,是分3步,用临时文件连在一起的。C语言的预处理机制不属于语言的一部分,但是它的作用非常重要。

在C语言中,以#开头的行都是预处理命令。最常用的预处理命令就是#include,#define。常用的还有#ifdef #ifndef #pragma。 记住一点,大多数预处理命令基本只是执行一个文本替换的过程。其他的预处理指令我们以后再讲,这里只讲讲#inclue。

#include 有两种形式,第一种是#include <stdio.h> , 第二种是#include "my.h"。 这两种形式的区别在于找头文件的默认路径不一样。 对于#include <stdio.h> 会 去INCLUDE环境变量指定的目录去找stdio.h这个文件。而#include "my.h"会在当前目录去找my.h,这个“当前目录”是指使用这条预处理命令的源文件所造的目录。比如,我有一个文件hello.c在C:/Cpp/ 目立下,即C:/Cpp/hello.c ,那么编译的时候预处理程序会在C:/Cpp/下找有没有叫my.h的头文件。按照上次讲的如果你写#include "../my.h"就是在C:/下找my.h这个文件。对于第一种情况可以不改INCLUDE环境变量而针对单个源文件来扩展搜寻的目录。 就是在编译的时候使用 -I参数。

比如我安装了一个附加库,比如MPICH2的mpi实现, MPICH2安装在D:/mpich2下,那么提供的头文件一般是在D:/mpich2/include。如果我要在程序中包含mpi.h,只需要写了#include <mpi.h> 。 但是在编译的时候比如加上一个选项 -ID:/mpich2/include。 否则编译的时候编译器就会抱怨无法打开mpi.h。

当然你也可以写成绝对路径 #include <d:/mpich2/include/mpi.h>。这样也可以编译通过,但是这样带来的问题是,比如你的同学拷贝你的程序在他的计算机上编译,由于他把mpich2安装在e:/software/mpich2下了,于是编译器又开始抱怨无法打开mpi.h。同理,写#include "my.h"的时候也不要写绝对路径。可能你会说,头文件不和源文件放在一起吗?还需要写相对路径吗?当你的程序由上百上千个文件组成时,可能就需要将源码组成一颗源码树。看看linux内核的源码就明白了。比如你的程序在D:/pro/hello/目录下,你把源文件全放在D:/pro/hello/src下,头文件全放在D:/pro/hello/include下, 那么include一个头文件最好写成 #include "../my.c" 而不要写成#include "D:/pro/hello/include/my.h"。 写成后者将会使的你的源代码树只能放在D:/pro下才能正确编译,而写成前者,即使你的源码拷贝到一台Linux机器上也能正确编译。
(注,如果你细心你就会发现 #include "../my.c"中用的是/,而不是/。这才保证了在windows和unix下都能正确识别)

前面讲了很多#include 的用法,#include 到底完成了些什么动作呢? 你可以用cl -E hello.c 或是gcc -E hello.c 看看。-E选项是告诉编译器在完成了预处理之后就停下来,不进行后面的步骤了。这样你就可以看到预处理到底完成了那些功能。如果看不清楚可以重定向到一个 文本文件中,改成cl -E hello.c >hellomid.c 或是 gcc -E hello.c >hellomid.c。这样hellomid.c就是预处理完成之后的结果。

你会发现预处理对于#include 只是把stdio.h的内容直接插入到了源文件里面。在里面有一行
int __cdecl printf( const char * _Format, ...);
这句话保证了你能正确的调用printf("Hellp world!/n");

等等! 那printf的代码在那里?难道#include 不是给我们的程序增加了printf子程序的代码吗?答案是printf实际的代码是在后面连接的时候才加入你的代码的。

#include 还有很多要注意的地方。这牵涉到头文件和源文件的关系,externl linkage和 internl lingage的区别等别的东西。这里只是强调, #include 某个头文件仅仅是讲这个头文件原样插入到了写#include 指令的位置。没有别的其他功能了。
下一篇讲连接link的问题。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值