泛化编程与预编译

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

运行结果:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值