预处理(c)

目录

一、C语言编译过程

1.1 预处理

1.2 编译

1.3 汇编

1.4 链接

二、宏定义

2.1 不带参数的宏定义

2.2 带参数的宏定义

2.3  带参宏与带参函数区别

三、#include 指令

四、选择性(条件)编译

4.1 #if 命令 、#else命令和elif命令

①、#if 命令

         ②、#else命令

③、#elif命令

4.2 #ifdef 命令 及 #ifndef命令

4.3 #undef命令

4.4 #line命令

4.5 #pragma命令



一、C语言编译过程

c语言编译过程:

预处理、编译、汇编、链接

1.1 预处理

gcc -E hello.c -o hell.i

   将 .c 中的头文件展开、宏展开

   生成的文件是 .i 文件

注意:

  • 预处理阶段只是对include等预处理操作进行处理并不会进行语法检查,这个阶段有语法错误也不会报错,第二阶段即编译阶段才会进行语法检查

1.2 编译

gcc -S hello.i -o hello.s

   将预处理之后的 .i 文件生成 .s 汇编文件

1.3 汇编

gcc -c hello.s -o hello.o

将 .s 汇编文件生成 .o 目标文件 

1.4 链接

gcc hello.o -o hello_elf

将 .o 文件链接成目标文件 

二、宏定义

宏是在预编译时候进行替换的

2.1 不带参数的宏定义

作用范围:从定义的地方到本文件的末尾

#define PI 3.14159

一般用大写字母表示,以便与变量名相区分


#define SIDE 5
#define PERIMETER 4 * SIDE
#define  AREA SIDE * SIDE

#define  STAND "you are girl"
printf(STAND);

//宏定义的好处:只要改变了定义是的常量表达式,则代码中只要是使用该宏定义的 位置都会改变 
#define PI 3.14

void main()
{
	printf("PI = %lf\n", PI);
	
	double d;
	d = PI;
	printf("d = %lf\n", d);
   
}

 

 

 如果想在中间中止宏定义范围

#undef PI//中止PI的作用

🌭注意点

  • 如果在串中含有宏名,则不进行替换
#define PI 3.14

void main()
{
    char xx[10] = "hello ,PI";	

	printf("%s\n", xx);
   
}

  • 如果串长于一行,可在该行末尾用‘ \ ’续行。
  • #define 命令出现在程序中函数外面,宏名有效范围定义命令之后到此源文件结束。

  • 用#undef 命令终止宏定义的作用域
#define TEST "this is an example" 

main()
{
   	printf(TEST);
   	printf("\n");
   	#undef TEST
}	

宏的好处:需要修改宏定义,其他地方在预编译的时候就会重新替换

2.2 带参数的宏定义

#define MIX(a,b) ((a) * (b) + (b))

main()
{
	int x = 5, y = 9;
	
   	printf("%d, %d\n", x, y);
   	printf("the min number %d\n", MIX(x, y));
   	printf("\n");
   	
}	

#include <stdio.h> 

//带参宏,类似一个简单的函数,将函数的参数进行设置 
//#define S(a, b)  a * b
//修改:
#define S(a, b)  ((a) * (b))

void main()
{

	printf(" %d\n", S(2, 4));
	
	//注意:宏定义只是简单的替换, 不会自动加括号 
	//2 + 8 * 4 //乘法优先级比加法高 
	//带括号: (2+8)*4 
	printf(" %d\n", S(2 + 8, 4));
   
}

 

注意点

  • 宏定义时参数要加括号。
  • 宏扩展使用括号来保护表达式低优先级的操作,以便调用时达到想要的效果。
  • 带参的宏的形参没有类型名

2.3  带参宏与带参函数区别

  • 带参宏被调用多少次就会展开多少次,执行代码时没有函数调用的过程,不需要压栈弹栈,所以带参宏,是浪费了空间,因为被展开多次,节省时间。
  • 带参函数,代码只有一份,存在代码段,调用的时候去代码段取指令,调用时候,压栈弹栈,有个调用的过程,所以说,带参函数节省空间,浪费时间,
  • 带参函数的形参是有类型的,带参宏的形参是没有类型的
  • 如果功能实现代码相对简单,并不需要开辟太多空间,可以选择使用带参宏,但大多数情况都会使用函数。

三、#include 指令

#include可以将另外一个源文件全部内容包含进来,

#include <stdio.h> //系统指定的路径下找的头文件
#include "studio.h" //先在当前目录下找头文件,找不到,再到系统指定路径下找
  • 尖括号:系统到存放C库函数头文件所在的目录中寻找要包含的文件,即标准方式。如:为调用库函数包含的头文件
  • 双引号:系统先在用户当前目录中寻找要包含的文件,若找不到,再存放C库函数头文件所在的目录中寻找要包含的文件.如:用户自己编写的文件

存在将文件嵌入#include命令中,嵌套的嵌入文件。

文件f1.h
#define P printf
#define S scanf
#define D "%d"
#define C "%c"


文件f2.c
#include <stdio.h>
#include "f1.h"

void main()
{
    int a;
    P("请输入第一个整数值:\n");
    S(D, &a);
}

一般情况将以下内容放在 . h中

  • 宏定义
  • 结构、联合和枚举声明
  • typedef声明
  • 外部函数声明
  • 全局变量声明

注意点

  • 一个#include 命令只能指定一个被包含的文件
  • 😊include也可以包含.c文件,但不要包含,因为include包含的文件会在预处理编译被展开,如果一个.c被包含多次,展开多次,会导致函数重复定义。所以不要包含.c文件
  • 😊预处理阶段只是对include等预处理操作进行处理并不会进行语法检查,这个阶段有语法错误也不会报错,第二阶段即编译阶段才会进行语法检查
  • 文件包含可以嵌套,即一个被包含的文件中还可以包含另一个被包含文件
  • 当在filel.c中包含文件file2.h时,那么在预编译后就成为一个文件而不是两个文件,这时如果file2.h中有全局静态变量,则该全局变量在filel.c文件中也有效,这时不需要再用extern声明。

四、选择性(条件)编译

4.1 #if 命令 、#else命令和elif命令

一般用于注释多行代码

#if 0 

...

#endif

①、#if 命令

#if 命令:

如果 #if 指令后的参数表达式为真,则编译 #if 到 #endif之间的程序段,

否则跳过这段程序。#endif用来表示#if段的结束。

形式:

#define NUM 50

main()
{
	int i = 0;
	
	#if NUM > 50
	 	i++;
	#endif
	
	#if NUM == 50
	 	i = i + 50;
	#endif
	
	#if NUM < 50
	 	i--;
	#endif
	
   	printf(" %d\n", i);
   	printf("\n");
}	

②、#else命令

如果表达式为真,编译第一段代码,否则编译第二段代码

#else :为#if为假时提供另一种选择,

#define NUM 50

main()
{
	int i = 0;
	
	#if NUM > 50
	 	i++;
	#else
	#if NUM < 50
	 	i--;
	#else
	 	i = i + 50;
	#endif
	#endif
	
   	printf(" %d\n", i);
   	printf("\n");
}	

③、#elif命令

#elif命令: 用来建立“如果 。。。 或者如果。。。”

#define NUM 50

main()
{
	int i = 0;
	
	#if NUM > 50
	 	i++;
	#elif NUM < 50
	 	i--;
	#else
	 	i = i + 50;
	#endif
	
   	printf(" %d\n", i);
   	printf("\n");   	
}	

4.2 #ifdef 命令 及 #ifndef命令

 #ifdef :如果有定义

  • #ifdef: 如果宏替换名已经被定义,则对“语句段”进行编译;如果没有定义#ifdef后面的宏替换名。则不执行。

  • #ifdef#else连用:

     如果宏替换名已被定义过,则对“语句段1”进行编译;

     如果没有定义#ifdef后面的宏替换名,则对“语句段2”进行编译。

 #ifndef:如果无定义

  • #ifndef: 如果宏替换名未被定义,则对“语句段”进行编译;如果定义#ifndef后面的宏替换名。

  • #ifndef#else连用:

如果宏替换名未被定义过,则对“语句段1”进行编译;

如果定义#ifndef后面的宏替换名,则对“语句段2”进行编译。 

 #ifndef 常用在防止头文件重复包含的情况。常用于多文件编程中.h的第一行是#ifndef,最后一行是#endif

#define STR "dillgence is the parent of success\n"

main()
{
	#ifdef STR
		printf(STR);
	#else
		printf("dillgence is the root of all evil\n");  
	#endif
	
	printf("\n");
	
	#ifndef ABC
		printf("dillgence is the root of all evil\n");
	#else
		printf(STR);  
	#endif
	
   	printf("\n");
   	
}	

4.3 #undef命令

#undef 可以删除事先定义的宏定义。

#define MAX_SIZE 100
char array[MAX_SIZE];
#undef  MAX_SIZE

4.4 #line命令

#line 用于改变__LINE__与__FILE__的内容,__LINE__存放当前编译行的行号,__FILE__存放当前编译的文件名

行号为源程序中当前行号,文件名为源文件的名字,命令#line主要用于调试及其他特殊应用。

#line 100 "15.7.C" 
 
  main()
{
  	printf("1.当前行号:%d\n", __LINE__);
  	printf("2.当前行号:%d\n", __LINE__);
	printf("\n");
 }
 

4.5 #pragma命令

#pragma:设定编译器的状态,或指示编译器完成一些特定动作。

  • message参数:该参数能够编译信息输出窗口中输出相应的信息。
  • code_seg参数:设置程序中函数代码存放的代码段
  • code参数:保证头文件被编译一次。

预定义宏名

  • __LINE__:当前被编译代号的行号。
  • __FILE__:当前源程序的文件名称。
  • __DATE__:当前源程序的创建时间
  • __TIME__:当前源程序的创建时间
  • __STDC__:用来判断当前编译器是否为C标准,其值为1,表示符合标准C,否则不是标准C

选择性编译都是在预处理阶段干的事情。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值