C语言:预处理详解(知识点和代码演示)

和黛玉学编程........>


预定义符号

__FILE__ //进行编译的源文件
__LINE__ //文件当前的行号
__DATE__ //文件被编译的日期
__TIME__ //文件被编译的时间
__STDC__ //如果编译器遵循ANSI C,其值为1,否则未定义

比如:d576f738cf314f76a6ee0bc6c8617d87.png

 #define定义常量

#define name stuff 

 注意:在定义标书符的时候,建议不要在最后加上;  容易导致问题

比如这种情况:

if(condition)
max = MAX;
else
max = 0;

这里的if和else就是变成两条语句了

d29800001fdc40858cacc8c8bfca6d23.png

 

#define定义宏

#define机制包括了一个规定,允许把参数替换到文本中,这种实现通常称为宏(macro)或定义宏(define macro)

 #define name( parament-list ) stuff

    parament-list 是⼀个由逗号隔开的符号表,它们可能出现在stuff中(参数列表的左括号必须与name紧邻,如果两者之间有任何空白存在,参数列表就会被解释为stuff的一部分)

 举个例子:#define SQUARE( x ) ((x) *(x))

 这里我们需要注意一下这里的x需要加上括号,最好是,不然结果容易出错

      所以用于对数值表达式进⾏求值的宏定义都应该用这种方式加上括号,避免在使⽤宏时由于参数中的操作符或邻近操作符之间不可预料的相互作用。

 比如:如果你不加括号就出错啦

printf ("%d\n",a + 1 * a + 1 );

14958a083d004ad98ee9709220b52c75.png

 

带有副作用的宏参数

 x+1; //不带副作用
x++; //带有副作用

也就是我们的参数可能在使用过程中改变了自身的值,所以使用的时候你需要小心哦 

 宏替换的规则

1. 在调用宏时,首先对参数进行检查,看看是否包含任何由#define定义的符号。如果是,它们首先被替换。
2. 替换文本随后被插人到程序中原来文本的位置。对于宏,参数名被他们的值所替换。
3. 最后,再次对结果文件进行扫描,看看它是否包含任何由#define定义的符号。如果是,就重复上述处理过程

宏函数的对比

宏经常被用于执行简单运算,比如加减法,找两个数的最大

#definr MAX(a,b) ((a)>(b)?(a):(b))

这个时候就有人说了,我都会函数了,为什么不使用函数呢

 原因如

1. ⽤于调⽤函数和从函数返回的代码可能比实际执行这个小型计算工作所需要的时间更多。所以宏比函数在程序的规模和速度方面更胜⼀筹。
2. 更为重要的是函数的参数必须声明为特定的类型。所以函数只能在类型合适的表达式上使⽤。反之
这个宏怎可以适用于整形、长整型、浮点型等可以⽤于 > 来比较的类型。宏是类型无关的。

 所以宏的参数可以出现类型

#define MALLOC(num, type)\
(type )malloc(num sizeof(type))
...
//使⽤
MALLOC(10, int); //类型作为参数
//预处理器替换之后:
(int )malloc(10 sizeof(int));

 但是并不是宏就是最好的

1. 每次使用宏的时候,一份宏定义的代码将插入到程序中。除非宏比较短,否则可能大幅度增加程序的长度。
2. 宏是没法调试的。
3. 宏由于类型无关,也就不够严谨。
4. 宏可能会带来运算符优先级的问题,导致程容易出现错。

所以勒,就需要大家打代码的时候多多动脑子啦 

373715cef6c54fa8b6eddc9e11f8ef5d.png

 

 #和##

#

#运算符所执行的操作可以理解为”字符串化“

比如:我们想打印出: the value of a is 10

可以写

#define PRINT(n) printf("the value of "#n " is %d", n); 
printf("the value of ""a" " is %d", a); 

在打印的时候上面的#n就会被下面的a替换掉

##

## 可以把位于它两边的符号合成⼀个符号,它允许宏定义从分离的文本片段创建标识符。 ## 被称为记号粘合

比如我们要求两个数的较大值的时候

#define GENERIC_MAX(type) \
type type##_max(type x, type y)\
{ \
return (x>y?x:y); \
}
GENERIC_MAX(int) 
GENERIC_MAX(float)
int main()
{
int m = int_max(2, 3);
printf("%d\n", m);
float fm = float_max(3.5f, 4.5f);
printf("%f\n", fm);
return 0;
}

type type##_max(type x, type y)如果输入int 就可以变成int int-max(int x,int y)

但是在实际情况里面,这个使用比较少

#undef

这条指令⽤于移除一个宏定义

#undef NAME

 命令行定义

许多C的编译器提供了⼀种能力,允许在命令行中定义符号。⽤于启动编译过程。

#include <stdio.h>
int main()
{
int array [ARRAY_SIZE];
int i = 0;
for(i = 0; i< ARRAY_SIZE; i ++)
{
array[i] = i;
}
for(i = 0; i< ARRAY_SIZE; i ++)
{
printf("%d " ,array[i]);
}
printf("\n" );
return 0;
}

 编译指令

gcc -D ARRAY_SIZE=10 programe.c

 条件编译

在编译⼀个程序的时候我们如果要将一条语句(一组语句)编译或者放弃是很方便的

比如代码:

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

常见的:

//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 "filename"

 先在源文件所在目录下查找,如果该头文件未找到,编译器就像查找库函数头头件⼀样在
标准位置查找头文件。如果找不到就提示编译错误

 Linux环境和vs环境的标准头文件的路径是不一样的

库文件

#include <filename.h>

 查找头文件直接去标准路径下去查找,如果找不到就提示编译错误

 嵌套文件的包含

我们已经知道, #include 指令可以使另外⼀个件文被编译。
这种替换的⽅式很简单:预处理器先删除这条指令,并用包含文件的内容替换。
⼀个头文件被包含10次,那就实际被编译10次,如果重复包含,对编译的压力就比较大。

 那就可以:

#ifndef __TEST_H__
#define __TEST_H__
//头⽂件的内容
#endif //__TEST_H__

或者

#pragma once

 感谢阅读到这里,里面许多东西还是需要自己多多练习才能熟能生巧哦!

大家加油!

对你有帮助不如点个赞,关个注wow~~~~~~~~~~~

 

 

相关推荐

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-03-12 08:20:04       98 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-03-12 08:20:04       106 阅读
  3. 在Django里面运行非项目文件

    2024-03-12 08:20:04       87 阅读
  4. Python语言-面向对象

    2024-03-12 08:20:04       96 阅读

热门阅读

  1. 代码随想录三刷day26

    2024-03-12 08:20:04       48 阅读
  2. [IAGC] Kafka消费者组的负载均衡策略

    2024-03-12 08:20:04       47 阅读
  3. 敏捷开发精准估算

    2024-03-12 08:20:04       45 阅读
  4. 常见Linux系统的优劣对比(Ubuntu、RHEL、CentOS)

    2024-03-12 08:20:04       45 阅读
  5. c/c++输入和输出标准库stdio和iostream介绍

    2024-03-12 08:20:04       42 阅读
  6. 数据库学习案例20240311 -mysql xtrabackup 备份与恢复

    2024-03-12 08:20:04       47 阅读
  7. 使用VScode避坑指南

    2024-03-12 08:20:04       47 阅读