C语言预处理命令

编译预处理指令:对源程序编译之前做一些处理,生成扩展C源程序

1、种类:

  • 宏定义 #define
  • 文件包含 #include
  • 条件编译 #if–#else–#endif等

2、格式:

  • “#”开头
  • 占单独书写行
  • 语句尾不加分号

3、宏定义

在C语言源程序中允许用一个标识符来表示一个字符串,称为“宏”。被定义为“宏”的标识符称为“宏名”。在编译预处理时,对程序中所有出现的“宏名”,都用宏定义中的字符串去代换,这称为“宏代换”或“宏展开”
宏定义是由源程序中的宏定义命令完成的。宏代换是由预处理程序自动完成的

格式:#define 宏名 宏替换值
作用:在进行编译预处理的时候,编译器会对程序中出现的所有的宏名使用宏字符串去进行替换–宏替换仅为简单字符替换

实例:

#include 
#define PI 3.1415   //宏名PI,替换所用的字符串3.1415

int main(int argc, char *argv[])
{
	int r = 5;
	float s;

	s = (float)PI * r * r;
	printf("s = %7.2f\n", s);

	r = 4;
	s = (float)PI * r * r;
	printf("s = %7.2f\n", s);

	return 0;
}

该程序预处理以后main函数会变成如下形式:

int main(int argc, char *argv[])
{
 int r = 5;
 float s;

 s = (float)3.1415 * r * r;
 printf("s = %7.2f\n", s);

 r = 4;
 s = (float)3.1415 * r * r;
 printf("s = %7.2f\n", s);

 return 0;
}

在C中经常使用宏来定义常量–所以经常把C中的常量叫做符号常量

#include 
#define PI 3.1415   //宏名PI,替换所用的字符串3.1415
#define RC PI * 5.0
#define VAL PI + 5  //在定义复杂的宏的时候一定要注意避免出现副作用,因为宏替换只是简单的字符替换
#define VAL1 ((PI)  + 5)

int main(int argc, char *argv[])
{
	int r = 5;
	float s;

	s = (float)PI * r * r;
	printf("s = %7.2f\n", s);

	r = 4;
	s = (float)PI * r * r;
	printf("s = %7.2f\n", s);

	s = RC;   //s = 3.1415 * 5.0
	printf("s = %7.2f\n", s);
    
	s = VAL * r * r;   //PI + 5 * r * r
	printf("s = %7.2f\n", s);

	s = VAL1 * r * r;   //PI + 5 * r * r
	printf("s = %7.2f\n", s);

	return 0;
}
[root@localhost 10_bits]# gcc test3.c -o test3
[root@localhost 10_bits]# ./test3
s =   78.54
s =   50.26
s =   15.71
s =   83.14
s =  130.26

带参数宏:

#include 
#define PI 3.1415   //宏名PI,替换所用的字符串3.1415
#define MAX(x, y) x > y ? x : y  //x和y成为带参宏的形参
int main(int argc, char *argv[])
{
	int num1 = 12;
	int num2 = 21;
	int res = MAX(num1, num2);  //在使用带参宏的时候要将对应的实参传递给形参  -- 和函数调用是一样的

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

	return 0;
}
上述代码进行宏替换以后称为:
int main(int argc, char *argv[])
{
 int num1 = 12;
 int num2 = 21;
 int res = num1 > num2 ? num1 : num2;  //在替换过程中除了将带参宏定义中的宏值替换宏名以外,还会将实参替换宏定义中的形参

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

 return 0;
}

带参宏和函数调用的区别:

  1. 带参数宏的代码在编译过程以后,宏代码会变成程序的本地代码(宏的代码和程序的代码在同一环境下),而函数调用中函数的代码要靠链接进来
  2. 对于一些简单的算法(算法中包含的语句不多的情况下),使用带参宏相比于使用函数调用,程序的运行效率要高
  3. 带参数的宏经常可以适用于不同的参数类型和返回值类型,所以他的通用性更好;但是他的安全性更差。在编译过程中,如果说是函数调用的话,对于实参传递给形参,对于将函数的返回值赋值给其他的对象都会进行类型判定,如果出现类型不兼容,在编译的过程中就会发现;而使用带参宏,由于在编译过程中只进行替换,往往不能发现类型方面的问题,经常都会在程序运行过程中出现意想不到的错误
#include 
#define PI 3.1415   //宏名PI,替换所用的字符串3.1415
#define MAX(x, y) x > y ? x : y

/*
int MAX(int x, int y)
{
    return x > y ? x : y;
}
*/

int main(int argc, char *argv[])
{
	int num1 = 12;
	int num2 = 21;
	double dnum1 = 1.23;
	double dnum2 = 2.32;
	double dres = MAX(dnum1, dnum2);
	int res = MAX(num1, num2);
    
	//res = MAX(dnum1, dnum2);
	printf("res = %d\n", res);
	printf("dres = %7.2f\n", dres);

	return 0;
}
[root@localhost 10_bits]# gcc test3.c -o test3
[root@localhost 10_bits]# ./test3
res = 2
dres =    2.32

4、文件包含

在编译预处理过程中,会将包含的文件的内容替换到本文件 中
在头文件中定义数据结构,函数原型
在一个c源程序中实现函数
在主程序中调用函数
比如在stu.h中定义学生结构体和函数原型

typedef struct Stu
{
	int no;
	char name[12];
	float score;
}Stu;

void show_stu(Stu tmp);

在stu.c中实现stu.h中定义的函数

#include "stu.h"
#include 

void show_stu(Stu tmp)
{
    printf("%d, %s, %7.2f\n", tmp.no, tmp.name, tmp.score);
}

在main.c中调用函数

#include 
#include "stu.h"

int main(int argc, char *argv[])
{
	Stu st = {101, "Jack", 88.5};
	show_stu(st);
	
	return 0;
}
[root@localhost student_manager]# gcc stu.c main.c -o main
[root@localhost student_manager]# ./main
101, Jack,   88.50

5、条件编译

#include 

int main(int argc, char *argv[])
{
	int num1  = 10;
	int num2 = 20;
	int res;

	#ifdef SUM
		res = num1 + num2;
	#else
		res = num2 - num1;
	#endif

	printf("res = %d\n", res);
	
	return 0;
}

该代码编译预处理以后会变成如下:

int main(int argc, char *argv[])
{
 int num1 = 10;
 int num2 = 20;
 int res;

  res = num2 - num1;

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

 return 0;
}

如果程序原型为:

#include 
#define SUM

int main(int argc, char *argv[])
{
	int num1  = 10;
	int num2 = 20;
	int res;

	#ifdef SUM
		res = num1 + num2;
	#else
		res = num2 - num1;
	#endif

	printf("res = %d\n", res);
	
	return 0;
}

则编译预处理以后为:

int main(int argc, char *argv[])
{
 int num1 = 10;
 int num2 = 20;
 int res;

  res = num1 + num2;

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

 return 0;
}

所以条件编译是根据条件十分满足来确定需要编译的语句块
#ifdef … #else … #endif结构是根据指定的标识符是否定义来确定要编译的语句,如果指定标识符在条件编译语句之前已经定义了,选择ifdef部分的语句进行编译(ifdef或者else部分可以是多条件语句,并且不需要使用大括号),如果指定的标识符没有定义则选择else部分的语句进行编译

条件编译还有另外一种结构#ifndef … #else … #endif

所以前面多文件工程应该写成
stu.h

#ifndef __MYHEAD
#define __MYHEAD
typedef struct Stu
{
	int no;
	char name[12];
	float score;
}Stu;
#endif

head1.h

#ifndef __HEAD_1
#define __HEAD_1
#include "stu.h"

void show_stu(Stu tmp);

#endif

stu.c

#include "stu.h"
#include "head1.h"
#include 

void show_stu(Stu tmp)
{
    printf("%d, %s, %7.2f\n", tmp.no, tmp.name, tmp.score);
}

main.c

include <stdio.h>
#include "stu.h"
#include "head1.h"

int main(int argc, char *argv[])
{
	Stu st = {101, "Jack", 88.5};
	show_stu(st);
	
	return 0;
}

我们也经常使用条件编译语句来进行调试

#include 
#define SUM
//#define DEBUGER

int main(int argc, char *argv[])
{
	int num1  = 10;
	int num2 = 20;
	int res;
   
   #ifdef DEBUGER
    printf("ok1\n");
   #endif

	#ifndef SUM
		res = num1 + num2;
	#else
		res = num2 - num1;
	#endif

	printf("res = %d\n", res);
   
   #ifdef DEBUGER
	printf("ok2\n");
   #endif
	
	return 0;
}

你可能感兴趣的:(编程语言,c语言,开发语言)