C语言复习笔记 8

来源:互联网 发布:淘宝上的冬木古雨 编辑:程序博客网 时间:2024/05/17 23:18

预处理命令

程序设计语言的预处理的概念:在编译之前进行的处理。 C语言的预处理主要有三个方面的内容:

1. 宏定义;
2. 文件包含;
3. 条件编译。

预处理命令以符号“#”开头。

以#号开头的命令称为预处理命令。

C语言源文件要经过编译、链接才能生成可执行程序:
1)编译(Compile)会将源文件(.c文件)转换为目标文件。对于VC/VS,目标文件后缀为.obj;对于GCC,目标文件为.o。

编译是针对单个源文件的,一次编译操作只能编译一个源文件,如果程序中有多个源文件,就需要多次编译操作。

2)链接(Link)是针对多个文件的,它会将编译生成的目标文件以及系统中的库、组件等合并成一个可执行程序。

关于编译和链接的过程、目标文件和可执行文件的结构、.h文件和.c文件的区别。待研究。

在实际开发中,有时候在编译之前还需要对源文件进行简单的处理。例如,我们希望自己的程序在Windows和Linux下都能够运行,那么就要在Windows下使用VS编译一遍,然后在Linux下使用GCC编译一遍。但是现在有个问题,程序中要实现的某个功能在VS和GCC下使用的函数不同(假设VS下使用 a(),GCC下使用 b()),VS下的函数在GCC下不能编译通过,GCC下的函数在VS下也不能编译通过,怎么办呢?

这就需要在编译之前先对源文件进行处理:如果检测到是VS,就保留 a() 删除 b();如果检测到是GCC,就保留 b() 删除 a()。

这些在编译之前对源文件进行简单加工的过程,就称为预处理(即预先处理、提前处理)。

预处理主要是处理以#开头的命令,例如#include

宏定义

宏定义是预处理命令的一种,它允许用一个标识符来表示一个字符串。

#define N 100就是宏定义,N为宏名,100是宏的内容。在编译预处理时,对程序中所有出现的“宏名”,都用宏定义中的字符串去代换,这称为“宏代换”或“宏展开”。

宏定义是由源程序中的宏定义命令#define完成的,宏代换是由预处理程序完成的。

对宏定义的几点说明

1) 宏定义是用宏名来表示一个字符串,在宏展开时又以该字符串取代宏名,这只是一种简单的替换。字符串中可以含任何字符,可以是常数,也可以是表达式,预处理程序对它不作任何检查,如有错误,只能在编译已被宏展开后的源程序时发现。(注意与C语言带双引号的字符串区别开,这里指的是广义上的字符串)

2) 宏定义不是说明或语句,在行末不必加分号,如加上分号则连分号也一起替换。

3) 宏定义必须写在函数之外,其作用域为宏定义命令起到源程序结束。如要终止其作用域可使用#undef命令。例如:

#define PI 3.14159int main(){    // Code    return 0;}#undef PIvoid func(){    // Code}//表示PI只在main函数中有效,在func中无效。

4) 宏名在源程序中若用引号括起来,则预处理程序不对其作宏代换,例如:

#include <stdio.h>#define OK 100int main(){    printf("OK\n");    return 0;}//该例中定义宏名OK表示100,但在 printf 语句中 OK 被引号括起来,因此不作宏代换,而作为字符串处理。

5) 宏定义允许嵌套,在宏定义的字符串中可以使用已经定义的宏名,在宏展开时由预处理程序层层代换。

6) 习惯上宏名用大写字母表示,以便于与变量区别。但也允许用小写字母

7) 可用宏定义表示数据类型,使书写方便。例如:
#define UINT unsigned int
在程序中可用UINT作变量说明:
UINT a, b;

应注意用宏定义表示数据类型和用typedef定义数据说明符的区别。宏定义只是简单的字符串代换,是在预处理完成的,而typedef是在编译时处理的,它不是作简单的代换,而是对类型说明符重新命名。被命名的标识符具有类型定义说明的功能。

这里写图片描述

typedef为C语言的关键字,作用是为一种数据类型定义一个新名字。这里的数据类型包括内部数据类型(int,char等)和自定义的数据类型(struct等)。
在编程中使用typedef目的一般有两个,一个是给变量一个易记且意义明确的新名字,另一个是简化一些比较复杂的类型声明。

C语言中带参数的宏定义

C语言允许宏带有参数。在宏定义中的参数称为形式参数,在宏调用中的参数称为实际参数,这点和函数有些类似。

对带参数的宏,在调用中,不仅要宏展开,而且要用实参去代换形参。

带参宏定义的一般形式为:
#define 宏名(形参列表) 字符串
在字符串中含有各个形参。

带参宏调用的一般形式为:
宏名(实参列表);
例如:

#define M(y) y*y+3*y  //宏定义// Codek=M(5);  //宏调用在宏调用时,用实参5去代替形参y,经预处理宏展开后的语句为k=5*5+3*5

对带参宏定义的说明

1) 带参宏定义中,形参之间可以出现空格,但是宏名和形参列表之间不能有空格出现。

2) 在带参宏定义中,不会为形式参数分配内存,因此不必指明数据类型。而在宏调用中,实参包含了具体的数据,要用它们去代换形参,因此必须指明数据类型。

这一点和函数是不同的:在函数中,形参和实参是两个不同的变量,都有自己的作用域,调用时要把实参的值传递给形参;而在带参数的宏中,只是符号的替换,不存在值传递的问题。

带参数的宏和函数很相似,但有本质上的区别:宏展开仅仅是字符串的替换,不会对表达式进行计算;宏在编译之前就被处理掉了,它没有机会参与编译,也不会占用内存。而函数是一段可以重复使用的代码,会被编译,会给它分配内存,每次调用函数,就是执行这块内存中的代码。

#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;}

运行结果:
1^2 = 1
2^2 = 4
3^2 = 9
4^2 = 16
5^2 = 25

注意一点 printf()函数是从右开始入栈的。

C语言条件编译详解

假如现在要开发一个C语言程序,让它输出红色的文字,并且要求跨平台,在 Windows 和 Linux 下都能运行,怎么办呢?

这个程序的难点在于,不同平台下控制文字颜色的代码不一样,我们必须要能够识别出不同的平台。

Windows 有专有的宏_WIN32,Linux 有专有的宏linux,以现有的知识,我们很容易就想到了 if else,请看下面的代码:

#include <stdio.h>int main(){    if(_WIN32){        system("color 0c");        printf("http://c.biancheng.net\n");    }else if(__linux__){        printf("\033[22;31mhttp://c.biancheng.net\n\033[22;30m");    }else{        printf("http://c.biancheng.net\n");    }    return 0;}

但这段代码是错误的,在 Windows 下提示 linux 是未定义的标识符,在 Linux 下提示 _Win32 是未定义的标识符。对上面的代码进行改进:

#include <stdio.h>int main(){    #if _WIN32        system("color 0c");        printf("http://c.biancheng.net\n");    #elif __linux__        printf("\033[22;31mhttp://c.biancheng.net\n\033[22;30m");    #else        printf("http://c.biancheng.net\n");    #endif    return 0;}

#if、#elif、#else 和 #endif都是预处理命令,整段代码的意思是:如果宏 _WIN32 的值为真,就保留第 4、5 行代码,删除第 7、9 行代码;如果宏 linux 的值为真,就保留第 7 行代码;如果所有的宏都为假,就保留第 9 行代码。

这些操作都是在预处理阶段完成的,多余的代码以及所有的宏都不会参与编译,不仅保证了代码的正确性,还减小了编译后文件的体积。


这种能够根据不同情况编译不同代码、产生不同目标文件的机制,称为条件编译。条件编译是预处理程序的功能,不是编译器的功能。

#if 命令

#if 命令的完整格式为:#if 整型常量表达式1    程序段1#elif 整型常量表达式2    程序段2#elif 整型常量表达式3    程序段3#else    程序段4#endif

它的意思是:如常“表达式1”的值为真(非0),就对“程序段1”进行编译,否则就计算“表达式2”,结果为真的话就对“程序段2”进行编译,为假的话就继续往下匹配,直到遇到值为真的表达式,或者遇到 #else。这一点和 if else 非常类似。

需要注意的是,#if 命令要求判断条件为“整型常量表达式”,也就是说,表达式中不能包含变量,而且结果必须是整数;而 if 后面的表达式没有限制,只要符合语法就行。这是 #if 和 if 的一个重要区别。

#elif 和 #else 也可以省略,如下所示:#include <stdio.h>int main(){    #if _WIN32        printf("This is Windows!\n");    #else        printf("Unknown platform!\n");    #endif    #if __linux__        printf("This is Linux!\n");    #endif    return 0;}

#ifdef 命令

#ifdef 命令的格式为:#ifdef  宏名    程序段1#else    程序段2#endif它的意思是,如果当前的宏已被定义过,则对“程序段1”进行编译,否则对“程序段2”进行编译。

#ifndef 命令

#ifndef 命令的格式为:#ifndef 宏名    程序段1 #else     程序段2 #endif#ifdef 相比,仅仅是将 #ifdef 改为了 #ifndef。它的意思是,如果当前的宏未被定义,则对“程序段1”进行编译,否则对“程序段2”进行编译,这与 #ifdef 的功能正好相反。

区别

最后需要注意的是,#if 后面跟的是“整型常量表达式”,而 #ifdef 和 #ifndef 后面跟的只能是一个宏名,不能是其他的。

0 0
原创粉丝点击