一篇思维导图送给大家(总结思维导图真的提升非常大);
目录
预处理
一些预定义符号
这些预定义符号就可以打印对应的内容,比如我们来举个例子
int main()
{
printf("file:%s\n", __FILE__);//进行编译的源文件
printf("line:%d\n", __LINE__);// 文件当前的行号
printf("data:%s\n", __DATE__);// 文件被编译的日期
printf("time:%s\n", __TIME__);// 文件被编译的时间
return 0;
}
#define定义标识符
#define M 10
int main()
{
printf("%d\n", M);
return 0;
}
- 这里会有一个问题,#define后面要不要加分号呢?
答案是不要。为什么不要呢?
看这个例子如果M后面加上分号,第一个条件里的内容就变成M;;又多了一个分号就会导致语法错误。
#define定义宏
什么是宏呢?宏就是可以将参数替换到文本中。
说这个可能大家都不太理解,我来用代码给大家举个例子。
#define MAX(x,y) ((x)>(y)?(x):(y))
int main()
{
int a = 10;
int b = 20;
printf("max = %d\n", MAX(a, b));
return 0;
}
这里需要注意的是
还有一点要注意的是
#define DOUBLE(x,y) x*y
int main()
{
int a = 2;
int b = 3;
printf("%d\n", DOUBLE(a + 2, b + 1));
return 0;
}
这里的答案是多少呢?难道是4*4=16么?
答案是错误的这里会将x,y替换变成,a+2*b+1 这里就会导致操作符优先级问题导致结果不是你想要的,解决办法是,当你害怕操作符优先级问题导致结果错误的时候,你就可以加上括号避免操作符优先级问题。
#define DOUBLE(x,y) (x)*(y)
我们改成这样就不会有问题了。
#define的替换规则
总结:先对参数检查,看是否包含define定义的符号,如果有就先将他们替换,然后将替换的值插入到文本中,最后再次对结果扫描看是否有define定义的符号,如果有就继续重复。
#和##
#的作用是把一个宏参数变成对应的字符串
这里有一个例子
这里我们可以发现不管printf里放入几个字符串,最后都会将他们转化成一个。好我们接下来看
printf("the value of b is %d\n", b);
我可不可以让双引号里的b改变呢?
当然可以这就需要用到#
#define PRINT(n) printf("the value of "#n" is %d\n", n)
int main()
{
int a = 10;
//printf("the value of a is %d\n", a);
PRINT(a);
//printf("the value of ""a"" is %d\n", a);
int b = 20;
//printf("the value of b is %d\n", b);
PRINT(b);
//printf("the value of ""b"" is %d\n", b);
return 0;
}
这里宏中的#b是将原来的字以字符串的形式替换,也就是比如b 就会以”b”替换到文本中。
##的作用是可以将两个符号合成一起
#define ADD(x,y) x##y
int main()
{
printf("%d\n", ADD(521,1314));
return 0;
}
带副作用的宏参数
#define MAX(x,y) ((x)>(y)?(x):(y))
int main()
{
int x, y, z;
x = 5;
y = 8;
z = MAX(x++, y++);
printf("x=%d y=%d z=%d\n", x, y, z);
return 0;
}
这个答案会是什么呢?
这里要注意的是在使用宏的时候它不是将x和y算好之后在替换,而是将x,y替换成x++,y++
替换结果变为:
z = ((x++) > (y++) ? (x++) : (y++));
所以是这样算的x=5,y=8,x和y进行比较之后x++,x=6,y++,y=9,返回给z,z=9,然后y又要++,所以y=10.
宏与函数对比
我们拿两个数比较举例子
函数写法:
int get_max(int x, int y)
{
return x > y ? x : y;
}
int main()
{
int a = 10;
int b = 20;
int ret = get_max(a, b);
return 0;
}
宏的写法:
#define MAX(x,y) ((x)>(y)?(x):(y))
int main()
{
int a = 10;
int b = 20;
printf("max = %d\n", MAX(a, b));
return 0;
}
我们从时间上考虑,先说宏,宏经过一步逻辑运算就可以得到结果,而函数呢,它需要经过函数调用,逻辑运算和返回值 这三步,所以宏比函数更有优势,但是只是这样简单的逻辑运算,宏更占优势,但是如果碰见复杂的运算,函数比宏更有优势。
我们在从类型上考虑,比如我们要比较浮点数和一个整形的大小,这时使用函数,由于函数只能返回一个值要么是整形要么是浮点数,所以在比较不同类型数值大小的时候我们就不能用函数,而宏既可以返回任意类型。
宏的缺点:
宏也有不好的地方,当一个宏代码长度很长的时候,他要替换成多处,这样就会导致代码长度增长。
宏是无法调试的
宏与类型无关,所以是不严谨的
宏还会出现操作符优先级问题,容易导致出错
宏是不可以递归的。
#undef
命令行定义
许多 C 的编译器提供了一种能力,允许在命令行中定义符号。用于启动编译过程。例如:当我们根据同一个源文件要编译出不同的一个程序的不同版本的时候,这个特性有点用处。(假 定某个程序中声明了一个某个长度的数组,如果机器内存有限,我们需要一个很小的数组,但是另外一 个机器内存大写,我们需要一个数组能够大写。)
条件编译
调试性的代码,删除可惜,保留又碍事,所以我们可以选择性的编译。
1.#if 常量表达式//...#endif// 常量表达式由预处理器求值。如:#define __DEBUG__ 1#if __DEBUG__//..#endif2. 多个分支的条件编译#if 常量表达式//...#elif 常量表达式//...#else//...#endif3. 判断是否被定义#if defined(symbol)#ifdef symbol#if !defined(symbol)#ifndef symbol
文件包含
终于到了最后一个,这个文件包含在高质量c/c++编程书中就出过两个关于头文件包含的问题,所以这块非常重要。
我们在进行头文件包含的时候,主要使用#include命令,这里有两种方式,一种是#include后面用双引号,一种是#include后面用<>.那两种是否有区别呢?
先说#include" "
再说#include<>
那标准库文件也可以用include"",只不过他要查找两次更浪费时间,但是这样做查找的效率就低些,当然这样也不容易区分是库文件还是本地文件了。
总结 #include""是先在本源文内目录寻找,然后在去标准库路径寻找,而#include<>是直接去标准库路径去寻找。
头文件重复包含问题
在一个工程中如果出现头文件重复包含,如果有几十万行代码的话,再加上头文件重复包含就会导致大量浪费时间,就会导致编译工程时间要很长时间。
头文件重复包含解决方案
第一种是使用#pragma once
另外一种是可以在头文件最前面加上这个。
#ifndef __TEST_H__
#define __TEST_H__
//头文件的内容
#endif //__TEST_H__
高质量c/c++编程这本书出过这么两道题。