一、#ifndef#define#endif

注意问题:变量一般不要定义在.h文件中。

总结:

二、#define宏定义

三、namespace命名空间


一、#ifndef#define#endif

首先说明下几点基础知识:

1.预编译阶段把所有#include ”***.h“ (“”与<>的区别这里就不说了)用***.h的内容来替换了, 所以之后就没有.h了所有.h的内容都已经包含进了需要它们的.cpp中。

2.生成最后的exe文件是由编译、链接两步完成的, 编译是源代码生成obj二进制目标文件的过程, 注意一个源代码文件(指.cpp, 而非.h, .h已经被包含进.cpp中了)生成一个obj文件。
详情可参见:https://blog.csdn.net/qq_30815237/article/details/88948632

从语法上来分析下#ifndef#define#endif

头文件“a.h”如下:

//-----a.h-----
#ifndef A_H_
#define A_H_
void hello();
#endif

在预编译阶段, 当第一次执行该段代码(即#include "a.h",参见第一条)时, 由于我们并没有宏定义A_H_, 所以会执行#define A_H_以及void fun()两条语句, 第二次执行该段代码时因为#ifndef A_H_为假就直接走到#endif后面也就等于该次#include "a.h"什么也没做了     因此头文件的中的#ifndef,这是一个很关键的东西。比如你有两个C文件,这两个C文件都include了同一个头文件a.h。而编译时,这两个C文件要一同编译成一个可运行文件,于是问题来了,出现大量的声明冲突。 
      所以把头文件的内容都放在#ifndef和#endif中。不管你的头文件会不会被多个文件引用,你都要加上这个。一般格式是这样的:

#ifndef <标识>
#define <标识> ......
...... #endif 

<标识>在理论上来说可以是自由命名的,但每个头文件的这个“标识”都应该是唯一的。标识的命名规则一般是头文件名全大写,前后加下划线,并把文件名中的“.”也变成下划线,如:a.h

#ifndef A_H_
#define A_H_
...... #endif 

注意问题:变量一般不要定义在.h文件中。

总结:

当我们一个简单的project中有三个文件main.cpp, a.cpp, a.h,而 main.cpp 和a.cpp分别包含了a.h, 在预编译阶段, 两个编译单元是都会分别包含a.h的, 即使他们使用了#ifndef#define#endif, 这也是为什么当a.h被多个文件包含时我们不允许在a.h中定义变量及函数的原因, 因为在链接阶段会出现重定义。 但是在a.h中定义一个static变量却是允许的, 因为static变量是模块性作用域, 就这个例子来说, 若我们在a.h中写static int sss = 0;那么main.cpp与a.cpp使用的sss将为2个独立的sss.

那么是否#ifndef#define#endif就没用了呢, 大家可以想想, 当我们a.cpp中写了多个#include "a.h"时, 如果我们使用了#ifndef#define#endif,那么预编译阶段就只会包含一个a.h中的内容到a.cpp中, 你也许会说, 有谁会傻到在a.cpp中写多个#include "a.h"呢, 那么请考虑稍微复杂点的情况, 当我们main.cpp中包含了a.h和b.h, 而a.h中我们又包含了b.h, 那么如果我们使用了#ifndef#define#endif则main.obj只会包含一份b.h。

from:https://blog.csdn.net/q191201771/article/details/6399820

from:https://blog.csdn.net/mad1989/article/details/7920173?utm_source=blogxgwz9


二、#define宏定义

为了程序的通用性,可以使用#define预处理宏定义命令,它的具体作用,就是方便程序段的定义和修改。举个例子,如果一个项目的代码很长,有5000多行,而且基本上裏面的同一个常量N就占了3000多行,如果说今后你要是想对这个常量N的值进行修改,如果在程序的开始没有定义一个#define N,那么改起来不是很麻烦,那不是要晕过去了,如果你事先定义了一个      #  define N ‘常量值’,那么当你要修改程序内部的常量值时,只要修改N后面的常量值就等于把程序内部的要修改的相同常量值都改了。

define将一个变量强制定义为你想要的值。如#define N 3.14就是将PI的值定义成3.14,这样在程序里看到N就可以替换成3.14

简单的define定义

#define MAXTIME 1000

一个简单的MAXTIME就定义好了,它代表1000,如果在程序里面写:

if(i<MAXTIME){.........}

编译器在处理这个代码之前会对MAXTIME进行处理替换为1000。

这样的定义看起来类似于普通的常量定义CONST,但也有着不同,因为define的定义更像是简单的文本替换,而不是作为一个量来使用,这个问题在下面反映的尤为突出:

宏定义的缺点: 

(1) 无法对宏定义中的变量进行类型检查 

此缺点,是相对于const变量来说的:

二者的区别:
(1) #define是在编译的预处理阶段起作用,而const是在编译、运行的时候起作用。
(2)方式: #define只是简单的字符串替换,没有类型检查。而const有对应的数据类型,是要进行判断的,可以避免一些低级的错误。 
(3)存储:#define只是进行展开,有多少地方使用,就替换多少次,它定义的宏常量在内存中有若干个备份???;const定义的只读变量在程序运行过程中只有一份备份。
(4)调试: const常量可以进行调试的,define是不能进行调试的,因为在预编译阶段就已经替换掉了。

const优点:
(1)const常量有数据类型,而宏常量没有数据类型。编译器可以对前者进行类型安全检查。而对后者只进行字符替换,没有类型安全检查,并且在字符替换可能会产生意料不到的错误。
(2)有些集成化的调试工具可以对const常量进行调试,但是不能对宏常量进行调试。
(3)const可节省空间,避免不必要的内存分配,提高效率

define仅仅是编译时期替换,所以其定义的变量,是不会在运行时候分配内存的,不占用内存空间. const定义的变量,是 Run-Time时期的变量,如果类型不匹配,系统在运行时候,就会发现并提示或报错,对应的,const变量在运行时期,也是一种变量,系统会为其分配内存. ???

#define PI 3.14     //预处理后 占用代码段空间
const float PI=3.14;    //本质上还是一个 float,占用数据段空间

(2) 边界效应 

A. 未加括号带来的边界效应 
由于宏定义的时候,其各个分量未加括号,而在使用宏定义的时候,传递的参数是变量的表达式,然后经过系统展开后,由于优先级的原因,导致其结果不是你所希望的. 例如:

#define MUL(A,B) A*B

而在使用的时候,这样的调用: 
int a=1,b=2,c=3,d=0; 
d=MUL(a+b,c) 
经过编译时候展开,就变成了 
d=a+b*c 
而不是我们所希望的 
d=(a+b)*c 
[解决办法] 
其解决办法也很简单,就是给每个分量,都加上括号,就可以避免此类问题 
即,在宏定义的时候,如此定义:

#define MUL(A,B) ((A)*(B))

from:https://blog.csdn.net/xianqianshi3004/article/details/79278170

from:https://blog.csdn.net/yi_ming_he/article/details/70405364


三、namespace命名空间

namespace是C++新加的一个概念,中文名为命名空间,相对来说更便捷地解决了函数重名和变量命名的问题。其一般定义如下形式:

namespace 命名空间名{变量声明或函数声明}。

一般使用命名空间中的变量或函数则通过"命名空间名::变量或函数"来使用,如下面代码所示。可能有人会问命名空间存在的意义在哪,自己写的程序完全可以规避命名相同的情况,不过假如你调用其他人所写的程序则有很大的可能有变量重名以及函数重名的问题,然而命名空间的撞车概率则相对较小,显然。

再看下面代码中的程序第二行为"using namespace std;",这个using又是做什么的。我们知道#include<iostream>是引入iostream文件中的代码,其中定义了std命名空间,而cout,endl则声明在std命名空间内,假如不使用using关键字,cout和endl则需要写成std::cout以及std::endl,而cout以及endl都是我们需要经常使用的,这就增加了大量的代码量,通过using则可以在主函数中直接调用命名空间中的变量和函数。当命名空间之间变量命名有冲突也能使用using关键字,但是在使用变量的时候仍需在前面加上命名空间以示区别,如代码最后两行。

#include <iostream>
using namespace std;namespace A
{int x=0;void function(){};
}namespace B
{int x=1;void function(){};
}
using namespace A;
using namespace B;
int main(){cout << A::x << endl;cout << B::x << endl;return 0;
}

from:https://www.cnblogs.com/lun14061076/p/6514172.html

#ifndef#define与namespace杂谈相关推荐

  1. #ifndef.#define, #endif 的用法

    文件中的#ifndef.#define, #endif 很关键,是为了避免多重包含,比如如果两个C文件同时包含同一头文件,那么就会出现问题,所以使用这种方法可以有效避免这种情况. 一般用法: 以add ...

  2. ifndef/define/endif——主要目的是防止头文件的重复包含和编译

    原文:http://www.jb51.net/article/100939.htm 原文:https://blog.csdn.net/abc5382334/article/details/180527 ...

  3. c:#ifndef, #define, #endif 作用

    #ifndef, #define, #endif 作用 #ifndef 它是if not define 的简写,是宏定义的一种,实际上确切的说,这应该是预处理功能三种(宏定义.文件包含.条件编译)中的 ...

  4. c/c++头文件中#ifndef/#define/#endif的用法

    想必很多人都看过"头文件中用到的 #ifndef/#define/#endif 来防止该头文件被重复引用".但是是否能理解"被重复引用"是什么意思?头文件被重复 ...

  5. [Windows编程] #pragma once 和#ifndef ... #define ... #endif 比较

    C++中防止头文件被多次include 的常见方式有: 1) 用#ifndef ...  #define ... #endif  宏 #ifndef __MYHEADER_H__ #define __ ...

  6. C语言头文件为什么要加#ifndef #define #endif(防止头文件重复包含)

    当你用VC的菜单新增一个类,你会发现自动生成的代码总是类似下面的样子: #if !defined(AFX_XXXX__INCLUDED_)#define AFX_XXXX__INCLUDED_具体代码 ...

  7. C++中#ifndef/#define/#endif使用详解

    想必很多人都看过"头文件中的 #ifndef/#define/#endif 防止该头文件被重复引用".但是是否能理解"被重复引用"是什么意思?是不能在不同的两个 ...

  8. #ifndef/#define/#endif

    #ifndef/#define/#endif 头文件中的 #ifndef/#define/#endif 防止该头文件被重复引用 "被重复引用"是指一个头文件在同一个cpp文件中被i ...

  9. 【转载】#pragma once与#ifndef #define #endif的区别

    一.相同点         两者的共同点都是为了避免同一个文件被 include 多次,但是 #ifndef #define #endif 不只有这个作用.  在能够支持这两种方式的编译器上,二者并没 ...

最新文章

  1. 《创业维艰分享之五》所有得,有所乐,日事日清,循序渐进。
  2. python运算符(算数运算符、赋值运算符、复合赋值运算符、比较运算符、逻辑运算符)
  3. 成都郫县php开发学校_成都各区九年制学校、十二年制学校汇总
  4. 联科集团携手阿里云发布科研混合云平台 共建科研教育新生态
  5. 【SQL】使用调用层接口
  6. js技巧--转义符\的妙用
  7. 【转载】C/C++中long long与__int64的区别
  8. java 百度账号注册界面_基于百度AI开放平台的人脸识别的注册登录(1)
  9. 3.Lucene3.x API分析,Director 索引操作目录,Document,分词器
  10. DataFrame/Series获取列名以及更改列名(转)
  11. Java版大顶堆的实现
  12. bg感_【0328】BG推文 | 5本我在逃生游戏里养娃娃+岁月缱绻已无你+关于我比女主苏这回事+消失的白月光又回来了等...
  13. CocoaPods pod install
  14. Bootstrap 滚动监听插件Scrollspy 的事件
  15. iOS 3DTouch
  16. 请问诸位大神,Android怎么实现图片转动
  17. #绘制圆心_AutoCAD绘制齿轮教程
  18. 我的世界服务器显示unknown,我的世界找不到家怎么办-​我的世界unknown
  19. libcrypto.so.1.0.0编译
  20. Linux 2.6.19.x 内核编译配置选项简介

热门文章

  1. oracle ora32771,Oracle的文件号、相对文件号及其他(续)
  2. 软考网络工程师学习笔记1-计算机网络概念
  3. 2019年信息安全工程师备考技巧
  4. Android 8款开源游戏引擎
  5. id vue2路由传参_Vue2.0中 $route 和 $router 的区别
  6. 初识云计算:历史、服务、架构
  7. Javascript组成--ECMAScript,DOM,BOM
  8. JavaScript 'Pig latin is cool'=='igPay atinlay siay oolcay'
  9. Social Network Analysis的Centrality总结,以及networkx实现EigenCentrality,PageRank和KatzCentrality的对比...
  10. 调用Interop.zkemkeeper.dll无法使用解决方案