预处理详解(二)-- 条件编译 - 头文件包含 - ##和#运算符


在这里插入图片描述

一.##和#运算符

1.#运算符(字符串化)

#运算符将宏的参数变成一个字符串 这里的#并不是#include或#define中的# 在举例子之前,说明一下字符串是有自动连接的特点的

printf("hello""world");
//打印后是helloworld

在这里插入图片描述

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

在这里插入图片描述

2.##运算符(粘合符)

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

例如:

//求两个数(不同类型的变量)的最大值
//定义了函数模版
#define a_MAX(type) \
type type_##MAX(type x,type y) \
{                            \
  return ((x>y)?x:y);         \
}                              

在这里插入图片描述
##和#算是冷门的知识点

拓展:
offsetof – 宏
计算结构体的成员的偏移量
是相较于首字节地址的偏移量
包含的头文件是 #include<stddef.h>
printf(结构体关键字,结构体成员名);

二.条件编译(很重要)

在编译一条指令时删除又可惜保留有碍事,那么我们可以选择有条件的编译

常见的条件编译指令:

1.单分子的条件编译
#if 常量表达式 //常量表达式由预处理器求值
  //使用      //条件如果为假(0),不参加编译,非0为真,参与编译
  //...
#endif
2.多分支的条件编译(跟ifelse差不多)
#if 常量表达式
//...
#elif 常量表达式
//...
#else
//...
#endif
3.判断是否被定义
#define MAX 1 //定义了MAX
#if defined(MAX)//执行
//...
#endif

#ifdef MAX //定义了MAX,执行
//...
#endif

#if !defined(MAX)//这两个都是如果未定义就执行
#ifndef MAX

4.嵌套指令(if else 的嵌套也差不多)
#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

三.命名约定

函数的命名和宏的命名非常相似
所以在语言上无法区分两者

1.宏名的命名

宏名全都大写(MAX)

2.函数的命名

函数名不全大写(Add)

四.#undef(用于移除一个宏定义)

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

int main()
{
	int a = 1;
	int b = 2;
	int ret = MAX(a, b);
    #undef MAX
	//MAX的宏定义被移除了,后面无法运行了
	int c = MAX(a, b);

	return 0;
}

五.命名行约定

许多编译器允许在命令行中定义符号(但是vs不允许,vscode可以),用于启动编译过程

例如:我们根据同一个源文件要编译出一个程序的不同版本时,这个还是有用的。(假定某个程序中声明了某个长度的数组,如果机器内存有限,要一个小的数组,但另一个机器内存大些,我们要一个大点的数组)

int main()
{
  int arr[SIZE];//在命令行改变SIZE的大小,即可改变数组的大小
  int i = 0;
  for(i = 0;i < SIZE;i++)
  {
   arr[i] = i;
  }
  for(i = 0;i < SIZE;i++)
  {
   printf("%d ",arr[i]);
  }
  return 0;
}

六.头文件被包含的方式

1.本地文件包含

#include"filetest"

先在源文件(本工程路径)下查找,编译器再到标准库中去查找,再没找到就编译错误

vs环境下标准头文件的路径:
C:\Program Files (x86)\Microsoft Visual Studio 12.0 \VC\include (vs2013下的路径)

Linux环境下标准头文件的路径:
/usr/include

2.库文件包含

查找标准库下的头文件,就直接到标准库下查找
找不到就编译错误

本地文件包含可以用""吗?
答案是肯定的,但是这样做效率就低些,这样也不易于区分是库文件还是本地文件

3.嵌套文件包含(重复引入了同一个头文件)

//test.c
#include"test.h"
#include"test.h"
#include"test.h"

int main()
{
  //使用
  //...
  return 0;
}
//test.h
test();
//使用
//...

test.h头文件在test.c中被重复(多次)包含,那么test.h中的内容会被拷贝3份到test.c中
如果头文件的工程量比较巨大,这样被多次拷贝,后果将不堪设想
那么怎么解决这个问题呢?
可以使用条件编译

#ifndef __ TEST_H__ //如果没有这个文件为真就执行下面代码
#define __ TEST_H__//定义这个文件
//使用
//...
#endif
// 下次进入 #ifndef 为假(有这个文件了),不进入,一直为假
//只出现一份内容

包含一次头文件,判断一次#ifndef,第二次包含也是如此,如此往复
第一次包含头文件是没定义__TEST_H__的,所以会执行里面的代码,第二次已经定义了__TEST_H__ ,为假,不执行代码了,往后也不执行了

#pragma once
//就可以避免头文件的重复引用
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值