(六)学习笔记 c语言基础 宏定义和条件编译

1.什么是宏定义?

引用自百度百科

宏(Macro),是一种批量处理的称谓。计算机科学里的宏是一种抽象(Abstraction),它根据一系列预定义的规则替换一定的文本模式。解释器或编译器在遇到宏时会自动进行这一模式替换。对于编译语言,宏展开在编译时发生,进行宏展开的工具常被称为宏展开器。宏这一术语也常常被用于许多类似的环境中,它们是源自宏展开的概念,这包括键盘宏和宏语言。绝大多数情况下,“宏”这个词的使用暗示着将小命令或动作转化为一系列指令。

宏定义:是指用一个宏名(名字)来代表一个字符串。宏定义的功能是在编译预处理时,对程序中所有出现的“宏名”都用宏定义中的字符串去代换,这称为“宏代换”或“宏展开”。
2.宏应用
1).宏定义常量

#define PI 3.1415926
#define PATH "D:\project\src"

2).宏定义表达式

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

注意:宏定义仅是做简单的文本替换,如果是表达式需要用括号括起来,否则可能会出现逻辑上的“错误”

如下会得到意想不到的结果:

#include<stdio.h>
#define SUM(x, y) x + y //正确写法是:#define SUM(x, y) ((x) + (y))

int main()
{
    int a = 1, b = 2;
    int sub = SUM(a, b) * SUM(a, b);
    printf("sub = %d\n", sub);
    return 0;
}
宏定义换行表达式(交换两个整数值)
#define SWAP(x, y) \
{                  \
    int temp = x;  \
    x = y;         \
    y = temp;      \
}
注意:宏定义行连接符是’\’
3.宏定义运用的好处与缺点
宏定义的好处:

1).提高代码的可读性,方便进行修改;
2).宏定义是简单的文本替换,使用带参的宏定义完成函数调用,可以提高程序的运行效率,减少系统开销。
分析:宏是在预处理阶段进行宏替换的,执行时不需要转换,占用编译时间。并且宏定义不进行内存分配,由变量定义分配内存。(宏定义不存在类型问题,参数也是无类型的)
函数调用是在编译后,程序运行时进行的,需要分配内存,值传递,返回值等一系列操作,占用运行时间。
3).宏是由预处理器处理的,通过字符串操作可以完成很多编译器无法实现的功能。比如##连接符。

宏定义缺点:

1).对带参的宏而言,由于是直接替换,并不会检查参数是否合法,存在安全隐患。
2).不方便调试问题。

4.什么是条件编译?

引用自百度百科

—般情况下,C语言源程序中的每一行代码.都要参加编译。但有时候出于对程序代码优化的考虑.希望只对其中一部分内容进行编译.此时就需要在程序中加上条件,让编译器只对满足条件的代码进行编译,将不满足条件的代码舍弃,这就是条件编译(conditional compile)。
5.条件编译应用

三种语法格式:

1).if格式
#if 表达式
     条件语句1
#else
     条件语句2
#endif

当表达式的值为真时,编译条件语句1,否则编译条件语句2。分支#else和条件语句2可以没有。

2).ifdef格式
#ifdef 标识符
     条件语句1
#else
     条件语句2
#endif

当标识符已被定义时(用#define定义),编译条件语句1,否则编译条件语句2。分支#else和条件语句2可以没有。

3).ifndef格式
#ifndef 标识符
     条件语句1
#else
     条件语句2
#endif

和ifdef格式相反(if not def)

6.#if(表达式)、#if defined(标识符) 和 #ifdef (标识符) 的区别
1).#if(表达式)后面跟的是表达式,条件为真时,把条件语句编译进去。
2).#if defined(标识符)后面跟的是宏定义,不管宏定义的标识符是真还是假,只要存在就把条件语句编译进去。
3).#ifdef(标识符)和第二个用法一样,但是只能用于单个标识符,作为判断依据。
注意:如果是多个宏定义标识符作为判断依据,只能用defined()。

如:

#if (deined(A) || defined(B))
    条件语句1
#endif
7.综合引用
1).防止一个头文件被重复包含
#ifndef _HELLO_H
#define _HELLO_H 

#include<a.h>
#include<b.h>

int main();
#endif

当头文件第一次被包含时,正常处理,符号_HELLO_H被定义为1。如果头文件被再次包含,通过条件编译,它的内容被忽略。符号_HELLO_H按照被包含头文件的文件名进行取名,以避免由于其他头文件使用相同的符号而引起的冲突。

注意:预处理器仍将整个头文件读入,即使这个头文件所有内容将被忽略。这种处理将托慢编译速度,如果可能,应该避免出现多重包含。

#ifndef的是方式是受C/C++语言标准支持。#ifndef方式依赖于宏名称要唯一。
优点:可以保证同一个文件不会被包含多次,也能保证内容完全相同的两个文件不会被同时包含。
缺点:如果不同头文件中的宏名不小心”碰撞”,可能就会导致你看到头文件明明存在,编译器却硬说找不到声明的状况。由于编译器每次都需要打开头文件才能判定是否有重复定义,因此在编译大型项目时,#ifndef会使得编译时间相对较长。

另外一种处理方式#pragma once
#pragma once
#include<a.h>
#include<b.h>

int main();

#pragma once由编译器提供保证:
同一个文件名不会被包含多次。无法对一个头文件中的一段代码作#pragma once声明,只能针对文件。
优点:不会出现宏名一样引发的奇怪问题,大型项目的编译速度也因此得以提高。
缺点:如果某个头文件有多份拷贝,此方法不能保证它们不被重复包含。在C/C++中,#pragma once是一个非标准但是被广泛支持的方式。

注意:兼容性不好,较老编译器不支持这种方式,如GCC 3.4版本之前不支持#pragma once
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值