这篇博客详细讲解了C/C++程序被翻译成可执行程序需要经理的步骤。其中,预处理(也称为预编译)阶段干了5件事情,分别是:
程序员写注释,是给人看的,不是给计算机看的。所以,在预处理阶段会把注释都删掉。C/C++中有2种注释风格,分别是:
/*
开头,*/
结尾,不能嵌套注释。/* 这里写注释 */
//
开头,一直到本行末尾。// 这里写注释
#define可以定义2种替换方式。
如#define NUM 100
,如果遇到了NUM就会被替换成100。
printf("%d\n", NUM); // 会被替换成printf("%d\n", 100);
当然,如果想移除一个#define
定义的标识符,可以使用#undef
。
#undef NUM
如#define MAX(x, y) ((x)>(y)?(x):(y))
,如果遇到了MAX(…,…)就会替换掉。
int sum = MAX(10, 20); // 会被替换成int sum = ((10)>(20)?(10):(20));
以上的代码中,会把10传给x,把20传给y,然后根据把((x)>(y)?(x):(y))中的x换成10,y换成20,作为替换的结果。
宏和函数有很多相同点和不同点,关于这点的总结我会单独写一篇博客来阐述。
头文件包含有2种方式,分别是:
这两种方式对应的文件查找策略是不一样的。
那么,我们是不是可以写:#include "stdio.h"
?
确实是可以的,但是不建议。有2个原因:
建议:
当编译器找到这个头文件后,就会用该头文件中的代码替换掉#include的位置。
需要注意的是:为了防止一个头文件被重复包含在同一个文件中,比如:
#include "test.h"
#include "test.h"
#include "test.h"
以上代码重复包含了3次test.h这个头文件,会导致其中的代码被拷贝3份,降低效率。所以一般会在头文件第一行加上#pragma once
,这句代码的作用是,哪怕被重复包含,也只会把里面的代码拷贝一次。当然,也可以使用条件编译指令来达到同样的效果,后面会讲。
条件编译,即根据不同的条件,选择是否编译某一段代码。常见的条件编译指令有:
#if
可以理解成if
,#elif
可以理解成else if
,#else
可以理解成else
,比如:
#if 1==1
printf("hehe\n");
#elif 2==2
printf("haha\n");
#elif 3==3
printf("heihei\n");
#else
printf("hengheng\n");
#endif
如果1==1
成立,就会printf("hehe\n");
会被编译,其他语句不会被编译。如果1==1
不成立,2==2
成立,printf("haha\n");
会被编译,其他语句不会被编译。如果2==2
仍然不成立,3==3
成立,printf("heihei\n");
会被编译,其他语句不会被编译。如果以上都不成立,printf("hengheng\n");
会被编译,其他语句不会被编译。这和if()
,else if()
,else
的逻辑是一样的。注意最后要用#endif
来结束。
#ifdef
后面的标识符如果已经被#define
定义,则后面的语句会被编译,否则不会被编译。注意最后也要使用#endif
来结束。
#ifndef
和#ifdef
恰恰相反,如果后面的标识符没有被#define
定义译,否则不会被编译。注意最后也要使用#endif
来结束。
比如:
#define __DEBUG__
#ifdef __DEBUG__
printf("debug\n"); // 这条语句会被编译
#endif
#ifndef __DEBUG__
printf("not debug\n"); // 这条语句不会被编译
#endif
// 移除__DEBUG__的定义
#undef __DEBUG__
#ifdef __DEBUG__
printf("debug\n"); // 这条语句不会被编译
#endif
#ifndef __DEBUG__
printf("not debug\n"); // 这条语句会被编译
#endif
除此之外,#ifdef __DEBUG__
就等价于#if defined(__DEBUG__)
,#ifndef __DEBUG__
就等价于#if !defined(__DEBUG__)
。
前面讲过,如果要防止头文件被重复包含,可以使用#pragma once
,也可以使用条件编译。比如test.h
头文件中,只要这么写:
#ifndef __TEST_H__
#define __TEST_H__
// 这里写头文件的内容
#endif
假设被重复包含了,那么头文件中的内容就会被拷贝多份。假设被重复包含3次,就会像下面这样:
#ifndef __TEST_H__
#define __TEST_H__
// 这里写头文件的内容
#endif
#ifndef __TEST_H__
#define __TEST_H__
// 这里写头文件的内容
#endif
#ifndef __TEST_H__
#define __TEST_H__
// 这里写头文件的内容
#endif
有没有发现,由于第一次#ifndef
后面的语句会被编译,从而会#define
后面的__TEST_H
这个标识符,从而第2、3次的#ifndef
后面的语句都不会被编译。这就提高了编译的效率。
我们可以使用命令行编译来指定某些标识符的值。比如创建一个main.c:
#include
int main()
{
int arr[SZ] = {0};
int i = 0;
for (i=0; i<SZ; i++)
{
arr[i] = i;
printf("%d ", arr[i]);
}
printf("\n");
return 0;
}
使用gcc编译这段代码时,可以指定SZ的值:
gcc main.c -D SZ=100
gcc main.c -D SZ=10
#define
定义的标识符和宏替换掉。#include
的头文件展开。<>
和""
对应的查找策略是不一样的。<>
会直接到库目录中去找,""会先到工程目录中找,找不到了再去库目录中去找。为了防止头文件被重复包含,建议使用#pragma once
或者条件编译。#if, #elif, #else, #ifdef, #ifndef, #endif
。条件编译都要用#endif来结束。#ifdef NUM
和#if defined(NUM)
等价,#ifndef NUM
和#if !defined(NUM)
等价。#pragma once
,也可以使用#ifndef, #define, #endif
。-D
选项来实现这一点。