CPP {extern声明, 头文件里的extern声明, 头文件的防重复包含的宏定义}

CPP {extern声明, 头文件里的extern声明, 头文件的防重复包含的宏定义}

extern声明

性质

#有参数默认值的函数#
void F(int, string){}, void F(int = 1, string = "A"){}, 这是错误的! (因为对于F(2,"X")此时两个函数都可以调用 就歧义了);
其实有参数默认值的函数, 他不是放在实现里的, 而是放在extern声明里的, 即extern void F(int,string); extern void F(int=1,string="A"); 然后统一写一个实现void F( int a, string b){ ...}; (即不同的有默认值的函数声明 他关心的 只是函数参数的值而已, 跟函数实现是无关的, 即他们都对应同一个函数实现);
. 注意, 此时你F()F(2) 他都会使用的是F(int=1,string="A"), 即F() == F(1,"A"), F(2) == F(2, "A");
. 所以如果你再写一个extern void F( int, string="B"); 这是错误的, 因为对于F(2) 他可以是F(2,"A") 也可以是F(2,"B") 这就歧义了, 即有默认值的函数声明 最多只有1个;

头文件的防重复包含的宏定义

定義

A.h头文件 必须要形如以下格式:
#ifndef A_H_
#define A_H_
...
#endif

extern技术 他是防止链接时的错误(即重定义错误), 而头文件的防重复包含的宏定义 这个技术 他与链接无关 他是防止编译时的错误, 主要是以下几个作用:
0(头文件相互包含的编译错误): 比如A.h里面 有#include<B.h>, 同时 B.h里面 有#include<A.h>, 此时如果A.h, B.h里 都没有使用防重复包含技术, 那么编译器就陷入死循环了(因为无限套娃了… 知道运行时死循环 还有编译时死循环 hh);
. 注意这个语法是合法的 并不是我们凭空故意瞎想出来的, 两个头文件互相包含 并不是一个语法错误, 因为只要你使用防重复包含技术 即使你让两个头文件互相包含 这并没有错; (但最好别这么弄 说明你的代码逻辑有问题 可能会存在潜在的逻辑错误) 问题在于: 你头文件里的代码逻辑是什么, 你要保证 即使两个头文件相互包含了 但是这两个头文件里的代码逻辑 一定是单向的 不可以是(两者相互调用);
. . 比如A.h: using a = b; B.h: using b = a;, 此时即使使用防重复包含技术 还是会编译出错, 因为头文件展开的次序 一定是唯一的, 比如程序最先执行进入A.h, 那么他里面有一个#include <B.h> 因此会去展开B.h (虽然B.h里也包含了A.h 但因为此时已经有#define A_H_了 不会再去展开A.h), 然后执行using b = a; 会发现 全局里并没有这个a符号, 于是报错 (undefined未定义错误);
. . 再举个更复杂的错误, 两个头文件相互包含 也使用防重复包含技术了, A.h: using a = int; B.h: using b = a;, 即使你的头文件里的代码逻辑是单向的, 但你要保证 最终头文件展开的次序头文件里代码逻辑的方向是一致的; 比如编译器先进入A.h 然后由于重复包含 编译器随后进入了B.h 此时遇到了using b = a 同样会产生未定义错误, 因此 你要保证 编译器要最先展开A.h 即有了using a = int;之后 再去展开B.h;
. 总之: 最好不要头文件相互包含, 即使相互包含 只要使用这个宏定义技术 到时候 文件展开的次序 一定是唯一的! 要么是先展开A.h 然后再展开B.h 要么是相反, 这取决于: 编译器最先遇到哪个头文件, 比如main.cpp里面是 #include<A.h> 那么编译器最先遇到A.h (但是他是后展开的) 因为A.h里面有#include<B.h> 因此会最先展开B.h, 总之 在一个.o生成文件里 他俩的展开次序是固定的 虽然相互包含了;
1(代码冗余): 比如A.cpp里面 他会有#include <A.h>, #include <B.h> 然后B.h里面 也有#include<A.h>; 这样会导致 看起来A.cpp里面 会重复多次的 #include<A.h> 也就是比如A.h里面是 extern int a;, 那么A.cpp里面 有多个extern int a;, 这虽然语法是没问题的, 但毕竟不太好 因为肯定效率会损失; 而使用防重复包含技术后 就可以解决这个问题;
. 注意这个语法是合法的 并不是我们凭空故意瞎想出来的, 而且他是常见的 你可以把头文件包含 想象成是一棵树, 那么一个节点 他会#include <所有的父节点> 那么根节点会重复包含 在实际开发中 他是经常会发生的;

性质

头文件的extern, 主要用在 链接期间(即多个.o文件) 发挥作用;
而头文件的防重复包含技术, 主要是在 编译期间(即单个.o文件) 发挥作用;

头文件里的extern声明

定义

頭文件裏的變量/函數 都必須用extern声明 不可以实现(实现必须放在.cpp文件), 否则 会导致重复定义redefinitoin错误;
他主要是在
多个.cpp文件会涉及到链接
时, 会使用这个技术; 主要是防止全局符号的重复定义;
. 比如说G.h里面有个: int D; 然后A.cpp和B.cpp都会#include<G.h>, 此时编译是不会出错的 (因为A.o, B.o没有错误), 但是将A.o, B.o链接到一起时 会出错 因为有两个全局变量int D;

  • 11
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1. C 语言中的指针和内存泄漏 5 2. C语言难点分析整理 10 3. C语言难点 18 4. C/C++实现冒泡排序算法 32 5. C++中指针和引用的区别 35 6. const char*, char const*, char*const的区别 36 7. C中可变参数函数实现 38 8. C程序内存中组成部分 41 9. C编程拾粹 42 10. C语言中实现数组的动态增长 44 11. C语言中的位运算 46 12. 浮点数的存储格式: 50 13. 位域 58 14. C语言函数二维数组传递方法 64 15. C语言复杂表达式的执行步骤 66 16. C语言字符串函数大全 68 17. C语言宏定义技巧 89 18. C语言实现动态数组 100 19. C语言笔试-运算符和表达式 104 20. C语言编程准则之稳定篇 107 21. C语言编程常见问题分析 108 22. C语言编程易犯毛病集合 112 23. C语言缺陷与陷阱(笔记) 119 24. C语言止缓冲区溢出方法 126 25. C语言高效编程秘籍 128 26. C运算符优先级口诀 133 27. do/while(0)的妙用 134 28. exit()和return()的区别 140 29. exit子程序终止函数与return的差别 141 30. extern与static存储空间矛盾 145 31. PC-Lint与C\C++代码质量 147 32. spirntf函数使用大全 158 33. 二叉树的数据结构 167 34. 位运算应用口诀和实例 170 35. 内存对齐与ANSI C中struct内存布局 173 36. 冒泡和选择排序实现 180 37. 函数指针数组与返回数组指针的函数 186 38. 右左法则- 复杂指针解析 189 39. 回车和换行的区别 192 40. 堆和堆栈的区别 194 41. 堆和堆栈的区别 198 42. 如何写出专业的C头文件 202 43. 打造最快的Hash表 207 44. 指针与数组学习笔记 222 45. 数组不是指针 224 46. 标准C中字符串分割的方法 228 47. 汉诺塔源码 231 48. 洗牌算法 234 49. 深入理解C语言指针的奥秘 236 50. 游戏外挂的编写原理 254 51. 程序实例分析-为什么会陷入死循环 258 52. 空指针究竟指向了内存的哪个地方 260 53. 算术表达式的计算 265 54. 结构体对齐的具体含义 269 55. 连连看AI算法 274 56. 连连看寻路算法的思路 283 57. 重新认识:指向函数的指针 288 58. 链表的源码 291 59. 高质量的子程序 295 60. 高级C语言程序员测试必过的十六道最佳题目+答案详解 297 61. C语言常见错误 320 62. 超强的指针学习笔记 325 63. 程序员之路──关于代码风格 343 64. 指针、结构体、联合体的安全规范 346 65. C指针讲解 352 66. 关于指向指针的指针 368 67. C/C++ 误区一:void main() 373 68. C/C++ 误区二:fflush(stdin) 376 69. C/C++ 误区三:强制转换 malloc() 的返回值 380 70. C/C++ 误区四:char c = getchar(); 381 71. C/C++ 误区五:检查 new 的返回值 383 72. C 是 C++ 的子集吗? 384 73. C和C++的区别是什么? 387 74. 无条件循环 388 75. 产生随机数的方法 389 76. 顺序表及其操作 390 77. 单链表的实现及其操作 391 78. 双向链表 395 79. 程序员数据结构笔记 399 80. Hashtable和HashMap的区别 408 81. hash 表学习笔记 410 82. C程序设计常用算法源代码 412 83. C语言有头结点链表的经典实现 419 84. C语言惠通面试题 428 85. C语言常用宏定义 450
不多说了 直接上目录: 1. C 语言中的指针和内存泄漏 5 2. C语言难点分析整理 10 3. C语言难点 18 4. C/C++实现冒泡排序算法 32 5. C++中指针和引用的区别 35 6. const char*, char const*, char*const的区别 36 7. C中可变参数函数实现 38 8. C程序内存中组成部分 41 9. C编程拾粹 42 10. C语言中实现数组的动态增长 44 11. C语言中的位运算 46 12. 浮点数的存储格式: 50 13. 位域 58 14. C语言函数二维数组传递方法 64 15. C语言复杂表达式的执行步骤 66 16. C语言字符串函数大全 68 17. C语言宏定义技巧 89 18. C语言实现动态数组 100 19. C语言笔试-运算符和表达式 104 20. C语言编程准则之稳定篇 107 21. C语言编程常见问题分析 108 22. C语言编程易犯毛病集合 112 23. C语言缺陷与陷阱(笔记) 119 24. C语言止缓冲区溢出方法 126 25. C语言高效编程秘籍 128 26. C运算符优先级口诀 133 27. do/while(0)的妙用 134 28. exit()和return()的区别 140 29. exit子程序终止函数与return的差别 141 30. extern与static存储空间矛盾 145 31. PC-Lint与C\C++代码质量 147 32. spirntf函数使用大全 158 33. 二叉树的数据结构 167 34. 位运算应用口诀和实例 170 35. 内存对齐与ANSI C中struct内存布局 173 36. 冒泡和选择排序实现 180 37. 函数指针数组与返回数组指针的函数 186 38. 右左法则- 复杂指针解析 189 39. 回车和换行的区别 192 40. 堆和堆栈的区别 194 41. 堆和堆栈的区别 198 42. 如何写出专业的C头文件 202 43. 打造最快的Hash表 207 44. 指针与数组学习笔记 222 45. 数组不是指针 224 46. 标准C中字符串分割的方法 228 47. 汉诺塔源码 231 48. 洗牌算法 234 49. 深入理解C语言指针的奥秘 236 50. 游戏外挂的编写原理 254 51. 程序实例分析-为什么会陷入死循环 258 52. 空指针究竟指向了内存的哪个地方 260 53. 算术表达式的计算 265 54. 结构体对齐的具体含义 269 55. 连连看AI算法 274 56. 连连看寻路算法的思路 283 57. 重新认识:指向函数的指针 288 58. 链表的源码 291 59. 高质量的子程序 295 60. 高级C语言程序员测试必过的十六道最佳题目+答案详解 297 61. C语言常见错误 320 62. 超强的指针学习笔记 325 63. 程序员之路──关于代码风格 343 64. 指针、结构体、联合体的安全规范 346 65. C指针讲解 352 66. 关于指向指针的指针 368 67. C/C++ 误区一:void main() 373 68. C/C++ 误区二:fflush(stdin) 376 69. C/C++ 误区三:强制转换 malloc() 的返回值 380 70. C/C++ 误区四:char c = getchar(); 381 71. C/C++ 误区五:检查 new 的返回值 383 72. C 是 C++ 的子集吗? 384 73. C和C++的区别是什么? 387 74. 无条件循环 388 75. 产生随机数的方法 389 76. 顺序表及其操作 390 77. 单链表的实现及其操作 391 78. 双向链表 395 79. 程序员数据结构笔记 399 80. Hashtable和HashMap的区别 408 81. hash 表学习笔记 410 82. C程序设计常用算法源代码 412 83. C语言有头结点链表的经典实现 419 84. C语言惠通面试题 428 85. C语言常用宏定义 450 有需要的朋友可以根据需求下载,内容为WORD格式的,绝对清晰
1. C 语言中的指针和内存泄漏 5 2. C语言难点分析整理 10 3. C语言难点 18 4. C/C++实现冒泡排序算法 32 5. C++中指针和引用的区别 35 6. const char*, char const*, char*const的区别 36 7. C中可变参数函数实现 38 8. C程序内存中组成部分 41 9. C编程拾粹 42 10. C语言中实现数组的动态增长 44 11. C语言中的位运算 46 12. 浮点数的存储格式: 50 13. 位域 58 14. C语言函数二维数组传递方法 64 15. C语言复杂表达式的执行步骤 66 16. C语言字符串函数大全 68 17. C语言宏定义技巧 89 18. C语言实现动态数组 100 19. C语言笔试-运算符和表达式 104 20. C语言编程准则之稳定篇 107 21. C语言编程常见问题分析 108 22. C语言编程易犯毛病集合 112 23. C语言缺陷与陷阱(笔记) 119 24. C语言止缓冲区溢出方法 126 25. C语言高效编程秘籍 128 26. C运算符优先级口诀 133 27. do/while(0)的妙用 134 28. exit()和return()的区别 140 29. exit子程序终止函数与return的差别 141 30. extern与static存储空间矛盾 145 31. PC-Lint与C\C++代码质量 147 32. spirntf函数使用大全 158 33. 二叉树的数据结构 167 34. 位运算应用口诀和实例 170 35. 内存对齐与ANSI C中struct内存布局 173 36. 冒泡和选择排序实现 180 37. 函数指针数组与返回数组指针的函数 186 38. 右左法则- 复杂指针解析 189 39. 回车和换行的区别 192 40. 堆和堆栈的区别 194 41. 堆和堆栈的区别 198 42. 如何写出专业的C头文件 202 43. 打造最快的Hash表 207 44. 指针与数组学习笔记 222 45. 数组不是指针 224 46. 标准C中字符串分割的方法 228 47. 汉诺塔源码 231 48. 洗牌算法 234 49. 深入理解C语言指针的奥秘 236 50. 游戏外挂的编写原理 254 51. 程序实例分析-为什么会陷入死循环 258 52. 空指针究竟指向了内存的哪个地方 260 53. 算术表达式的计算 265 54. 结构体对齐的具体含义 269 55. 连连看AI算法 274 56. 连连看寻路算法的思路 283 57. 重新认识:指向函数的指针 288 58. 链表的源码 291 59. 高质量的子程序 295 60. 高级C语言程序员测试必过的十六道最佳题目+答案详解 297 61. C语言常见错误 320 62. 超强的指针学习笔记 325 63. 程序员之路──关于代码风格 343 64. 指针、结构体、联合体的安全规范 346 65. C指针讲解 352 66. 关于指向指针的指针 368 67. C/C++ 误区一:void main() 373 68. C/C++ 误区二:fflush(stdin) 376 69. C/C++ 误区三:强制转换 malloc() 的返回值 380 70. C/C++ 误区四:char c = getchar(); 381 71. C/C++ 误区五:检查 new 的返回值 383 72. C 是 C++ 的子集吗? 384 73. C和C++的区别是什么? 387 74. 无条件循环 388 75. 产生随机数的方法 389 76. 顺序表及其操作 390 77. 单链表的实现及其操作 391 78. 双向链表 395 79. 程序员数据结构笔记 399 80. Hashtable和HashMap的区别 408 81. hash 表学习笔记 410 82. C程序设计常用算法源代码 412 83. C语言有头结点链表的经典实现 419 84. C语言惠通面试题 428 85. C语言常用宏定义 450

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值