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 后面跟的只能是一个宏名,不能是其他的。
- C语言复习笔记 8
- C语言复习笔记
- C语言复习笔记
- c语言复习笔记1
- C语言老师复习笔记
- C语言复习笔记 1
- C语言复习笔记 2
- C语言复习笔记 3
- C语言复习笔记 4
- C语言复习笔记 5
- C语言复习笔记 6
- C语言复习笔记 7
- C语言复习笔记 9
- C语言复习笔记 10
- C语言复习笔记 11
- C语言复习笔记 12
- C语言复习笔记 13
- C语言复习笔记 14
- matlab串口采集数据
- hdu3791--二叉搜索树
- memset
- 杭电oj(java版)——1090 A+B for Input-Output Practice (II)
- Android Apk重签名教程
- C语言复习笔记 8
- Java同步工具类——闭锁
- 重定位的理解
- 整理一下当年的学习笔记之:ajax请求json和普通项
- MAC下org.apache.jsp.index_jsp和No output folder的解决方案
- 用 JLINK和自带STLINK给STM32F429 Discovery 烧录程序
- Java8 新特性之集合: forEach(Consumer<? super T> action)
- JDBC编程之事务处理
- JAVA中的final关键字