C语言预处理下

简介: C语言预处理下

宏和函数对比

宏#define DOUBLE(x) ((x)+(x))也可以通过函数实现,那么为什么不用函数来实现呢?


这就要说说宏和函数的区别了


宏的优点:


用于调用函数和从函数返回的代码可能比实际执行这个小型计算工作所需要的时间更多,函数需要创建栈帧等等,所以宏比函数在程序的规模和速度方面更胜一筹。

宏与类型无关在前面我们定义的各个宏中可以看出,宏中的参数不需要指明类型,函数的参数必须声明为特定的类型,所以函数只能在类型合适的表达式上使用。反之这个宏怎可以适用于整形、长整型、浮点型等

宏的缺点:


每次使用宏的时候,一份宏定义的代码将插入到程序中。除非宏比较短,否则可能大幅度增加程序的长度。

宏是没法调试的。

宏由于与类型无关,所以也就不够严谨。

宏可能会带来运算符优先级的问题,导致程容易出现错。

所以最好使用宏去执行一些简单的运算


宏和函数的使用语法很相似,所以我们一般无法通过语言本身区别宏和函数

所以要有一个习惯: 把宏的名全部大写,函数名不全大写


3.#undef

#undef用于移除一个宏定义


#undef NAME/ /如果现存的一个名字需要被重新定义,那么它的旧名字首先要被移除。


观察一下代码:


#define MAX 1000
int a = MAX;
#undef MAX
int b = MAX


在VS中,我们可以看到第二个MAX报错了,因为#undef已经移除了上面的宏定义



99236e8b99dc49628797adcaa093e7cb.png



4.条件编译

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


调试性的代码,删除可惜,保留又碍事,所以我们可以选择性的编译。


或者我们编写一个可移植的代码,在不同平台就要编译不同部分的代码


下面是几个常见的条件编译指令:


1.

#if 常量表达式 //如果为真,#if与#endif中的语句参加编译,如果为假则不参加编译
   //...
#endif 
//...
1


举个例子:


#define __DEBUG__ 1
int main()
{
#if __DEBUG__ 
  printf("hehe");  
#endif
  printf("haha");
}


__DEBUG__ 值为1,为真,所以会编译

结果会输出:hehehaha

对于会编译和不会编译的语句,在IDE中会呈现不同的颜色


fb7aad90eac24d46b99d26ebd1768167.png





2.

多个分支的条件编译

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


示例:



#define M 2
int main()
{
#if M ==0
  printf("0");
#elif M ==1
  printf("1");
#elif M==2
  printf("2");
#else
  printf("3");
#endif
}


该代码只会编译printf("2");语句


3.判断是否被定义

//被定义
#if defined(symbol)
#ifdef symbol
//未被定义
#if !defined(symbol)
#ifndef symbol


示例:

#define A
#define B
int main()
{
#ifdef A
  printf("yes\n");
#endif
  return 0;
#if defined(B)
  printf("yes\n");
#endif
  return 0;
#if !defined(C)
  printf("no\n");
#endif
  return 0;
#ifndef C
  printf("no");
#endif
  return 0;
}


4.上面那些判断条件也可以如if else语句一样嵌套使用


5.文件包含

头文件的包含

本地文件包含:


#include "filename"

1

查找策略:先在源文件所在目录下查找,如果该头文件未找到,编译器就像查找库函数头文件一样在标准位置查找头文件。


库文件包含:


#include <filename.h>
1

如果还按照本地文件的包含从源文件所在目录下开始找是在浪费时间,因为库文件不可能在源文件所在目录下。

所以将文件名用<>扩起来,就是让程序直接从标准位置查找头文件。


对于库函数,也是可以用""包含的,就是查找的效率就低些,也不容易区分是库文件还是本地文件。


嵌套文件包含

如果一个程序中,包含了许多文件,并且这些文件中存在重复文件,在预处理阶段这些都会被导入,这回导致程序冗余


如何解决这个问题,答案就是:条件编译


在每个头文件的开头写:


#ifndef __TEST_H__
  #define __TEST_H__
  //头文件的内容
#endif  
1

2

3

4

举个例子,如图:




ae668cc5f7c44da38d39b620185bd497.png




当某个源文件第一次导入这个头文件,先走#ifndef __TEST_H__语句,发现 __TEST_H__并未定义,所以接下来运行#define __TEST_H__定义了这个标识符,如果这个源文件又导入了这个文件,当运行到#ifndef __TEST_H__是会发现已经定义了 __TEST_H__,所以就不会包含#ifndef和#endif间的语句了.


或者


#pragma once


目录
相关文章
|
1月前
|
编译器 C语言
C语言--预处理详解(1)
【10月更文挑战第3天】
|
1月前
|
编译器 Linux C语言
C语言--预处理详解(3)
【10月更文挑战第3天】
|
1月前
|
自然语言处理 编译器 Linux
【C语言篇】编译和链接以及预处理介绍(上篇)1
【C语言篇】编译和链接以及预处理介绍(上篇)
41 1
|
22天前
|
C语言
【c语言】你绝对没见过的预处理技巧
本文介绍了C语言中预处理(预编译)的相关知识和指令,包括预定义符号、`#define`定义常量和宏、宏与函数的对比、`#`和`##`操作符、`#undef`撤销宏定义、条件编译以及头文件的包含方式。通过具体示例详细解释了各指令的使用方法和注意事项,帮助读者更好地理解和应用预处理技术。
23 2
|
1月前
|
编译器 Linux C语言
【C语言篇】编译和链接以及预处理介绍(下篇)
【C语言篇】编译和链接以及预处理介绍(下篇)
32 1
【C语言篇】编译和链接以及预处理介绍(下篇)
|
1月前
|
C语言
C语言--预处理详解(2)
【10月更文挑战第3天】
|
1月前
|
编译器 C语言
C语言预处理详解
C语言预处理详解
|
1月前
|
存储 C语言
【C语言篇】编译和链接以及预处理介绍(上篇)2
【C语言篇】编译和链接以及预处理介绍(上篇)
36 0
|
3月前
|
存储 自然语言处理 程序员
【C语言】文件的编译链接和预处理
【C语言】文件的编译链接和预处理
|
3月前
|
程序员 编译器 C语言
C语言中的预处理指令及其实际应用
C语言中的预处理指令及其实际应用
88 0