C语言:预处理

1.预处理

1.1#define定义标识符

举个例子:

#define MAX 1000         //将代码中的MAX直接替换成1000
#define reg register     //为 register这个关键字,创建一个简短的名字

我们在#define定义标识符的时候需要在后面加上:(分号)吗?如果我们不知道该不该加的时候,不妨来尝试一下加了会有什么后果,看下面的例子:

#define MAX 100;

int main() {
	printf("%d\n", MAX);
	//相当于 printf("%d\n",100;);
	return 0;
}

这段代码根本无法编译,不符合语法规则。这种类似的例子还有很多,所以我们在#define定义标识符时不要带上;(分号)。

1.2#define定义宏

举个例子:

#define SQUARE(x)  x*x

这种定义会存在一个问题,

printf("%d\n",SQUARE(1+4));

这个宏的功能是用于求平方,这句代码的本意是想求5的平方,但最后的输出结果却不是这样的,结果是9,这是因为#define的规则是进行替换,上面的代码会直接替换为:

printf("%d\n",1+4*1+4);

如果我们想求的是5的平方,我们需要在定义时加上括号以避免运算符的优先级带来的副作用。正确的定义如下:

#define SQUARE(x)  ((x)*(x))
1.3#define的替换规则

在程序中扩展#define定义符号和宏时,需要涉及几个步骤:

  1. 在调用宏时,首先对参数进行检查,看看是否包含任何由#define定义的符号。如果是,它们首先 被替换。
  2. 替换文本随后被插入到程序中原来文本的位置。对于宏,参数名被他们的值替换。
  3. 最后,再次对结果文件进行扫描,看看它是否包含任何由#define定义的符号。如果是,就重复上 述处理过程。

我们来看一个例子:

#define ptr1 int*
typedef int* ptr2;

int main() {
	ptr1 p1, p2;
	ptr2 p3, p4;
	return 0;
}

在这段代码中,p1、p3、p4都是int*类型,p2是int类型,因为#define是单纯的替换,而typedef就相当于是一个重命名。

1.4宏和函数对比
属性#define定义宏函数
代 码 长 度每次使用时,宏代码都会被插入到程序中。除了非 常小的宏之外,程序的长度会大幅度增长函数代码只出现于一个地方;每 次使用这个函数时,都调用那个 地方的同一份代码
执 行 速 度更快存在函数的调用和返回的额外开 销,所以相对慢一些
操 作 符 优 先 级宏参数的求值是在所有周围表达式的上下文环境 里,除非加上括号,否则邻近操作符的优先级可能 会产生不可预料的后果,所以建议宏在书写的时候 多些括号。函数参数只在函数调用的时候求 值一次,它的结果值传递给函 数。表达式的求值结果更容易预 测。
带 有 副 作 用 的 参 数参数可能被替换到宏体中的多个位置,所以带有副 作用的参数求值可能会产生不可预料的结果。函数参数只在传参的时候求值一 次,结果更容易控制。
参 数 类 型宏的参数与类型无关,只要对参数的操作是合法 的,它就可以使用于任何参数类型。函数的参数是与类型有关的,如 果参数的类型不同,就需要不同 的函数,即使他们执行的任务是 不同的。
调 试宏是不方便调试的函数是可以逐语句调试的
递 归宏是不能递归的函数是可以递归的
1.5条件编译

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

常见的条件编译指令:

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
1.6文件包含

我们知道两种文件包含的格式,#include<add.h>,#include"add.h",这两种包含的区别在于,前者直接去库中找对应的文件,而后者会先在工程中找相应的文件,找不到的时候再去库中找,所以我们一般引用头文件的规则是:若是我们自己写的头文件,则用“ ”来引用,若是需要库中的头文件,则用< >来引用。

很多时候,难免会出现头文件的重复包含,当重复包含时,我们有两种解决方法:

每个文件开头写:

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

或者:

#pragma once

这样就可以避免重复包含头文件的问题了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值