免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
1234下一页
最近访问板块 发新帖
查看: 19888 | 回复: 33

[请教] 关于使用ifndef避免宏重复定义的讨论 [复制链接]

论坛徽章:
0
发表于 2010-06-20 04:17 |显示全部楼层
10可用积分
通过 #ifndef来避免宏的重复定义,

case1, 不使用  #ifndef , 而重复定义宏, 在编译的时候并没有什么问题,而且这样也不会对结果有什么影响吧?

   #define MAC

   ..................

   #define MAC


case 2, 不使用 #ifndef, 重复定义宏, 后面覆盖前面, 这样保证了该程序的正确性,但是影响到后面用到宏的地方
,如果加了ifndef MAC, 那么保证了其他调用宏的正确性,但是本程序确使用了错误的宏, (通过#else prinf ("xxxxx"来提示错误?)

  # define MAC  1

...........

  # define MAC  2




所以,想请教,加 #ifndef   ....   的作用到底是为了什么呢? 还是我在对其理解上有问题?

还请大家指教

最佳答案

查看完整内容

1. 多次包含的情况include xxx 就是将xxx的内容原地展开假设有:a.h, 内容是Ab.h, 内容是:#include "a.h"Bc.h, 内容是:#include "a.h"C如果有一个文件x.c, 内容是:#include "b.h"#include "c.h"Xb.h和c.h的内容就会被插入到X之前, 也就是这个样子:ABACXA的内容就出现了2次。在更复杂的环境中, A的内容还可能出现多次。2. 多次出现是有问题的一般来说, 重复声明没什么问题。所以, 如果A.h中止包含一些声明, 那重复了也 ...

论坛徽章:
2
青铜圣斗士
日期:2015-11-26 06:15:59数据库技术版块每日发帖之星
日期:2016-07-24 06:20:00
发表于 2010-06-20 04:17 |显示全部楼层
本帖最后由 OwnWaterloo 于 2010-06-27 21:35 编辑

1. 多次包含的情况

include xxx 就是将xxx的内容原地展开

假设有:
a.h, 内容是A

b.h, 内容是:
#include "a.h"
B

c.h, 内容是:
#include "a.h"
C

如果有一个文件x.c, 内容是:
#include "b.h"
#include "c.h"
X

b.h和c.h的内容就会被插入到X之前, 也就是这个样子:
A
B
A
C
X

A的内容就出现了2次。

在更复杂的环境中, A的内容还可能出现多次。


2. 多次出现是有问题的

一般来说, 重复声明没什么问题。
所以, 如果A.h中止包含一些声明, 那重复了也没什么关系。

比如:

  1. int f(int);
  2. int f(int);
  3. int f(int);
  4. extern int i;
  5. extern int i;
  6. extern int i;
  7. struct x;
  8. struct x;
  9. struct x;
复制代码
重复写N次也没关系。

但头文件中会出现一类"定义", 在同一翻译单元中是不能重复的。
比如:

  1. struct x { ... };
  2. struct x { ... }; // 重复定义

  3. #define M ...
  4. #define M ...  // 重复定义
复制代码
3. 头文件保护符

有时候必须将这些定义放在头文件中, 所以就要用头文件保护符。
另外还有一类"定义", 会产生外部符号。
这类"定义"在一个链接过程中只能有唯一一份。
是不可以加入到头文件中的。
这种定义依然有例外……  就是inline、模板和匿名名字空间, 就不扯远了……


假设A的内容是:

  1. #ifndef A_H
  2. #define A_H
  3. AA
  4. #endif
复制代码
如果A被展开多次,例如上面的X, 就会变成这个样子

  1. // A_H是a.h的保护符, 必须是一个不冲突的名字。 那么,这里就不会有A_H的定义
  2. // 然后紧接这下一行中的条件编译就会选中#ifndef 和#endif之间的部分, 也就是#define A_H 和AA
  3. #ifndef A_H
  4. #define A_H
  5. AA
  6. #endif


  7. B

  8. // 在a.h被第一次包含后, A_H就获得定义
  9. // 所以下一行的条件编译部分就被取消, AA就不会重复出现多次
  10. #ifndef A_H
  11. #define A_H
  12. AA
  13. #endif

  14. C

  15. X
复制代码
最终交给编译器看到的代码就是:

  1. AA
  2. B
  3. C
  4. X
复制代码
只要A_H是唯一的, AA就不会重复出现。

就解决了这个问题, 一般情况就是这么用的, 是为惯例

4. 外部头文件保护符

上面的用法是"内部头文件保护符"。 a.h的保护符是使用在a.h里。
另外一种用法是"外部头文件保护符", 如:

------ a.h ------
AA

------ b.h ------
#ifndef A_H
#define A_H
#include "a.h"
#endif
B

------ c.h ------
#ifndef A_H
#define A_H
#include "a.h"
#endif
C

当X同时包含b.h和c.h时, 最终效果和内部头文件保护符差不多。

两者对比, 外部的优势是可以减少打开a.h的次数。
而内部保护符可以降低a.h和b.h,c.h之间的耦合。


5. 定义保护符

马上就要到主题了……

将头文件保护符的用法扩展一下, 就变成了定义保护符(这个名字是我捏造的)。
保护的不是某个"头文件" 而是某个"定义", 如:

------ a.h ------

#ifndef A_X
#define A_X
struct x { ... };
#endif

#ifndef A_M
#define A_M
#define M ...
#endif

...


b.h和c.h直接包含a.h, 最终效果也是一样。


6. 重复的定义保护符

到主题了……
同样是一个捏造的词。

假设:
b.h包含a.h是为了获得struct x的定义。
而c.h包含a.h是为了获得宏M的定义。

除了上面作法, 还有另一种做法:

a.h和上面差不多

  1. #ifndef X
  2. #define X
  3. struct x { ... };
  4. #endif

  5. #ifndef M
  6. #define M ...
  7. #endif
复制代码
而b.h和c.h并不包含a.h, 而是直接将需要的定义写在b.h和c.h中
------ b.h ------

  1. #ifndef X
  2. #define X
  3. struct x { ... };
  4. #endif

  5. B
复制代码
------ c.h ------

  1. #ifndef M
  2. #define M ...
  3. #endif

  4. C
复制代码
这样做其实耦合比外部头文件保护符还要高, 所以一般是不会采用的。

C的标准头文件必须这样做
因为C89有一个要求, 具体我不记得了。
要么是要求标准头文件不能包含其他标准头文件。
要么是要求标准头文件不能包含任何其他文件。
(C++和C99取消了这个要求)


stdio.h是C89的标准头文件。
例如, 它需要定义一个size_t, 作为一些函数的参数类型。
而另外有一些标准头文件也会有size_t。
所以这些头文件中的size_t都是这样提供的:

  1. #ifndef _SIZE_T_DEFINED
  2. #define _SIZE_T_DEFINED
  3. typedef unsigned xxx size_t;
  4. #endif
复制代码
或者也可能将若干定义分组, 共用一个保护符。



不知道lz遇到的是不是这个情况?


---------- 补充一下, 关于C89的standalone头文件

关于这个, 我记得是在一本什么书上看到过, 而且肯定不是老谭写的那种书。
随便翻了翻C89 , 在可能出现的地方没有找到相应条款。
我那本C89是扫描版的……  不太好查……

总之, 在查到引用资料前, 关于这个问题姑且先当我在胡说八道吧

论坛徽章:
5
狮子座
日期:2013-08-20 10:12:24午马
日期:2013-11-23 18:04:102015年辞旧岁徽章
日期:2015-03-03 16:54:152015亚冠之德黑兰石油
日期:2015-06-29 18:11:1115-16赛季CBA联赛之新疆
日期:2024-02-21 10:00:53
发表于 2010-06-20 05:07 |显示全部楼层
如果喜欢后面的定义的优先级高,就不写ifdef

如果喜欢前面的定义的优先级高,就写ifdef

论坛徽章:
0
发表于 2010-06-20 10:08 |显示全部楼层
用#ifndef可以避免.h文件被多次引用,可以通过判断一些预编译宏存在与否来判断编译器类型以及操作系统。比如:
//如果不是微软MS VC编译器
#ifndef _MSC_VER
#error "this project require VC++ compiler"
#endif

//如果不是Windows操作系统
#ifndef _WIN32
#error "this project support only Windows "
#endif

如果不是GCC编译器
#ifndef __GNUC__
#error "this project require GCC compiler"
#endif

当然你也可以不用#ifndef,而改用#if !defined()这样的新语法。

论坛徽章:
323
射手座
日期:2013-08-23 12:04:38射手座
日期:2013-08-23 16:18:12未羊
日期:2013-08-30 14:33:15水瓶座
日期:2013-09-02 16:44:31摩羯座
日期:2013-09-25 09:33:52双子座
日期:2013-09-26 12:21:10金牛座
日期:2013-10-14 09:08:49申猴
日期:2013-10-16 13:09:43子鼠
日期:2013-10-17 23:23:19射手座
日期:2013-10-18 13:00:27金牛座
日期:2013-10-18 15:47:57午马
日期:2013-10-18 21:43:38
发表于 2010-06-20 10:40 |显示全部楼层
#ifndef有其他作用呀,如

#ifndef  AA

#define BB之类的  

#endif

论坛徽章:
0
发表于 2010-06-20 12:32 |显示全部楼层
没什么好不好,存在即合理,个中滋味,你自己品吧

论坛徽章:
0
发表于 2010-06-20 13:40 |显示全部楼层
1.ifndef用于防止头文件的重复定义
  #ifndef xxx
   #define xxx
   #endif

2.用于控制程序流程 or 变量值
  至少我一般有
  #ifdef  DEBUG
   #define  m_debug(xxxx)  fprintf(xxx)
   #else
   #define  m_debug(xxxx)
  #endif

  用于调试和发布 这样只需make的时候定义DEBUG or not

论坛徽章:
0
发表于 2010-06-20 13:49 |显示全部楼层
这个东西用了才能体会到他的用处

论坛徽章:
0
发表于 2010-06-20 16:30 |显示全部楼层
刚才试了下,重复引用头文件好像也不会造成什么影响吧?

论坛徽章:
0
发表于 2010-06-20 16:36 |显示全部楼层
回复 1# nine8


    这样追问下去没有意义的,会陷入了“纯语法”的怪圈。
    总要结合如何编程才有意义
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP