c语言-预处理命令

文章介绍了C语言中的预处理命令,包括#define用于宏定义,#include用于引入头文件,以及条件编译#if、#ifdef等,还提到了带参宏定义和预定义宏的使用。同时,文章强调了预处理命令在编译过程中的文本替换特性及其重要性。
摘要由CSDN通过智能技术生成

概念

  • 预处理命令是以#开头的命令,在编译启动时,首先会执行预处理程序来完成预处理,再进行编译和链接
  • 预处理命令对源文件(文本文件)进行简单的文本替换:将代码当场普通文本,进行头文件导入、宏定义、条件编译…
  • 一般位于源文件最前面

#include

将头文件(.h)内容插入到源文件中#include命令所在位置

#include <header.h>     // 到系统路径下查找头文件
#include "header,h"		// 先在当前目录下查找头文件,再到系统路径下查找

""的功能比<>更强大,标准头文件在系统路径下,用<>就足够了,自己编写的头文件一般放在当前目录,需要用""

  • 用法举例:
    c语言一般将需要调用的函数和main()放在不同源文件中,将函数声明放在头文件,并用#include将声明导入main()所在源文件
    my.c代码:
    int sum(int a, int b){
    	return a+b;
    }
    
    my.h代码:
    int sum(int, int)
    
    main.c代码:
    #include <stdio.h>
    #include "my.h"
    int main() {
    	printf("%d", sum(1,2));
    }
    

宏定义

宏定义就是用一个标识符(宏名)表示一个字符串(即源文件中的代码文本)

#define 宏名 字符串   // 预处理时,代码文本中的宏名会被替换成字符串
  1. 宏定义中的字符串是一般意义里的字符序列,不是c语言中的字符串,无需加""
  2. 用字符串替换宏名,是简单粗暴的文本替换,字符串中可以含任何字符,预处理程序对它不作任何检查,如有错误就只能在编译已被宏展开后的源程序时发现
  3. 字符串表达式一般要加()!防止替换后因为运算优先级出问题
  4. 宏定义不是语句,行末无需加;,如加上则分号也会加入替换
  5. 宏定义必须在函数外,其作用域是从宏定义命令起,到源程序结束;如要提前终止作用域,可以用#undef命令
  6. 代码中的宏名如果被引号包围,预处理程序就不会对它宏替换
    #define OK 100
    void main() {
    	printf("OK\n");        \\ OK
    }
    
  7. 宏名允许嵌套
    #define PI 3.141596
    #define C 2*PI*r
    
  8. 可以用宏名表示数据类型,但这和typedef定义数据类型说明符存在区别
    宏名是简单的文本替换,不涉及编程语法;而typedef 数据类型 新的数据类型名;是在编译时进行的,是在语法层面上给原有的数据类型起了个新名字
    #define P1 int *
    typedef int(*P2);
    
    P1 a,b;  
    
    宏名替换后为 int *a,b;,a为int *类型,b为int类型
    P2 a,b;
    
    P2在typedef后语法上指的就是int *类型,所以,a和b均为int *类型
  9. 预定义宏:c语言已经预先定义好的宏,可以直接使用
    printf("Date : %s\n", __DATE__);    // 当前编译日期
    printf("Time : %s\n", __TIME__);  	// 当前编译时间
    printf("File : %s\n", __FILE__);	// 当前源文件名
    printf("Line : %d\n", __LINE__);	//当前行号
    

带参宏定义

对带参数的宏,在展开过程中,不仅要用字符串替换宏名,还要用实参替换形参
定义:

#define 宏名(形参列表) 字符串  

调用:

宏名(实参列表);

例如:

#define SQ(y) ((y)*(y))   // 定义
k = SQ(5) 				// 调用:展开后为k=((5)*(5)) 
  • 带参宏定义时,宏名和形参列表之间不能存在空格,否则会被识别为不带参的宏定义

    #define SQ (y) ((y)*(y))    // SQ会被直接替换成(y) ((y)*(y))
    
  • 形参列表无需指明数据类型:因为带参宏定义只是简单的文本替换,将形参文本替换成实参文本,并不会真的为形参分配内存;而函数是将实参的值copy给形参

    #define SQ1(y) ((y)*(y))
    
    int SQ2(int y) {
    	return y * y;
    }
    
    void main() {
    	int a =1, b = 1;
    	printf("%d, %d\n", a, SQ1(a++));    //输出3,1:展开为((a++)*(a++)),两次自加
    	printf("%d, %d", b, SQ2(b++));   	// 输出2, 1:实参传值给形参,只进行一次自加
    }
    
  • 字符串内的形参和整个字符串通常要用括号括起来以避免出错!

    #define SQ(y) y*y   // 如果字符串内形参不加括号,字符串也不加括号
    
    k = SQ(a+1) +1      	// 展开为a+1*a+1+1
    

条件编译

编译源程序中满足条件的程序段,使生成的目标程序较短,减少内存的开销并提高程序的效率
1) #if 条件为整型常量表达式
如果条件值为真即整型常量表达式值非0,就编译对应代码块

#if 整型常量表达式
	代码块
#elif 整型常量表达式
	代码块
...
#elif 整型常量表达式
	代码块
#else
	代码块
#endif
	
  • 表达式中不能含变量,且结果必须为整数
  • #elif和#else可省略,可以是单分支,双分支,多分支

2)#ifdef和#ifndef 条件为宏名

#ifdef 如果宏名已被定义,就编译对应代码块
#ifndef 如果宏名还没有被定义,就编译对应代码块

#ifdef 宏名
	代码块
#else 
	代码块
#endif

#else可省略,可以是单分支,双分支

#error

预处理到#error命令时将停止编译并输出用户自定义的错误消息

#error 自定义错误消息
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值