C语言编码安全规范 高频易错10条

1.程序中不要出现仅靠大小写区分的相似的标识符,以防止混淆。

2.防止局部变量与全局变量同名。

double d = 0; 
void func(void) 
{
    int d = 5;
    double b = d;//有的编译器会匹配全局变量,因为全局变量的类型更匹配,有的会匹配局部变量。
}
首先,匹配哪一个会有不确定性,不同编译器不同。
其次,如果局部变量和全局变量的类型与名称完全一致,在局部变量定义的有效域中,局部变量将覆盖全局变量。
直到退出局部变量定义域,全局变量的定义才又重新可见。
要求不能同名。

3.避免符号错误。

在写条件判断语句里,=和==这两个符号很容易因为疏忽而写错,从而造成一些非预期的结果
比如下面例子:
if(Setp = 2)//此时编译器并不会报错,在if判断语句里,实现上是对Setp先进行赋值然后判断的
{
    ...;
}

为了防止这种情况的发生,我们可以把常量写在前面,此时如果将==写成=则编译器会报错
if(2 == Setp)
{
    ...;
}

4.避免整数溢出。

5.时刻注意表达式是否会上溢、下溢,注意以下情况:

  a.自加或自减时

  b.两个值相加和相乘时

  c.两个值相减时

  d.多数值互相运算时

整型溢出,分为无符号整型溢出和有符号整型溢出。
对于unsigned整型溢出,C的规范是有定义的——“溢出后的数会以2^(8*sizeof(type))作模运算”,
也就是说,如果一个unsigned char(1字符,8bits)溢出了,会把溢出的值与256求模。
unsigned char x = 0xff;
printf("%d\n", ++x);
输出:0 (因为0xff + 1是256,与2^8求模后就是0)
如果是x = x + 2;结果会如何?逻辑会如何?
对于signed整型的溢出,C的规范定义是“undefined behavior”,也就是说,编译器爱怎么实现就怎么实现。
对于大多数编译器来说,算得啥就是啥。比如:
signed char x =0x7f; //注:0xff就是-1了,因为最高位是1也就是负数了
printf(“%d\n”, ++x);
可能输出:-128,因为0x7f + 0x01得到0x80,也就是二进制的1000 0000,符号位为1,负数,
后面为全0,就是负的最小数,即-128。
千万别以为signed整型溢出就是负数,这个是不定的。 比如上面 x = x * 0x05;  可能得到的是123

示例一:整形溢出导致死循环
short len = 0;
while(len < MAX_LEN)
{
   len += readFromInput(fd, buf);
   buf += len;
}
如果MAX_LEN设置成32767则会死循环。

示例二:整形转型时的溢出
#define MAX_LEN 256 
int copy_something(char *buf, int len)
{ 
    char mybuf[MAX_LEN];
    int rtnValue = FALSE;
    if(len < MAX_LEN)
    {
        memcpy(mybuf, buf, len);
        rtnValue = TRUE;
    }
    else 
    { 
        rtnValue = FALSE;
    }

    return rtnValue;
}
如果len是一个负数进来,可以通过if,memcpy函数则会将len强转为unsigned。

示例三:分配内存
nresp = packet_get_int(); 
if (nresp > 0) 
{ 
    response = malloc(nresp*sizeof(char*)); 
    for (i = 0; i < nresp; i++) 
    { 
        response[i] = packet_get_string(NULL); 
    }
}
当变量nresp的值为1073741825时,nresp*sizeof(char*)的值为1073741825 * 4,
超过int型最大值溢出,结果成为了 0x100000004 ,实际分配大小为4。

6.用宏定义表达式时,要使用完备的括号。

示例:如下定义的宏都存在一定的风险。
#define RECTANGLE_AREA( a, b )  a * b
#define RECTANGLE_AREA( a, b )  (a * b)
#define RECTANGLE_AREA( a, b )  (a) * (b)
正确的定义应为:
#define RECTANGLE_AREA( a, b )  ((a) * (b))

简单举例 a为4+5, b为5-3, 45/RECTANGLE_AREA(a,b) 结果多少?

7.将宏所定义的多条表达式放在大括号中。

示例:下面的语句只有宏的第一条表达式被执行。
#define INTI_RECT_VALUE( a, b )\
    a = 0;\
    b = 0;

正确的用法应为:
#define INTI_RECT_VALUE( a, b )\
{\
    a = 0;\
    b = 0;\
}

8.用括号明确表达式的操作顺序,避免过分依赖默认优先级。

防止阅读程序时产生误解,防止因默认的优先级与设计思想不符而导致程序出错。
word = (high << 8) | low     (1)
if ((a | b) && (a & c))      (2)
if ((a | b) < (c & d))       (3)

如果书写为
high << 8 | low
a | b && a & c
a | b < c & d
语句不易理解,判断条件出错

9.两个浮点数不能直接进行比较,需要转换为整数后再进行比较,比较需要考虑系统允许的精度误差。

首先,浮点数在计算机当中的二进制表达方式就决定了大多数浮点数都是无法精确的表达的
现在的计算机大部分都是数字计算机,不是模拟机,数字机的离散化的数据表示方法自然无法精确表达大部分的数据量的。
其次计算机浮点数的精度在单精度float类型下,只有7位,在进行浮点运算的时候,
这个精度往往会导致运算的结果和实际期望的结果之间有误差
因为前两个原因,我们很难用 A==B来判定两个浮点数是否相同

如何比较浮点数
bool IsEqual(float a, float b, float absError, float relError )
{
    bool retValue = false;
    if (a == b) 
    {
        retValue = true;
    }
    else if (fabs(a-b) < absError ) 
    {
        retValue = true;
    }
    else if (fabs(a)> fabs(b)) 
    {
        retValue = (fabs((a-b)/a)> relError) ? true : false;
    }
    else 
    { 
        retValue = (fabs((a-b)/b)> relError) ? true : false;
    }
    return retValue;
}

浮点数比较实现 —— 利用整形
bool AlmostEqualUlps(float A, float B, int maxUlps)
{  
    bool retValue = false;
    assert(sizeof(float) == sizeof(int));
    if (A == B)
    {
        retValue = true;
    }
    else 
    {  
        int intDiff = abs(*(int*)&A - *(int*)&B);
        if (intDiff <= maxUlps)
        {
            retValue = true;
        } 
        else 
        {
            reValue = false;
        }
    }
    return retValue;
}


10.数组在使用下标读写前必须检查下标是否越界,如果不需要防护需要进行安全分析说明不需要防护,下标范围不会超出数字最大值。

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值