天天看点

预处理、内联函数

#define 宏定义命令

/*宏定义指令#define用来定义一个标识符和一个字符串,以这个标识符来代表这个字符串,在程序中每次遇到该标识符时就用所定义的字符串替换它。
定义一般形式:#define 宏名 字符串
*表示这是一条预处理命令。
*宏名是一个标识符,必须符合C语言标识符的规定。
*字符串可以是常数、表达式、格式字符串等
*#define命令出现在程序中的函数的外面,宏名的有效范围为定义命令之后到此源文件结束
*/
#include<stdio.h>
#define SIDE 5				//宏名定义后,就可以成为其它宏名定义中的一部分
#define PETIMETER 4*SIDE
#define AREA SIDE*SIDE
#define STANDARD "You are welcome to join us\n"				//如果串长于一行,可以在该行行尾用\续行
/*
*带参数的宏定义
*一般形式:#define 宏名(参数表)字符串
*/
#define MIX(a,b) ((a)*(b)+(b)) 
#define SQR(x) (x)*(x)
int main()
{	
	int x=3,y=4;
	printf("x,y:\n");
	printf("%d,%d\n",x,y);
	printf("the min number is:%d\n",MIX(x,y));
	y=SQR(x+y);
	printf("y=%d\n",y);

	printf(STANDARD);
#undef STANDARD				//终止宏定义的作用域
	printf("STANDARD\n");
	return 0;
}
           
/*明示常量(符号常量):#define定义 */
//每行#define由3部分组成:1.预处理命令 2.宏 3.替换体或替换列表 
//从宏变成可替换文本的过程叫做宏展开 
#include<stdio.h>
/**类对象宏**/ 
#define TWO 2		//可以使用注释: 
#define OW "Consistency is the last\
tive,haha"			//反斜杠把该定义延续到下一行
#define FOUR TWO*TWO	
#define PX printf("X is %d",x)
#define STR puts("\n********************")
#define FMT "X is %d.\n"
//宏替换体是记号型字符串,而不是字符型字符串(记号:替换体中单独的【用空白把这些词分开】词 ) 
#define SIX 2 * 3
/**类函数宏**/ 
#define SQUARE(X)	((X)*(X))
#define PSQR(X) printf("The square of X is %d",((X)*(X)))
/*预处理粘合剂:##(把两个记号粘合为一个记号)*/
#define XNAME(n) x##n	//宏展开为x4 
#define PRINT_XN(n)	printf("x"#n"=%d\n",x##n)

/*变参宏:(宏参数)...*__VA_ARGS__(用于替换体中,代表省略代表什么)*/
#define PR(...) printf(__VA_ARGS__)  
/*#undef指令:取消已定义的#defien指令*/ 
 
int main()
{
	int x=FOUR;
	/*
		int x=TWO*TWO;
		int x=2*2;
	*/
	PX;
	STR;
	printf(FMT);
	STR;
	PSQR(3);
	STR;
	int XNAME(1)=14;		//变成int x1=14
	int XNAME(2)=20;
	int x3=30; 
	PRINT_XN(1);
	PRINT_XN(2);
	PRINT_XN(3);
	PR("Howdy");	//一个参数
	STR;
	PR("weight=%d,shipping=$%d\n",23,45); 
	return 0;	
}
           
/*作用:删除事先定义好的宏定义(将宏名局限在仅需要的代码段中)
*一般形式:#undef 宏替换名
*/
#include<stdio.h>
#define ABC 1
int main()
{
	int i=0;
	i=i+ABC;
	printf("i=%d\n",i);
#undef ABC

	printf("i=%d\n",i);

	return 0;
}
           

条件编译

/*
*只希望其中一部分内容在满足一定条件时才进行编译,
* #if 常数表达式
*   语句段
* #endif
*含义如果#if命令后的参数表达式为真,则编译#if到#endif之间的程序段,否则跳过这段程序

*/
#include<stdio.h>
#define NUM 50
int main()
{
	int i=0;
#if NUM>50
	i++;
#endif
#if NUM==50
	i=i+50;
#endif
#if NUM<50
	i--;
#endif
	printf("now i is:%d\n",i);
	return 0;
}
#include<stdio.h>
#define NUM 50
int main()
{
	int i=0;
#if NUM>50
	i++;
#else			//作用:为#if为假时提供另一种选择 
#if NUM<50
	i--;
#else
	i=i+50;
#endif
#endif
	printf("i=%d\n",i);

	return 0;
}
#ifdef MAVIS
	#include "horse.h"		//如果已用#define 定义了MAVIS,则执行下面的指令
	#define STABLES 5
#eles
	#include "cow.h"		//如果没用用#define定义MAVIS,则执行下面的指令
	#define STABLES 15
#endif 

/*常用这种方法调试程序*/ 
#include<stdio.h>
#define DEBUG 
/*int main() 
{
	int i;
	int total=0;
	for(i=1;i<=4;i++)
	{
		total+=i;
	#ifdef DEBUG
		printf("i=%d\n",i);
	#endif	
	}	
	printf("total=%d",total);	
} 
*/
//防止相同的宏被重复定义
/*arrays.h头文件*/ 
#ifndef SIZE
	#define SIZE 100
#endif

#define SIZE 10
//#inlude"arrays.h"		//SIZE则被设置成10,当执行到这行时,由于SIZE是已定义的,所以跳过了#defien SIZE 100这行代码 

/*ifndef指令通常用于防止多次包含一个文件:应该像下面这样设置头文件*/
/*things.h*/ 
#ifndef THINGS_H_
#define THINGS_H
/*
	头文件中其他内容 
*/
#endif 
 
#if SYS==1
#include "ibm.h"
#endif

#if SYS==1
	#include<stdio.h>
#elif SYS==2
	#include<string.h>
#elif SYS==3 
	#include<stdlib.h>
#else
	#include<limits.h>
#endif


//测试名称是否定义
/*这里defined是一个预处理运算符,如果 它的参数用#define定义过,则返回1,否则返回0 
*/
#if defined(MA)	 
	#include<stdio.h>
#elif defined(MAX)
	#include<string.h>
#endif 
/*编译指示
	_Pragma:预处理运算符,把字符串转换成普通的编译指示。
	例如:-Pragme("nonstandardreatmenttypeB on") 
	等价于下面的指令:
	#pragma nonstandardreatmenttypeB on
	该运算符不使用#符号,所以可以把它作为宏展开的一部分
#define PRAGM(X) _Pragma(#X)
#defien LIMRG(X) PRAGMA(STDC CX_LIMITED_RANGE X)
 
*/ 
#pragma c9x on	//让编译器支持C9X 
//一些预定义宏
//#error让预处理器发出一条错误消息,该消息包含指令中的文本。如果可能的话,编译过程应该中断。			
/*#if__STDC_VERSION__==201112L
#error 不是C99
#endif 
*/

//
#include<stdio.h>
#include<stdio.h> 
int main()
{
	printf("预处理的日期(\"Mmm dd yyyy\"形式的字符串字母量):%s\n",__DATE__);
	printf("表示当前源代码文件名的字符串字面量:%s\n",__FILE__);
	printf("表示当前源代码文件中行号的整型常量:%d\n",__LINE__);
	printf("设置为1时,表名实现遵循C标准:%d\n",__STDC__);
	printf("本地环境设置为1,否则设置为0:%d\n",__STDC_HOSTED__);
	printf("支持C99标准,设置为199901L;支持C11标准,设置为201112L:%ld\n",__STDC_VERSION__);
	printf("翻译代码的时间,格式为\"hh:mm:ss\"%s\n",__TIME__);
	/*预定义标识符,它展开为一个代表函数名的字符串,*/
	printf("这个函数是:%s",__func__);		
//重置__LINE__和__FILE__宏报告的行号和文件名	
#line 1000 				//把当前行号重置为1000
#line 10 "cool.c"		//把行号重置为10,把文件名重置为cool.c


}
/*在#if条件编译命令中,需要判断符号常量所定义的具体值,但有时并不需要判断具体值,只需要知道这个符号常量定义了没有
*这时就可以采用另一种条件编译的方法即#ifdef与#ifndef命令,分别表示“如果有定义”和“如果没定义”
*/
#include<stdio.h>
#define STR "呵呵"
int main()
{
#ifdef STR
	printf(STR);
#else
	printf("没有被定义");
#endif
	printf("\n");
#ifndef ABC
	printf("傻逼,没有此定义");
#else
	printf(STR);
#endif
	printf("\n");
	return 0;
}
           

文件包含

文件包含的作用是把指定的文件模块内容插入到#include所在的位置

使用尖括号时,系统到存放c库函数头文件所在的目录中寻找要包含的文件,这为标准方式

用双引号时,系统先在用户当前目录中寻找要包含的文件,若找不到,再到存放c库函数头文件所在的目录中寻找要包含的文件。

通常情况:如果为调用库函数用#include命令来包含相关的头文件,则用尖括号可以节省查找的时间,

如果要包含的是用户自己编写的头文件,一般用双引号。用户编写的文件通常是在当前目录中,如果文件不在当前目录中,双引号可给出文件路径

注:1.一个#include命令只能指定依噶被包含的文件
		2.文件包含可以是嵌套的,即在一个被包含文件中还可以包含另一个被包含文件
           

头文件:经常用在文件头部的被包含的文件称为“标题文件”,或“头部文件”,一般以.h为后缀,

一般情况下,将如下内容放到.h文件中:

  • 1.明示常量——stdio.h中定义的EOF、NULL、BUESIZE(标准I/O缓冲区大小)
  • 2.宏函数
  • 3.结构、联合、枚举声明
  • 4.typedef声明
  • 5.外部函数声明
  • 6.全局变量声明

其他预处理命令

/*#line用来显示_LINE_与_FILE_的内容。
*_LINE_用来存放当前编译行的行号
*_FILE_用来存放当前编译的文件名
#line的一般形式如下:
#line 行号["文件名"]
			其中行号为任一正整数,可选的文件名为任意有效文件标识符。文件名为源文件的名字,行号为源程序中当前行号
*主要用于调试及其他特殊应用
*/
#include<stdio.h>
int main()
{
	printf("当前行号:%d\n",__LINE__);
	printf("当前行号:%d\n",__LINE__);
	printf("文件名:%s\n",__FILE__);
#line 100 "hhaha.c"
	printf("当前行号:%d\n",__LINE__);
	printf("文件名:%s\n",__FILE__);
	return 0;
}
           

内联函数

/*函数调用:建立调用、传递参数、跳转到函数代码并返回。使用宏使代码内联,可以避免这样的开销,
	把函数变成内联函数建议尽可能快地调用该函数 ,具体效果由实现定义,因此,把函数变成内联函数,编译器可能会用内联代码替换函数调用
	,并(或)执行一些其他的优化,但是也可能不起作用 
	
	创建内联函数的定义有多种办法:标准规定具有内部链接的函数可以成为内联函数,还规定了内联函数的定义
	与调用与该函数的代码必须在同一文件中。
	
	最简单的方法是使用函数说明符 inline 和存储类别说明符 static。通常,内联函数应定义在首次使用它的文件中,
	所以内联函数也相当于函数原型。 
*/
#include<stdio.h>
inline static void eatline()	//内联函数定义/原型
{
	while(getchar()!='\n')
		continue;
} 
int main()
{
	char ch;
	while(ch=getchar()!='\n')
	{
		eatline();	//编译器查看内联函数的定义(也是原型),可能会用函数体中的代码替换eatline()函数调用,相当于在函数调用位置输入函数体中的代码 
		putchar(ch);
	}
}
           

_Noreturn函数

Noreturn函数:表明调用完成后函数不返回主调函数。Noreturn 的目的是告诉用户和编译器,这个特殊的函数不会把控制

返回主调程序。告诉用户以免滥用该函数