C++程序员必读:让你的代码更强大(1
来源:互联网 发布:网络暴力调查数据图 编辑:程序博客网 时间:2024/05/01 23:46
C++程序员必读:让你的代码更强大(1)
这篇文章提出了一些建议,能有引导我们写出更加强壮的C++代码,以避免产生灾难性的错误。即使、因为其复杂性和项目团队结构,你的程序目前不遵循任何编码规则,按照下面列出的简单的规则可以帮助您避免大多数的崩溃情况。
Introduction
在实际的项目中,当项目的代码量不断增加的时候,你会发现越来越难管理和跟踪其各个组件,如其不善,很容易就引入BUG。因此、我们应该掌握一些能让我们程序更加健壮的方法。
这篇文章提出了一些建议,能有引导我们写出更加强壮的代码,以避免产生灾难性的错误。即使、因为其复杂性和项目团队结构,你的程序目前不遵循任何编码规则,按照下面列出的简单的规则可以帮助您避免大多数的崩溃情况。
Background
先来介绍下作者开发一些软件(CrashRpt),你可以http://code.google.com/p/crashrpt/网站上下载源代码。CrashRpt 顾名思义软件崩溃记录软件(库),它能够自动提交你电脑上安装的软件错误记录。它通过以太网直接将这些错误记录发送给你,这样方便你跟踪软件问题,并及时修改,使得用户感觉到每次发布的软件都有很大的提高,这样他们自然很高兴。
图 1、CrashRpt 库检测到错误弹出的对话框
在分析接收的错误记录的时候,我们发现采用下文介绍的方法能够避免大部分程序崩溃的错误。例如、局部变量未初始化导致数组访问越界,指针使用前未进行检测(NULL)导致访问访问非法区域等。
我已经总结了几条代码设计的方法和规则,在下文一一列出,希望能够帮助你避免犯一些错误,使得你的程序更加健壮。
Initializing Local Variables
使用未初始化的局部变量是引起程序崩溃的一个比较普遍的原因,例如、来看下面这段程序片段:
- // Define local variables
- BOOL bExitResult; // This will be TRUE if the function exits successfully
- FILE* f; // Handle to file
- TCHAR szBuffer[_MAX_PATH]; // String buffer
- // Do something with variables above...
上面的这段代码存在着一个潜在的错误,因为没有一个局部变量初始化了。当你的代码运行的时候,这些变量将被默认负一些错误的数值。例如bExitResult 数值将被负为-135913245 ,szBuffer 必须以“\0”结尾,结果不会。因此、局部变量初始化时非常重要的,如下正确代码:
- // Define local variables
- // Initialize function exit code with FALSE to indicate failure assumption
- BOOL bExitResult = FALSE; // This will be TRUE if the function exits successfully
- // Initialize file handle with NULL
- FILE* f = NULL; // Handle to file
- // Initialize string buffer with empty string
- TCHAR szBuffer[_MAX_PATH] = _T(""); // String buffer
- // Do something with variables above...
注意:有人说变量初始化会引起程序效率降低,是的,确实如此,如果你确实非常在乎程序的执行效率,去除局部变量初始化,你得想好其后果。
Initializing WinAPI Structures
许多Windows API都接受或则返回一些结构体参数,结构体如果没有正确的初始化,也很有可能引起程序崩溃。大家可能会想起用ZeroMemory宏或者memset()函数去用0填充这个结构体(对结构体对应的元素设置默认值)。但是大部分Windows API 结构体都必须有一个cbSIze参数,这个参数必须设置为这个结构体的大小。
看看下面代码,如何初始化Windows API结构体参数:
- NOTIFYICONDATA nf; // WinAPI structure
- memset(&nf,0,sizeof(NOTIFYICONDATA)); // Zero memory
- nf.cbSize = sizeof(NOTIFYICONDATA); // Set structure size!
- // Initialize other structure members
- nf.hWnd = hWndParent;
- nf.uID = 0;
- nf.uFlags = NIF_ICON | NIF_TIP;
- nf.hIcon = ::LoadIcon(NULL, IDI_APPLICATION);
- _tcscpy_s(nf.szTip, 128, _T("Popup Tip Text"));
- // Add a tray icon
- Shell_NotifyIcon(NIM_ADD, &nf);
注意:千万不要用ZeroMemory和memset去初始化那些包括结构体对象的结构体,这样很容易破坏其内部结构体,从而导致程序崩溃.
- // Declare a C++ structure
- struct ItemInfo
- {
- std::string sItemName; // The structure has std::string object inside
- int nItemValue;
- };
- // Init the structure
- ItemInfo item;
- // Do not use memset()! It can corrupt the structure
- // memset(&item, 0, sizeof(ItemInfo));
- // Instead use the following
- item.sItemName = "item1";
- item.nItemValue = 0;
- 这里最好是用结构体的构造函数对其成员进行初始化.
- // Declare a C++ structure
- struct ItemInfo
- {
- // Use structure constructor to set members with default values
- ItemInfo()
- {
- sItemName = _T("unknown");
- nItemValue = -1;
- }
- std::string sItemName; // The structure has std::string object inside
- int nItemValue;
- };
- // Init the structure
- ItemInfo item;
- // Do not use memset()! It can corrupt the structure
- // memset(&item, 0, sizeof(ItemInfo));
- // Instead use the following
- item.sItemName = "item1";
- item.nItemValue = 0;
Validating Function Input
在函数设计的时候,对传入的参数进行检测是一直都推荐的。例如、如果你设计的函数是公共API的一部分,它可能被外部客户端调用,这样很难保证客户端传进入的参数就是正确的。
例如,让我们来看看这个hypotethical DrawVehicle() 函数,它可以根据不同的质量来绘制一辆跑车,这个质量数值(nDrawingQaulity )是0~100。prcDraw 定义这辆跑车的轮廓区域。
看看下面代码,注意观察我们是如何在使用函数参数之前进行参数检测:
- BOOL DrawVehicle(HWND hWnd, LPRECT prcDraw, int nDrawingQuality)
- {
- // Check that window is valid
- if(!IsWindow(hWnd))
- return FALSE;
- // Check that drawing rect is valid
- if(prcDraw==NULL)
- return FALSE;
- // Check drawing quality is valid
- if(nDrawingQuality<0 || nDrawingQuality>100)
- return FALSE;
- // Now it's safe to draw the vehicle
- // ...
- return TRUE;
- }
Validating Pointers
在指针使用之前,不检测是非常普遍的,这个可以说是我们引起软件崩溃最有可能的原因。如果你用一个指针,这个指针刚好是NULL,那么你的程序在运行时,将报出异常。
- CVehicle* pVehicle = GetCurrentVehicle();
- // Validate pointer
- if(pVehicle==NULL)
- {
- // Invalid pointer, do not use it!
- return FALSE;
- }
- C++程序员必读:让你的代码更强大(1
- C++程序员必读:让你的代码更强大(1)
- C++程序员必读:让你的代码更强大
- 10个工具让你的 shell 脚本更强大
- 10个工具让你的 shell 脚本更强大
- 10个工具让你的shell脚本更强大
- 六个可以让你变得更强大的残酷事实
- 10个工具让你的 shell 脚本更强大
- 10个工具让你的 shell 脚本更强大
- 10个工具让你的 shell 脚本更强大
- c 强大的vim配置文件,让编程更随意
- 让你的C++代码更健壮
- 脚本,让你的代码更整洁
- 让你的代码看起来更漂亮
- const让你的代码更安全
- 10个足以让你成为更优秀的程序员的C语言资源
- 10个足以让你成为更优秀的程序员的C语言资源
- 能让你成为更优秀程序员的10个C语言资源
- (转载)web.xml 中的listener、 filter、servlet 加载顺序及其详解
- 出错"ORA-01000: maximum open cursors exceeded" 问题分析
- VMware workstation 7.0 下安装 unbuntu 12.10 Vmware Easy Install 解决办法
- 海量数据对比去除重复的解决方案
- 免费主机
- C++程序员必读:让你的代码更强大(1
- VMware Workstation 9下安装Fedora 18图文教程
- socket通信机制
- Node.js express获取参数有三种方法
- 如何成为强大的程序员?
- JToggleButton的使用
- Android实现推送方式解决方案
- byte to string
- 盘点有趣的恋爱心理