施展 武汉光电国家研究中心 & 计算机学院 华中科技大学
#define
编译预处理:对源程序进行编译之前所作的工作,它由预处理程序负责完成。编译时,系统将自动引用预处理程序对源程序中的预处理指令进行处理。
预处理指令:以"#"号开始的指令。
#include
用指定文件的内容取代该预处理指令行,有2种一般形式:
#include <文件名>
在指定的标准目录下寻找被包含文件
#include "文件名"
首先在用户当前目录中寻找被包含文件,若找不到,再在指定的标准目录下寻找
用一个标识符来表示一个字符串
#define 标识符 字符串
宏名:被定义的标识符。 宏代换(宏展开):在编译预处理时,用字符串去取代宏名。
#define M (y*y+3*y) void main(void) { int s,y; printf("Input a number: "); scanf("%d",&y); s=3*M+4*M+y*M; printf("s=%d\n",s); }
void main(void) { int s,y; printf("Input a number: "); scanf("%d",&y); s=3*(y*y+3*y)+4*(y*y+3*y)+y*(y*y+3*y); printf("s=%d\n",s); }
#define 标识符(标识符,标识符,…,标识符) 字符串
宏调用:给出实参 宏展开:
#define SQ(x) ((x)*(x))
宏调用:SQ(a+1) 宏展开:((a+1) * (a+1))
SQ(a+1)
((a+1) * (a+1))
宏调用:SQ(SQ(a)) 宏展开:((((a)*(a))) * (((a)*(a))))
SQ(SQ(a))
((((a)*(a))) * (((a)*(a))))
#define SQ(x) x*x SQ(a+b) // 展开为: a+b*a+b
#define SQ(x) (x)*(x) 27/SQ(3) // 展开为: 27/(3)*(3)
正确做法:表达式中的每个参数用括号括起来,整个表达式也用括号括起来。
#define SQ (x) ((x)*(x)) // 错误!被认为是无参宏定义
宏调用:SQ(3) 宏展开:(x) ((x)*(x)) (3) // 显然错误的
SQ(3)
(x) ((x)*(x)) (3)
#undef
终止宏名的作用域,形式为:
#undef 标识符
#include "everything.h" #undef SIZE // 取消everything.h中定义的SIZE #define SIZE 100 // 重新定义
#undef getchar int getchar(void) {…}
预处理程序提供了条件编译指令,用于在预处理中进行条件控制,根据所求条件的值有选择地包含不同的程序部分,因而产生不同的目标代码。
三种形式:#if、#ifdef、#ifndef
#if
#ifdef
#ifndef
#define R int main(void) { float r, s; printf("input a number: "); scanf("%f", &r); #ifdef R s = 3.14159 * r * r; printf("%f\n", s); #else s = r * r; printf("%f\n", s); #endif return 0; }
int main(void) { float r,s; printf (“input a number: ”); scanf(“%f ”,&r); s=3.14159*r*r; printf(“%f\n”,s); return 0; }
生成计算圆面积的目标程序
// #define R void main(void) { float c, s; printf("input a number: "); scanf("%f", &c); #ifdef R s = 3.14159 * c * c; printf("%f\n", s); #else s = c * c; printf("%f\n", s); #endif }
// #define R void main(void) { float c, s; printf("input a number: "); scanf("%f", &c); s = c * c; printf("%f\n", s); }
生成计算正方形面积的目标程序
#if 0 // 不编译的代码 #endif
#define DEBUG // 完成调试后,去掉该指令 #ifdef DEBUG printf("x=%d\n", x); #endif #ifdef DEBUG printf("y=%d\n", y); #endif
在头文件assert.h中,测试表达式的值是否符合要求:
assert.h
assert(e)
如果n < 0,会输出包含行号和文件名的错误信息并中断执行:
n < 0
Assertion failed: n >= 0, file test.c, line 32
防御性编程:使用户在运行程序(发布版本里)时,当出现意外情况时程序仍能继续工作。
断言的作用:看作一种简单的制造栅栏的方法,这种栅栏能使错误在穿过自己时暴露。
flowchart TD A[assert(e)] --> B{e为真?} B -->|是| C[继续执行] B -->|否| D[输出错误信息\n文件、行号] D --> E[程序中断]