目录
一.##和#运算符
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.多分支的条件编译(跟if和else差不多)
#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
//就可以避免头文件的重复引用