预处理详解

1.预定义符号

_ _FILE_ _
_ _LINE_ _
_ _DATE_ _
_ _TIME_ _
_ _STDC_ _
printf("file:%s\n,line:%d\n",_ _FILE_ __ _LINE_ _)

2.#define定义常量

#define name stuff
#define MAX 1000
#define reg register
#define do_forever for(;;)
#define CASE break;case
//如果定义的stuff太长,可以分几行写,出最后一行除外,每行后面都加上\
#define DEBUG_PRINT printf("file :%s\tline:%d\t\
                            date:%s\ttime:%s\n",\ 
                            _ _FILE_ _,_ _LINE_ _,   \
                            _ _DATE_ _,_ _TIME_ _)

在define定义标识符的时候,要不要加上;

不能,加上会出现语法错误

3.#define定义宏

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

#define name(parament_-list) stuff

  妻子的parament-list是一个逗号隔开的符号表,他们可能出现在stuff中

参数列表的左括号必须与name紧邻,如果俩者之间有空白存在,参数列表就会被解释为stuff的一部分。

#define SQUARE(x)(x)*(x)

x必须加上括号要不然可能会生成不了想要的值

这个宏接受一个参数x,在上述声明后,把SQUARE(5);置于程序中,预处理就会用下面的表达式替换上面的表达式:5*5

这个宏不加上括号也会出现上述类似的错误

#define DOUBLE(x)((x)+(x)) 

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

4.叠用副作用的宏参数

当宏参数在宏的定义中出现超过一次的时候,如果参数带有副作用,那么你在使用这个宏的时候就可能出现危险,导致不可预测的后果,副作用就是表达式求值时会出现永久性效果

x+1;
x++;//带有副作用
#define MAX(a,b) ((a)>(b)?(a):(b))
int main()
{
    int z = 3;
    int y = 5;
    int m = MAX(z++, y++);
    printf("%d %d %d", m, z, y);
    return 0;
}

输出结果是 6,4,7;

5.宏替换的规则

1.在调用宏时,首先对参数进行检查,看看是否包含任何由#define定义的符号,如果是,首先被替换。

2.替换为文本随后被插入到程序原本的位置,对于宏,参数名被他们的值所替换

3.最后,再次对结果文件进行扫描,看看它是否包含任何由#define定义的符号,如果是就重复操作

注意:

宏参数和#define定义中可以出现其他#define定义的符号,但是对于宏,不能出现递归。

当预处理器搜素#define定义的符号时,字符串常量的内容并不被搜索

6.宏函数的对比

宏被应用于执行更简单的运算

比如在俩个数中找出较大的,写成宏更有优势

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

为什么不要函数

原因1:调用函数和从函数返回的代码可能比实际执行这个小型计算所需要的时间更多,

所以宏鼻鼾声在程序的规模和速度更胜一筹

  2.宏的参与类型无关

宏的劣势与函数比

1.每次使用宏都会加大程序的长度

2.宏无法调试

3.宏由于类型无辜啊,就不够严谨

4.宏可能会带来运算符优先级的问题,会导致出现错误

与函数相比,宏的参数可以出现类型

#define MALLOC(num,type) (type) malloc(num,type);

宏与函数的对比

7.#和##

7.1#运算符

#运算符是将宏的一个参数转换为字符串字面量。它仅允许出现在带参数的宏的替换列表中

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

当我们有一个变量int a=10 ,我们想打印出: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);

7.2##运算符

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

这样的连接必须产生一个合法的标识符,否则结果就是未定义的

#define GENERIC_MAX(type)
type type##_max(type x,type y)
{
return(x>y?x:y);
}

在实际开发过程中##使用很少。

8.命名约定

习惯是 宏名全部大写

函数名不要全部大写

9.#undef

用于移除一个宏定义

10.命令行定义

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

gcc -D   NAME=number programe.c

11.条件编译

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

比如:

#define _ _DEBUG_ _ 
int main()
{
    int i = 0;
    int arr[] = { 0 };
    for (i = 0; i < 10; i++)
    {
        arr[i] = i + 1;
    }
#ifdef _ _DEBUG_ _
    for (i = 0; i < 10; i++)
    {
        printf("%d ", arr[i]);
   }
    #endif
    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

12.头文件包含

12.1包含方式

12.1.1本地文件包含

#include"filename";

查找策略:先在源文件所在的目录下查找,如果没找到,就在标准位置查找

Linux环境的标准头文件的路径

/usr/include

VS的标准头文件的路径

C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\include

12.1.2库文件包含

#include<filename>

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

12.2嵌套文件包含

如何解决头文件被重复引用的问题

条件编译

#ifndef __TEST_H__
#define __TEST_H__
//头⽂件的内容
#endif //__TEST_H__
或者
#pragma once

13.其他预处理指令

#error
#prama
#line

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值