最近仔细研读了肖舸先生写的《0 Bug: C/C++商用工程之道》一书,感触颇深,相信作者具备较深厚的技术背景与个人见解。其中的C/C++无错化程序设计一栏给我留下了深刻印象,总结了一下,如下: //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //安全的字符串拷贝函数 //参数按顺序排:目的地址,源地址,拷贝的最大字节数 void SafeStrcpy(char *pDest, char *pSource, int nCount) { int nLen = (int)strlen(pSource) + 1; //获得源字符串长度,+1保证'\0'; if (!pDest) goto SafeStrcpy_END_PROCESS; //保护措施,如果目标地址非法 if (!pSource) goto SafeStrcpy_END_PROCESS; //则放弃服务,跳到结束点 if (nLen > nCount) nLen = nCount; //避免读写出界的设计 memcpy (pDest, pSource, nLen); //实施拷贝 *(pDest+nLen-1) = '\0'; //手工添加\'0' SafeStrcpy_END_PROCESS: //这是结束点 return; } /**************************** * 以下为常用函数命名示例 ***************************/ //一个弹出式缓冲区的清空函数 void CTonyPopBuffer::Clean(void); //弹出式缓冲区的服务函数,以bool量表示内部的各种资源申请是否完成,是否可以服务。 bool CTonyPopBuffer::ICanWork(void); //安全的字符串拷贝函数 void SafeStrcpy(char *pDest, char *pSource, int nCount); //安全的字符串缓冲区打印函数,需要输入缓冲区指针,限制长度,变参打印机 int SafePrint(char *szBuf, int nMaxLength, char *szFormat, ...); //////////////////////////////////////////////////////////////////////////////////////////// // //安全的字符串拷贝函数二 //参数按顺序排:目的地址,源地址,目标地址空间大小(必须输入) void xg_strncpy(char *pD, char *pS, int nDestSize) { int nLen = strlen(pS) + 1; if (nLen > nDestSize) nLen = nDestSize; //目标地址和拷贝地址取小 memcpy (pD, pS, nLen); //强行以二进制定长拷贝 *(pD + nLen - 1) = '\0'; //强行进行\'0'修饰 } //安全的字符串构造函数(For WIN32) //用以取代有Bug的C类sprintf. //参数按顺序排:目的缓冲区,写入的字符串长度,格式化输出字符串(变参) int xg_printf(char *szBuf, int nDestSize, char *szFormat, ...) { int nListCount = 0; //计数器 va_list pArgList; va_start(pArgList, szFormat); //变参处理循环 nListCount += _vsnprintf(szBuf+nListCount, //处理每一个变参参数 nDestSize-nListCount,szFormat,pArgList); va_end(pArgList); *(szBuf+nDestSize-1) = '\0'; //人工补充'\0' return strlen(szBuf); //返回真实的字符串长度 } #ifdef WIN32 #define Linux_Win_vsnprintf _vsnprintf #else // not WIN32 #define Linux_Win_vsnprintf vsnprintf #endif //安全的变参输出函数 //szBuf:用户指定的输出缓冲区 //nMaxLength:用户指定的输出缓冲区尺寸 //szFormat:格式化输出字符串(变参,可以是多个) //返回输出的字符总数(strlen的长度,不包括最后的'\0') int SafePrintf(char *szBuf, int nMaxLength, char *szFormat, ...) { int nListCount = 0; va_list pArgList; //此处做安全性防护,防止用户输入非法的缓冲区,导致程序在此崩溃。 if (!szBuf) goto SafePrintf_END_PROCESS; //此处开启系统循环,解析每条格式化输出字符串 va_start(pArgList, szFormat); nListCount += Linux_Win_vsnprintf(szBuf+nListCount, nMaxLength-nListCount, szFormat, pArgList); va_end(pArgList); //实现缓冲区超限保护 if (nListCount > (nMaxLength-1)) nListCount = nMaxLength-1; //人工添加'\0',确保输出100%标准C字符串 *(szBuf + nListCount) = '\0'; SafePrintf_END_PROCESS: return nListCount; } //向指定的缓冲区输出一个时间戳字符串 //szBuf:用户指定输出缓冲区 //nMaxLength:用户指定的输出缓冲区尺寸 //返回输出的字符串总数(strlen的长度,不包括最后的'\0') static int GetATimeStamp(char *szBuf, int nMaxLength) { time_t t; struct tm *pTM = NULL; int nLength = 0; time(&t); pTM = localtime(&t); nLength = SafePrintf(szBuf, nMaxLength, "%s", asctime(pTM)); //这里是个重要的技巧,asctime(pTM)产生的字符串最后会自动带上回车符, //这给后面很多的格式化输出带来不便 //此处退格结束字符串,目的是过滤掉这个回车符 szBuf[nLength-1] = '\0'; return strlen(szBuf); //返回字符串长度 } ///////////////////////////////////////////////////////////////////////////////// |