宏一定要用大写
来源:互联网 发布:棉先生衣服怎么样 知乎 编辑:程序博客网 时间:2024/05/22 10:27
前言
被一个宏折腾了2次,才偶然发现问题。问题挺隐蔽的,也许是自己对BUG出现后的表现,还不太敏感。
以前认为宏需要大写,可能就是看着舒服,维护方便。
经过这个bug后,增加了体会。宏大写可以降低调试的难度,提高代码的清晰度。
BUG回放
最近维护的linux工程,为了出win版。代码中不可避免的用宏隔开,或定义了一些在win和linux下都能用的宏。
在工程中使用了, pthreads-w32库。
封装了一个简单的socket类, 对socket基本操作,进行了封装。
当socket任务完成时,需要关闭socket句柄。
linux版没问题。
在win版下,调用closesocket(h_sock), 直接报错了,陷在_close里面。
当时就想不明白,windows下关闭一个socket句柄不就是要调用closesocket么?还能咋样?
因为不关socket句柄,在频繁操作(e.g. 封装了一个函数,里面连接服务器,发送数据,关闭句柄)的情况下,内存不到一个小时,就被吃的差不多了。
既然不能调用closesocket,我就理所当然的认为,一定是被pthreads-w32坑了。
当时用CloseHandle来代替closesocket,测试的时间不长,没有内存泄漏了,暂时先这样。
这是一个月之前的事情。
前天,发现运行这个demo的计算机,长时间运行以后,再开socket服务,已经不行了,bind失败。这时候知道了,用CloseHandle来代替closesocket的后果,产生了句柄泄漏。当频繁调用那个封装函数时(e.g. 发一个小文件,就调用一次封装函数)。当运行几个小时后,socket句柄就被用的差不多了。
下面就是必须要解决这个问题,究竟咋的了。
知道调用closesocket会崩溃,那就单步进去崩着看。
当崩溃的时候,落在_close里面。当时感到好无助:( 这closesocket参数就一个socket句柄,调用一下就完事了,还能怎样?
突然想到,这不是一个API么?为啥会陷在源码里面?迷惑啊。
回到closesocket的调用点,不是有源码么? 那F12一下。
F12到位后,被惊呆了。看到代码是一个宏。。。
#define closesocket(sk) close(sk)
这使我深深的体会到,宏为什么要大写。
最近才开始正式维护这个工程,我不太确定这个可爱的宏是不是我写的。去查svn可以确定作者,但是该工程从旧版升级上来,有几个svn, 查起来不方便。这个宏就是我写的,又咋的了:)
问了老同事,他说这个宏以前就有。
因为这个工程以前是linux版,估计原作者也是为了以后这个类能兼容win版,就定义了这个迷惑性很强的宏。但是这个宏在这个socket封装类中并没有使用。因为关闭句柄时,直接调用close(sk)就可以。这个宏就埋在这里了。
其实这个宏可以写成下面这样, 让非原作者不要入坑太深。当包含了适当的头文件后,不进该坑。
#ifndef closesocket#define closesocket(sk) close(sk)#endif // #ifndef closesocket
如果用大写来表示宏,就很明了,使用者绝对不会入坑, 一看到就知道是宏。
宏的名称也不要和API的名称相同。
#ifndef SAFE_CLOSE_SOCKET#ifndef WIN32 #define SAFE_CLOSE_SOCKET(sk) \ if (INVALID_SOCKET != (sk)) { \ close((sk)); \ (sk) = INVALID_SOCKET; \ }#else #define SAFE_CLOSE_SOCKET(sk) \ if (INVALID_SOCKET != (sk)) { \ closesocket((sk)); \ (sk) = INVALID_SOCKET; \ }#endif#endif // #ifndef SAFE_CLOSE_SOCKET
- 宏一定要用大写
- 创建oralce表、序列一定要用大写啊!
- 新路程------makefile的M一定要大写。。。。
- 建表的脚本中,表名一定要全部大写。
- 为何一定要用Lua?
- 用数组一定要初始化
- 为什么一定要用pthread_join
- 定义函数一定要用function
- 练字一定要用钢笔吗?
- RecyclerView,你一定要这么用!!
- [转载]为什么科学计算一定要用fortran
- 教训:时间一定要用统一输入接口
- 持久层一定要用class吗?
- 强力推荐,一定要用 Google Chrome 浏览器
- 找下载资源不一定要用google
- 拷贝函数参数一定要用引用
- 使用StretchBlt之前一定要用SetStretchBltMode(COLORONCOLOR)
- 使用StretchBlt之前一定要用SetStretchBltMode(COLORONCOLOR)
- 关于分库分表?
- SpringBoot32-springboot开发部署与测试-云部署,基于Docker的部署
- 如何封装一个简单的Android关机接口
- 数组中只出现一次的数字
- leetcode题解-5. Longest Palindromic Substring
- 宏一定要用大写
- OI中C++常数问题及其优化
- 字符串左旋
- 简明矩阵的还原
- c# 教程
- poi导出数据
- C++取得系统时间方法
- 慕课网学习项目之答答租车系统
- c语言三阶幻方问题(回溯)