从学习C语言开始,已经接触这门语言好几年了,不敢说有多精通,本文就本人以往遇到过的几个问题做一个说明。
1、认识C语言的安全隐患。
首先,我们来看一个简单的例子。
int nData = 50;
char szBuf[20];
memset(szBuf,0,20);
strcpy(szBuf,(const char*)&nData);我们常见的strcpy函数一般是用来复制字符串的,但是我们这里将一个整数复制了进来,导致不可预知的结果,突然想到另一个用于复制的函数memcpy。它能够复制任意类型的内存读写块,我们也来看一个例子。
char szBuf[20];
memset(szBuf,0,20);
char szSrc[] = "zhouxuguang,hello world!";
memcpy(szBuf,szSrc,strlen(szSrc));在上面的代码中,szBuf总共才20个字节的内存空间,然而szSrc所占的内存空间远超过20个字节,这样就会造成缓冲区溢出,直至程序崩溃。所以说,C语言函数一般不对参数进行检查,这样做的目的是保持高效率,但同时也带来了安全隐患,只要我们在编程中注意到这些就可以了。
2、数据类型所带来的陷阱
在编程中,或许有许多人都只注重算法和流程控制,很少去关注数据类型给你设下的陷阱。如果某一天我成为IT公司面试官,我或许会给应聘者出下面这个题目。
借助于C语言库函数strlen写一个函数用于比较两个字符串的长度,函数原型是bool strCompare(char* szOne,char* anOther);。应聘者可能会说,丫的,你这不是玩我吗?这么简单的函数还要我写?先不说这么多,那先看看几组答案吧。
第一种答案:
bool strCompare(char* szOne,char* anOther)
{
return strlen(szOne)-strlen(anOther) >= 0;
}第二种答案:
bool strCompare(char* szOne,char* anOther)
{
return strlen(szOne) >= strlen(anOther);
}第三种答案:
bool strCompare(char* szOne,char* anOther)
{
assert((NULL != szOne) && (NULL != anOther));
return strlen(szOne) >= strlen(anOther);
}看了上面的三个答案,到底哪一个对,哪一个错呢,或者都有问题呢?
是不是很简单就写出来了?我想问,你敢保证没有错误吗?
首先,我们来认识一个数据类型,size_t,这个数据类型应该不陌生吧,在很多C语言库函数和STL中都有见过。它在win32下面实际上是unsigned int。说了半天废话,怎么还不到重点,别急,那我们来看看strlen的返回类型是什么,恰恰就是size_t,假设strlen(szOne) 比 strlen(anOther),相减的结果在数学上是一个负数,然而unsigned不能表示负数,其最终结果是一个很大的数,它恒大于等于0,所以该函数用于返回true。
那第二个函数就不会产生这样的问题,虽然有所改进,但是没有对参数进行检查,加入传进去的参数有一个为NULL,就会导致程序崩溃。
所以第三个函数就在第二个函数的基础上对参数进行了检查。
3、尽量用前闭后开的for循环
for循环在编程语言中可以说是运用得非常多,大部分算法的编写都与for循环有关系。可能有些人觉得for循环没啥,但要真正理解也不是那么容易。来看一个例子。
long sumArr(int* arr,unsigned nCount)
{
long sum = 0;
for (unsigned i = 0; i <= nCount-1; i++)
{
sum += arr[i];
}
return sum;
}上面的代码有什么问题,如果不仔细看,还真看不出有什么问题。首先可以肯定的是上面计算数组和的函数有问题。第一,当nCount为0时,那么for循环中i <= nCount-1永远为真,为什么,看上面第二点我分析的。无符号数0减去1就会得到unsigned的最大值,从而导致对数组arr的非法访问,最终导致程序崩溃。那么修改方法可以将nCount改为int型,也可以将i <= nCount-1改为i <= nCount。所以最好采用前闭后开的区间。
今天就写这么多,以后本文会更新。

被折叠的 条评论
为什么被折叠?



