标签:
1.预处理概述和文件包含命令
前面各章中,已经多次使用过#include
命令。使用库函数之前,应该用#include
引入对应的头文件。这种以#
号开头的命令称为预处理命令。
C语言源文件要经过编译、链接才能生成可执行程序:
1) 编译(Compile)会将源文件(.c文件)转换为目标文件。对于VC/VS,目标文件后缀为 .obj;对于GCC,目标文件后缀为 .o。
编译是针对单个源文件的,一次编译操作只能编译一个源文件,如果程序中有多个源文件,就需要多次编译操作。2) 链接(Link)是针对多个文件的,它会将编译生成的多个目标文件以及系统中的库、组件等合并成一个可执行程序。
#
开头的命令,例如#include
<stdio.h>
等。预处理命令要放在所有函数之外,而且一般都放在源文件的前面。.i
文件中,例如
main.c 的预处理结果在 main.i 中。和.c
一样,.i
也是文本文件,可以用编辑器打开直接查看内容。#include
是文件包含命令,主要用来引入对应的头文件。#include
的处理过程很简单,就是将头文件的内容插入到该命令所在的位置,从而把头文件和当前源文件连接成一个源文件,这与复制粘贴的效果相同。#include <stdio.h> #include "myHeader.h"使用尖括号
<
>
和双引号"
"
的区别在于头文件的搜索路径不同,我们将在《C语言头文件的路径》一节中深入探讨,请大家先记住:包含标准库的头文件一般用尖括号,包含自定义的头文件一般用双引号。
- #include <stdio.h>
- #define N 100
- int main(){
- int sum = 20 + N;
- printf("%d\n", sum);
- return 0;
- }
int
sum = 20 + N;
,N
被100
代替了。#define
N 100
就是宏定义,N
为宏名,100
是宏的内容。在编译预处理时,对程序中所有出现的“宏名”,都用宏定义中的字符串去代换,这称为“宏代换”或“宏展开”。#define
完成的,宏代换是由预处理程序完成的。#define 宏名 字符串
#
表示这是一条预处理命令,所有的预处理命令都以#开头。define
是预处理命令。宏名
是标识符的一种,命名规则和标识符相同。字符串
可以是常数、表达式等。
这里所说的字符串是一般意义上的字符序列,不要和C语言中的字符串等同,它不需要双引号。程序中反复使用的表达式就可以使用宏定义,例如:
#define M (n*n+3*n)它的作用是指定标识符
M
来代替表达式(y*y+3*y)
。在编写源程序时,所有的(y*y+3*y)都可由M代替,而对源程序编译时,将先由预处理程序进行宏代换,即用(y*y+3*y)表达式去替换所有的宏名M,然后再进行编译。
- #include <stdio.h>
- #define M (n*n+3*n)
- int main(){
- int sum, n;
- printf("Input a number: ");
- scanf("%d", &n);
- sum = 3*M+4*M+5*M;
- printf("sum=%d\n", n);
- return 0;
- }
sum=3*M+4*M+5*M
中作了宏调用。在预处理时经宏展开后该语句变为:
sum=3*(n*n+3*n)+4*(n*n+3*n)+5*(n*n+3*n);需要注意的是,在宏定义中表达式
(n*n+3*n)
两边的括号不能少,否则会发生错误。如当作以下定义后:
#difine M n*n+3*n在宏展开时将得到下述语句:
s=3*n*n+3*n+4*n*n+3*n+5*n*n+3*n;这相当于:
#undef
命令。例如:
- #define PI 3.14159
- int main(){
- // Code
- return 0;
- }
- #undef PI
- void func(){
- // Code
- }
- #include <stdio.h>
- #define OK 100
- int main(){
- printf("OK\n");
- return 0;
- }
#define PI 3.1415926 #define S PI*y*y /* PI是已定义的宏名*/对语句:
printf("%f", S);在宏代换后变为:
printf("%f", 3.1415926*y*y);
#define UINT unsigned int在程序中可用UINT作变量说明:
UINT a, b;应注意用宏定义表示数据类型和用typedef定义数据说明符的区别。宏定义只是简单的字符串代换,是在预处理完成的,而typedef是在编译时处理的,它不是作简单的代换,而是对类型说明符重新命名。被命名的标识符具有类型定义说明的功能。
#define PIN1 int * typedef (int *) PIN2;从形式上看这两者相似, 但在实际使用中却不相同。
PIN1 a,b;在宏代换后变成:
int *a,b;表示a是指向整型的指针变量,而b是整型变量。然而:
PIN2 a,b;表示a、b都是指向整型的指针变量。因为PIN2是一个类型说明符。由这个例子可见,宏定义虽然也可表示数据类型, 但毕竟是作字符代换。在使用时要分外小心,以避出错。
#define M(y) y*y+3*y //宏定义 // Code k=M(5); //宏调用在宏调用时,用实参5去代替形参y,经预处理宏展开后的语句为
k=5*5+3*5
。
- #include <stdio.h>
- #define MAX(a,b) (a>b) ? a : b
- int main(){
- int x , y, max;
- printf("input two numbers: ");
- scanf("%d %d", &x, &y);
- max = MAX(x, y);
- printf("max=%d\n", max);
- return 0;
- }
MAX
表示条件表达式(a>b)
? a : b
,形参a、b均出现在条件表达式中。程序第7行max=MAX(x,
y)
为宏调用,实参x、y,将代换形参a、b。宏展开后该语句为:
max=(x>y) ? x : y;用于计算x、y中的大数。
#define MAX(a,b) (a>b)?a:b写为:
#define MAX (a,b) (a>b)?a:b将被认为是无参宏定义,宏名MAX代表字符串
(a,b)
(a>b)?a:b
。宏展开时,宏调用语句:
max=MAX(x,y);将变为:
max=(a,b)(a>b)?a:b(x,y);这显然是错误的。
- #include <stdio.h>
- #define SQ(y) (y)*(y)
- int main(){
- int a, sq;
- printf("input a number: ");
- scanf("%d", &a);
- sq = SQ(a+1);
- printf("sq=%d\n", sq);
- return 0;
- }
sq=(a+1)*(a+1);这与函数的调用是不同的,函数调用时要把实参表达式的值求出来再赋予形参,而宏代换中对实参表达式不作计算直接地照原样代换。
- #include <stdio.h>
- #define SQ(y) y*y
- int main(){
- int a, sq;
- printf("input a number: ");
- scanf("%d", &a);
- sq = SQ(a+1);
- printf("sq=%d\n", sq);
- return 0;
- }
sq=a+1*a+1;由于a为9故sq的值为19。这显然与题意相违,因此参数两边的括号是不能少的。即使在参数两边加括号还是不够的,请看下面程序:
- #include <stdio.h>
- #define SQ(y) (y)*(y)
- int main(){
- int a,sq;
- printf("input a number: ");
- scanf("%d", &a);
- sq = 200 / SQ(a+1);
- printf("sq=%d\n", sq);
- return 0;
- }
sq=160/SQ(a+1);运行本程序如输入值仍为9时,希望结果为2。但实际运行的结果如下:
sq=200/(a+1)*(a+1);a为9时,由于“/”和“*”运算符优先级和结合性相同,则先作200/(9+1)得20,再作20*(9+1)最后得200。为了得到正确答案应在宏定义中的整个字符串外加括号,程序修改如下:
- #include <stdio.h>
- #define SQ(y) ((y)*(y))
- int main(){
- int a,sq;
- printf("input a number: ");
- scanf("%d", &a);
- sq = 200 / SQ(a+1);
- printf("sq=%d\n", sq);
- return 0;
- }
- #include <stdio.h>
- int SQ(int y){
- return ((y)*(y));
- }
- int main(){
- int i=1;
- while(i<=5){
- printf("%d^2 = %d\n", (i-1), SQ(i++));
- }
- return 0;
- }
- #include <stdio.h>
- #define SQ(y) ((y)*(y))
- int main(){
- int i=1;
- while(i<=5){
- printf("%d^2 = %d\n", i, SQ(i++));
- }
- return 0;
- }
之所以出现不同的结果,与 printf() 参数列表中表达式的计算顺序和优先级有关,这里不再深究。分析如下:在示例①中,函数调用是把实参 i 值传给形参 y 后自增 1,然后输出函数值,所以要循环5次,输出1~5的平方值。而在示例②中宏调用时只作代换,SQ(i++) 被代换为 ((i++)*(i++))。第一次循环,i 的值为1,(i++)*(i++)=1;第二次循环 i 的值为 3,(i++)*(i++)=9;第三次循环 i 的值为 5,(i++)*(i++)=25;第四次循环,i 的值为7,终止循环。
- #include <stdio.h>
- #define SSSV(s1, s2, s3, v) s1=l*w; s2=l*h; s3=w*h; v=w*l*h;
- int main(){
- int l=3, w=4, h=5, sa, sb, sc, vv;
- SSSV(sa, sb, sc, vv);
- printf("sa=%d\nsb=%d\nsc=%d\nvv=%d\n", sa, sb, sc, vv);
- return 0;
- }
#
和##
两个符号,它们能够对宏参数进行操作。
#
用来将宏参数转换为字符串,也就是在宏参数的开头和末尾添加引号。例如有如下宏定义:
#define STR(s)那么:
printf("%s", STR(c.biancheng.net)); printf("%s", STR("c.biancheng.net"));分别被展开为:
printf("%s", "c.biancheng.net"); printf("%s", "\"c.biancheng.net\"");可以发现,即使给宏参数“传递”的数据中包含引号,使用
#
仍然会在两头添加新的引号,而原来的引号会被转义。
- #include <stdio.h>
- #define STR(s) #s
- int main() {
- printf("%s\n", STR(c.biancheng.net));
- printf("%s\n", STR("c.biancheng.net"));
- return 0;
- }
##
称为连接符,用来将宏参数或其他的串连接起来。例如有如下的宏定义:
#define CON1(a, b) a##e##b #define CON2(a, b) a##b##00那么:
printf("%f\n", CON1(8.5, 2)); printf("%d\n", CON2(12, 34));将被展开为:
printf("%f\n", 8.5e2); printf("%d\n", 123400);将上面的例子补充完整:
- #include <stdio.h>
- #define CON1(a, b) a##e##b
- #define CON2(a, b) a##b##00
- int main() {
- printf("%f\n", CON1(8.5, 2));
- printf("%d\n", CON2(12, 34));
- return 0;
- }
- #include <stdio.h>
- #include <stdlib.h>
- int main() {
- printf("Date : %s\n", __DATE__);
- printf("Time : %s\n", __TIME__);
- printf("File : %s\n", __FILE__);
- printf("Line : %d\n", __LINE__);
- system("pause");
- return 0;
- }
- #include <stdio.h>
- #define WIN16 true
- int main(void){
- #ifdef WIN16
- printf("The value of sizeof(int) is 2.\n");
- #else
- printf("The value of sizeof(int) is 4.\n");
- #endif
- return 0;
- }
#define WIN16也具有同样的意义。只有取消程序的第2行才会去编译第二个 printf 语句。
ifdef
改为ifndef
。它的功能是,如果标识符未被#define命令定义过则对程序段1进行编译,否则对程序段2进行编译。这与第一种形式的功能正相反。
- #include <stdio.h>
- #define R 1
- int main(){
- float len, area_round, area_square;
- printf ("input a number: ");
- scanf("%f", &len);
- #if R
- area_round = 3.14159*len*len;
- printf("Area of round is: %f\n", area_round);
- #else
- area_square = len*len;
- printf("Area of square is: %f\n", area_square);
- #endif
- return 0;
- }
#error error_message例如,我们的程序针对Linux编写,不保证兼容Windows,那么可以这样做:
- #ifdef WIN32
- #error This programme cannot compile at Windows Platform
- #endif
#error
命令,提示用户发生了编译错误,错误信息是:
This programme cannot compile at Windows Platform
这和发生语法错误的效果是一样的,程序编译失败。请看下面的截图:"
"
,如果加上,引号会被一起输出。例如将上面的#error命令改为:
#error "This programme cannot compile at Windows Platform"那么错误信息如下:
- #ifndef __cplusplus
- #error 当前程序必须以C++方式编译
- #endif
#
号开头的代码行,#号必须是该行除了任何空白字符外的第一个字符。#后是指令关键字,在关键字和#号之间允许存在任意个数的空白字符,整行语句构成了一条预处理指令,该指令将在编译器进行编译之前对源代码做某些转换。指令 | 说明 |
---|---|
# | 空指令,无任何效果 |
#include | 包含一个源代码文件 |
#define | 定义宏 |
#undef | 取消已定义的宏 |
#if | 如果给定条件为真,则编译下面代码 |
#ifdef | 如果宏已经定义,则编译下面代码 |
#ifndef | 如果宏没有定义,则编译下面代码 |
#elif | 如果前面的#if给定条件不为真,当前条件为真,则编译下面代码 |
#endif | 结束一个#if……#else条件编译块 |
标签:
原文地址:http://blog.csdn.net/shuimanting520/article/details/51360142