现在,貌似使用c语言进行软件编程开发的人越来越少了啊。周围大部分同事都C++和python了。但是估计应该不会消失吧。我自己工作很多年一直都用C开发,以为挺不错。但是近几天无意翻了翻《C安全编码》,突然发现原来我对C一直都是一知半解啊。平时相当然认为的常规做法其实一直都是有问题的,下面就是一些记录。没有考虑到的地方,欢迎大家提出来。
一 字符究竟是什么类型
你可能理所当然的认为是char,或者unsigned char 或signed char。我也这么认为,其实我们都错了。
1.1 凡是带get语义的字符获得函数,如getc、fgetc、getchar....返回值都是int类型;
1.2 sizeof('a')的值是4,也就是int类型的大小,也就是说char a = ‘a’,执行sizeof(a)和sizeof('a')并不相等。
二 realloc和alloca函数使用
这两个都是申请内存空间的函数,可能这两个函数使用的并不多,我们一般使用malloc和free就够了。但是这2个函数需要拿出来说下,你稍微不小心就会出错,导致coredump。
2.1 realloc
原型是void* realloc(void* p, size_t size),这个函数用于空间扩展,并且扩展后的空间还必须包括原来空间的内容。p是前面通过malloc、calloc或realloc返回的指针。除了原来的内容保留下来,其余空间未初始化,而原来的空间指针p释放。这个函数主要有以下几种情况:
2.1.1 如果realloc返回空指针,那么旧的p值不变化,就当没有调用过realloc函数;
2.1.2 如果p为空,则realloc和malloc(size)相同;
2.1.3 如果size为0,realloc相当于free(p),但是不建议这么做,如下代码段:
-
char* p2;
-
char* p = malloc(100);
-
...
-
if((p2 = realloc(p, nsize)) == NULL)
-
{
-
if(p)
-
free(p)
-
p = NULL;
-
return NULL;
-
}
-
p = p2;
似乎没问题,仔细看下,如果nsize为0的话会如何?p会多次释放。改为如下就好了:
-
char* p2;
-
char* p = malloc(100);
-
...
-
if((nsize == 0) || ((p2 = realloc(p, nsize)) == NULL))
-
{
-
free(p)
-
p = NULL;
-
return NULL;
-
}
-
p = p2
其实,最好的办法是永远不申请0字节长度的空间。
2.2 alloca
这个函数申请的空间在栈上。不必手动释放。
三 无符号数据类型计算永远不会溢出
现代计算机存储数值都是以补码形式存储,如4位无符号整形的取值:0、1、2、3、4、5、6、7、8、9、10、11、12、13、14、15,最大数值是15,再加1回滚到0,而最小数值是0,,再减一会回滚到15,永远不会出现负数。所以下面循环永远不会退出:
-
for(unsigned i = n; i >= 0; i--)
四 求绝对值
补码形式存储4位有符号数举例子:0、1、2、3、4、5、6、7、-8、-7、-6、-5、-4、-3、-2、-1,这个也会回滚,你的注意点要几种的7和-8这两位身上,即四位有符号数最大是7,再加1就是最大的负数,四位有符号数最小是-8,再减1就是最大的7。除此之外,你还有没有发现什么,那就是正负数不对称。这要紧吗?接着看:
点击(此处)折叠或打开
-
#define abs(n) ((n) < 0 ? -(n) : (n))
也就是说4位有符号数,最小负数是-8,对-8取绝对值是8,但是遗憾的是4位有符号数不能表示8这个数值,你说有没有问题吧。
五 char型数值
如果你想要定义某数值类型为char,建议你不要这么定义,如果非要定义不可,那就定义成unsigned char或signed char。char类型做数值运算,究竟是按照unsigned char还是signed char来计算,取决于编译器,也就是说你这个char具体是什么数值取决于环境。如:
-
char a = 200;
-
int b = 1000;
-
printf("b/a = %d\n", b/a)
在我电脑上输出是-17,为毛啊?我的电脑把char型数值计算是按照signed char型来操作的,signed char是无法存储200这个数值的,200和位模式和-56的位模式相同,我的电脑就是1000/(-56)这样计算的。
先到这,有新的易出错的知识点再补。
阅读(809) | 评论(0) | 转发(0) |