c语言:预处理指令详解

发布时间:2024年01月16日

预定义符号

? ?C语言设置了?些预定义符号,可以直接使用,预定义符号也是在预处理期间处理的。

例子:

#define _CRT_SECURE_NO_WARNINGS 
#include<stdio.h>

int  main()
{
	//源文件的位置,文件名
	printf("%s\n", __FILE__);
	//这条代码的行号
	printf("%d\n", __LINE__);
	//日期
	printf("%s\n", __DATE__);
	//时间
	printf("%s\n", __TIME__);
	//检测是否支持ansic标准
	printf("%d\n", __STDC__);
	//电脑显示未定义,就是不是支持,支持的话会打印出1,
	//vs2022不支持
}

效果:


#define定义常量

格式

? ? ? ? ? ? ? ?#define 被替换的变量替换内容

?用法

如:

#define a 10+2
#define str hello world
//可以是常量,字符串,甚至是一串代码
//替换关键字
#define reg register
//自动补加break
#define CASE break;case
//替换一段代码
#define DEBUG_PRINT printf("file:%s\t" line:%d\t \date:%s\t time:%s\n",\__FILE__,__LINE__,__DATE__,__TIME__)

特别注明:#define 被替换 替换后面最好不加分号(避免出现格式问题)。


#define定义宏

#define机制包括了?个规定,允许把参数替换到文本中,这种实现通常称为宏(macro)或定义宏 (definemacro)。它允许在代码中创建符号常量或简单的函数替换。

宏的申明方式:

#define name(参数)? 表达式

#define MACRO_NAME value

其中,MACRO_NAME?是宏的名称,value?是宏的值或替换文本。

例子:

#define SQUARE(x) ((x)*(x))
#define square(a) (a)*(a)
#include<stdio.h>
int main()
{
	int a = 5;
	printf("%d\n", square(a + 1));
}

?结果是36.

特别注意

  1. 宏的参数中如果有操作符,和宏的内容中的操作符因为运算符有优先级的问题,可能导致运算顺序不达预期。
  2. 解决方法:宏在书写时,给参数,整个表达式都带上括号。

带有副作用的宏参数

当宏参数在宏的定义中出现超过一次的时候,如果参数带有副作用,那么你在使用这个宏的时候就可能出现危险,导致不可预测的后果。副作用就是表达式求值的时候出现的永久性效果。

例如:

#define a 1
b=a+1;//a=1,无改变a值
b=++a;//a=2,永久性改变a值

宏的替换规则

简单的文本替换:当预处理器遇到宏的名称时,会将其替换为宏的值。这是最基本的宏替换规则。

参数替换:如果宏定义包含参数,预处理器会将调用宏时提供的实际参数替换到宏定义中的参数位置。

宏展开:预处理器会递归地展开宏。这意味着,如果宏的值本身包含其他宏,预处理器会继续展开这些嵌套的宏,直到没有更多的宏需要展开为止。

#include <stdio.h>

#define MULTIPLY(a, b) ((a) * (b))
#define SQUARE(x) MULTIPLY(x, x)

int main() 
{
    int result = SQUARE(3 + 2);
    printf("Result: %d\n", result);
    return 0;
}

? ? ? 在这个例子中,SQUARE宏调用了MULTIPLY宏。当预处理器展开SQUARE(3 + 2)时,它会进行如? ? ? ? ? 下的宏展开过程:

  1. SQUARE(3 + 2)?展开为?MULTIPLY(3 + 2, 3 + 2)
  2. MULTIPLY(3 + 2, 3 + 2)?展开为?((3 + 2) * (3 + 2))
  3. 最终展开为?((3 + 2) * (3 + 2)),得到最终的表达式?((3 + 2) * (3 + 2))

宏的作用域:宏的作用域从宏定义的位置开始,一直到文件末尾或宏的#undef指令为止。在作用域内,预处理器会对宏进行替换。


宏的优势

可以传类型,如

#define MALLOC(num, type)\
 (type *)malloc(num sizeof(type))

 ...
 //使? 
 MALLOC(10, int);//类型作为参数 
 
 //预处理器替换之后: 
 (int *)malloc(10 sizeof(int));

#和##

#运算符

#运算符将宏的?个参数转换为字符串字面量。它仅允许出现在带参数的宏的替换列表中。?#运算符所执行的操作可以理解为“字符串化”。

#include<stdio.h>
#define PRINT(val,format) printf("the value of " #val " is "format"\n",val)
//相当于printf("the value of""a""is""%d""\n",a)
int main()
{
	int a = 10;
	PRINT(a, "%d");
	return 0;
}

##运算符

## 可以把位于它两边的符号合成一个符号,它允许宏定义从分离的文本片段创建标识符。 ## 被称 为记号粘合这样的连接必须产生?个合法的标识符。否则其结果就是未定义的。

例子

宏定义一个加法函数。

#include<stdio.h>
#define GENERIC_MAX(type) type type##_max(type x, type y)\
{\
 return (x>y?x:y);\
}
//##相当于把int和_max连起来,语法,不连起来type_max是一个整体,替换不了
//int int_max(int x,int y){return (x>y?x:y)}
GENERIC_MAX(int)
int main()
{
	int m = int_max(3, 5);
	printf("%d\n", m);
	return 0;
}


命名约定

  1. 把宏的名字全部大写
  2. 把函数的名字不全部大写

#undef

用于移除一个宏定义

此时,我们可以发现第二次打印显示MAX未声明,说明MAX的宏定义已经被移除。


条件编译指令

? ? ? 在编译一个程序的时候我们如果要将一条语句(一组语句)编译或者放弃是很方便的。因为我们有条件编译指令。

#include<stdio.h>
#define __a__
int main()
{
	int i = 0;
	int arr[10] = { 0 };
	for (i = 0; i < 10; i++)
	{
		arr[i] = i;
        #ifdef  __a__
		printf("%d\n", arr[i]);
        #endif  __a__
	}
}

当我们注释掉#define __a__时,#ifdef __a__和#endif __a__之间的语句不会被执行。a可以是其他表达式。

常见的条件编译指令

  1. 普通

    #if 常量表达式
    
     //...
    
    #endif
    
    //常量表达式由预处理器求值。 
    
    如:
    
    #define __DEBUG__ 1
    #if __DEBUG__
    
     //..
    
    #endif
    

  2. 多个分支的条件编译

    #if 常量表达式
    
     //...
    
    #elif 常量表达式
    
     //...
    
    #else
    
     //...
    
    #endif
    

  3. 判断是否被定义

    #if defined(symbol)
    #ifdef symbol
    #if !defined(symbol)
    #ifndef symbol
    

  4. 嵌套指令

    #if defined(OS_UNIX)
    
     #ifdef OPTION1
    
     unix_version_option1();
    
     #endif
    
     #ifdef OPTION2
    
     unix_version_option2();
     #endif
    
    #elif defined(OS_MSDOS)
    
     #ifdef OPTION2
    
     msdos_version_option2();
     #endif
    
    #endif
    

头文件被包含的方式

本地文件

? ? ? ? ? #include"add.c"

库文件

? ? ? ? ? #include<stdio.h>


嵌套文件包含

假如有一个本地文件被重复包含、引入,编译压力就会很大,这时,我们就可以引用条件编译解决。

或者,在引入前,加上

#pragma once

文章来源:https://blog.csdn.net/2303_80025768/article/details/135621530
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。