1. 概述
为实现代码的可移植性和可重用性,C语言引入预编译指令。
预编译指令以符号#开始,可以出现在源程序的任何位置,其作用范围是从出现位置到文件尾。
2. #define 指令
#define 指令用于宏定义
2.1 不带参数的宏定义
指令格式:
#define 符号常量名 替换文本 (没有分号)
符号常量名也称宏名,习惯用大写字母表示。
替换文本可以是C语言允许的标识符,关键字,数值,运算符,字符串已经语句等
通过预编译处理,( 编译器对源程序进行编译时,首先处理预编译指令。)程序中使用的符号常量名都被替换文本所替换,该过程称为宏展开。
结论:程序运行时,所有的符号常量名都被替换为替换文本
使用不带参数的宏定义时需注意: (假如PI是符号常量名)
①字符串中与符号常量名相同的字符串不进行替换
如:“PI” 不能替换,PIPI也不能替换
②宏定义只是简单的字符替换,不进行语法检查
③每条宏定义必须单独占一行
④宏不可以被定义多次,但宏定义可以使用已经定义的宏
⑤宏定义只作字符替换,不分配内存
⑥#undef 指令可以取消宏定义,此后,某个宏将不存在
指令格式: #undef 符号常量名
代码演示:
#include<stdio.h>
#define PI 3.1415926 //PI是符号常量名,3.14 是替换文本
#define 乘 * //乘 是符号常量名,* 是替换文本
#define 等于 = //等于 是符号常量名,= 是替换文本
#define 实数 float //实数 是符号常量名, float 是替换文本
#define S PI*2*2 //S 是符号常量名, P*4*4 是替换文本
int main()
{
实数 area, r;
r = 2.0; //半径为4.0
area 等于 PI 乘 r 乘 r;
//area 等于 PI 乘 r乘r; //不会替换
printf("area1 = %f\n", area);
printf("area2 = %f\n",S);
printf("area2 = S\n"); //不会替换
#undef S //取消S的宏定义
//printf("area2 = %f", S); //S 未定义
return 0;
/*运行结果:
area1 = 12.566370
area2 = 12.566370
area2 = S
*/
}
2.2 带参数的宏定义
宏定可以带参数,这些参数相当于实际参数的占位符
指令格式:
#define 宏名(形参列表) 宏体 (宏名之后没有空格)
代码演示:
#include<stdio.h>
#define PI 3.14
#define Area(r) PI*r*r
int main()
{
printf("sum = %f", Area(1 + 2)); //宏展开只是简单的替换
//我以为: 3.14*3*3 = 27
//实际上: 3.14*1+2*1+2 = 7
//故最好给宏的参数加上括号即
// #define Area(r) PI*(r)*(r)
return 0;
//运行结果:sum = 7.140000
}
2.3 带参数的宏定义和函数的主要区别
①
宏定义编译时进行处理,宏展开不分配存储空间,展开过程占用编译时间,不占用程序执行时间。
函数在程序运行期间被调用,系统为其分配临时内存,函数调用占用程序执行时间。
②
宏展开只是进行对应的字符替换。
函数调用时,先计算实参的值,再传入实参。
③
宏定义不存在类型问题,宏名无类型,其他参数也无类型。
④
宏展开过程会使源程序代码增加,编译后源程序代码长度确定
函数调用不会使源程序变长。
3. #include 指令
#include 指令用于实现文件的包含操作。
格式1:#include<文件名>
系统在编译器系统指定的头文件目录中找相应的文件
格式2:#include"文件名"
系统首在当前源程序文件所在目录下找文件,如果找不着
系统将在编译器系统指定的头文件目录中找相应的文件
故,自定义的文件应使用“ ”
系统的文件应使用<>
4. 条件编译
4.1 #ifdef … #else … #endif
#ifdef … #else … #endif 用于测试一个宏是否已经定义。
指令格式:
#ifdef 标识符
程序段1
#else
程序段2
#endif
如果 标识符 已定义 则执行程序段1,
否则执行程序段2。
代码演示:
#include<stdio.h>
int main()
{
#define C //定义宏
#ifdef C
printf("C已定义\n");
#else
printf("C未定义\n");
#endif
#undef C //取消宏定义
#ifdef C
printf("C已定义\n");
#else
printf("C未定义\n");
#endif
return 0;
/*
运行结果:
C已定义
C未定义
*/
}
4.2 #ifndef … #else … #endif
#ifndef … #else … #endif 用测试一个宏没有被定义
于#ifdef … #else … #endif 相对
指令格式:
#ifndef 标识符
程序段1
#else
程序段2
#endif
如果 标识符 已定义 则执行程序段2,
否则执行程序段1。
代码演示:
#include<stdio.h>
int main()
{
#define C //定义宏
#ifndef C
printf("C已定义\n");
#else
printf("C未定义\n");
#endif
#undef C //取消宏定义
#ifdef C
printf("C已定义\n");
#else
printf("C未定义\n");
#endif
return 0;
/*
运行结果:
C未定义
C未定义
*/
}
4.3 #if … #else … #endif
指令格式:
#if 表达式
程序段1
#else
程序段2
#endif
表达式为真 执行程序段1,
否则执行程序段2。
#include<stdio.h>
int main()
{
#if 1
printf("表达式结果为真\n");
#else
printf("表达式结果为假\n");
#endif
return 0;
//运行结果:表达式结果为真
}
4.4 #if … #elif … #endif
指令格式:
#if 表达式
程序段1
#elif 表达式
程序段2
#elif 表达式
程序段3
… …
#else
程序段4
#endif
#elif的个数不限,但是必须有#else,类似于多分支结构
代码演示:
#include<stdio.h>
int main()
{
#define A 3
#if A == 1
printf("A = %d\n", A);
#elif A == 2
printf("A = %d\n", A);
#elif A == 3
printf("A = %d\n", A);
#else
printf("error");
#endif
return 0;
//运行结果:A = 3
}
5. 其他指令
5.1 #error 指令
用于输出编译错误信息
常与条件编译一起使用
若#error 指令 被执行则,则编译器会终止编译
指令格式:
#error 错误信息
代码演示:
int main()
{
#define PI 3.14
#define Area(r) PI*(r)*(r) //定义宏
#undef Area //取消宏
#ifndef Area //宏无定义
#error Area 未定义 !!!!! //在编译时就会出错
#endif
}
错误结果:
5.2 #pragma 指令
#pragma once 可以防止同一段代码被包含多次
其实#pragma 指令的用法还有很多
5.3 #line
文件出错时更改时,更改出错信息
代码演示1:
int main()
{
#define PI 3.14
#define Area(r) PI*(r)*(r)
#undef Area
#ifndef Area
//#line 1 "main.c" //第二个参数可以省略
//
// //参数 "main.c" 表示在文件main.c中
// //参数 1 表示下一行为第一行
//
#error Area 未定义 !!!!! //在编译时就会出错
#endif
}
错误结果:
代码演示2:
int main()
{
#define PI 3.14
#define Area(r) PI*(r)*(r)
#undef Area
#ifndef Area
#line 1 "main.c" //第二个参数可以省略
//参数 "main.c" 表示在文件main.c中
//参数 1 表示下一行为第一行
#error Area 未定义 !!!!! //在编译时就会出错
#endif
}
错误结果:
5.4 #和##运算符
#用于实现文本替换
##用于连接两个标识串
代码演示:
#include<stdio.h>
#define print(name) printf("hello "#name"!"); //#name有单独的双引号
#define TEXT(x,y) x##y
int main()
{
int num;
TEXT(nu,m) = 10; //即num=10
printf("%d\n", num);
print(world)
return 0;
/*
运行结果:
10
hello world!
*/
}
5.5 异常处理
异常:用户的输入或者程序的逻辑结构存在问题,都将造成程序运行结果出现错误,严重时会造成系统宕机,通常将这类问题定义为异常。
C语言本身没有提供异常捕获机制,故需要提高程序容错能力来保证程序的稳定性。
C语言头文件assert.h中定义了宏assert用于断言操作。
断言是指假定当前结论正确,如果不正确,则程序会停止,并输出错误信息。
代码演示:
#include<stdio.h>
#include<assert.h>
int main()
{
int a, b;
printf("输入两数:");
scanf("%d%d", &a, &b);
assert(b != 0); //断言b!=0
printf("%d",a / b);
return 0;
}
运行结果: