编程常见bug检查1

总结一些日常工作常见的代码和编程情况,会分几个文章写出,也许对一些朋友有些作用

一、基础风格

1、设计函数参数时,请注意函数参数的顺序和个数

    *一般而言,建议输入参数在前,输出参数在后

    *输入参数是指针,且不改变时,请注意定义的时候类型加const

    *输入参数是struct或者Class实例且不修改时,可以使用const&来传递,或者const指针

        **这样可以避免当传入形参的实例对象较大时,拷贝会降低效率

        **还可以避免对该对象的改变

    *尽可能的减少不必要的参数个数

2、避免不必要的函数调用

    *不合理的代码:

    char* pszString = “hello, it's me”;

    for(int i = 0;i < strlen(pszString); ++i)

    {

        。。。DoSomething

    }

    *合理的代码

    char* pszString = “hello, it's me”;

    int nLength = (int)strlen(pszString);

    for(int i = 0;i < nLength; ++i)

    {

        。。。DoSomething

    }

3、避免头文件定义全局变量,防止重复引用头文件时,全局变量被重复定义;

4、字符串拼接时请注意避免越界

    1)因为有时候文件存储的文件名字段可能是错的,避免异常情况下的越界,拼接时需要注意是否越界

    2)snprintfs的count参数请用_TRUNCATE,避免buffer太小导致的宕机(针对windows的PC)

    https://msdn.microsoft.com/en-us/library/ms175769.aspx

5、如果处理资源出错,需要写日志时,请写明出错的文件名

6、继承接口或抽象类的子类,析构函数请注意定义为virtual

    *当指针类型是父类指针,指向的内容是子类对象时,定义为virtual可以避免delete该指针时会出现泄露的情况

7、DWORD/long和void*转换

    *64位的程序下,void*是8字节,DWORD/long是4字节。这样如果是指针存储在DWORD,高位的4个字节是会丢失

8、推荐调用函数时,对返回值进行判断,同时对错误的返回值做相应的错误处理

    *确认是否需要函数返回值?

    *确认出错了怎么办?错误如何表达?

    *是否需要定义出错处理的准则或出错后是否需要还原?

二、断言的使用

1、static_assert执行编译时的断言检查

    如:static_assert(sizeof(char *) == 8, "不是64位模式")

2、断言来检查参数合法性的场景:

    1)代码执行之前或者在函数入口处,用断言来检查参数的合法性

    2)代码执行之后或在函数出口处,用断言来检查是否正确执行,输出或内部状态是否如预期

3、避免用断言去检查程序错误:

    1)外部不可靠的数据应该做严格检查才能放到系统内部,这个时候它是守卫,提前检验和过滤不合理的数据和参数,应该使用错误检查处理代码,而不是用断言来做检查

        *外部不可靠的数据:如不合理的用户输入、或其他模块传入到该模块的消息或数据,

        有点像是对项目组外的员工,要动用项目组的资源,是需要经过审核的

    2)系统内部的交互数据(如程序内部的调用),可以用断言来检查意想不到的错误,或者程序内部的假设。(其实就是检查它的潜规则和逻辑边界、隐形假定)

        *因为系统内的调用者一般情况下是有义务负责传递给自己内部的数据是合理正常的数据

        就像项目内的员工,对于使用项目组的资源的门槛就会低很多

        *可理解为assert和出错处理是对所写程序建立不同的信用级别,也方便在Release版可以性能更好的运行程序

    3)推荐针对public的函数或接口的入口处对参数做严格的错误检查;对于Private的函数或者只有这个模块能看到的,可以用assert

4、避免断言中使用改变环境的语句:

    如不正确的代码:

    int Test(int i)

    {

        assert(i++);  //debug版和release版的i值就会不一样

        return i;

    }

    int main()

    {

        int i = 1;

        int nValue = Test(i);

        printf("%d\n",  nValue);

        return 0;

    }

    合理的形式:

    int Test(int i)

    {

        assert(i);

        return ++i;

    }

    注:与改变环境的语句类似的行为是宏定义。

    *请尽量不要在assert中调用宏,以防止宏的副作用

5、防错性程序设计常常要解决的是:现实中,防止用户数据丢失或程序崩溃而采取的措施。只是除此之外:

    *我们是否还希望进行防错性程序设计时,错误不要被隐瞒?

    *实现程序时,我们想要什么?我们期待错误发生时,我们能得到什么或者怎样处理它?

        **这有点像我们接到一个任务的思考点:

        1)我们做这个事的目标是完成它?做好它?

        2)做这个事是希望减轻自己负担?减轻别人负担?or both?

        3)出问题时怎么办?有预案吗?有线索吗?系统里需要埋下什么报警方案?影响大吗?

三、内存

1、内存常见的设计问题导致的代码bug

    1)内存的分配、释放接口应配对;且应该限定在一个同一模块或者同一抽象层内进行

        *否则会加大程序猿追踪内存块的生命周期的负担

        *还可能导致内存泄露、重复释放该内存、非法访问已经释放的内存、写入已经释放或者没有分配的内存等问题

    例:

    **不正确的代码:

    #define MIN_MEM_SIZE 10

    int CompareMemorySize(char* pchBuffer, size_t uSize)

    {

        if(uSize < MIN_MEM_SIZE)

        {

            free(p);

            p = NULL;

            return -1;

        }

        return 0;

    }

    void AllocMemory(size_t uSize)

    {

    char *pchBuffer = (char*)malloc(uSize);

    if(pchBuffer == NULL)

    {.........}

    if(CompareMemorySize(pchBuffer, uSize) == -1)  //这里pchBuffer实际上是已经释放了

    {

        free(pchBuffer);//重复释放了pchBuffer

        pchBuffer = NULL;

        return;

`   }

    ........

    free(pchBuffer);

    pchBuffer = NULL;

}

**正确的代码:

#define MIN_MEM_SIZE 10

int CompareMemorySize(char* pchBuffer, size_t uSize)

{

    if(uSize < MIN_MEM_SIZE)

    {

        return -1;

    }

    return 0;

}

void AllocMemory(size_t uSize)

{

    char *pchBuffer = (char*)malloc(uSize);

    if(pchBuffer == NULL)

    {.........}

    if(CompareMemorySize(pchBuffer, uSize) == -1)

    {

        free(pchBuffer);

        pchBuffer = NULL;

        return;

` }

        ........

    free(pchBuffer);

    pchBuffer = NULL;

}

    **这里可以总结出的维度:

(1)内存的分配、释放接口应配对;且应该限定在一个同一模块或者同一抽象层内进行

(2)尽可能函数的副作用越少越好:减少用户记住调用函数时,需要了解过多的函数内部的实现

2、避免对结构体执行逐个字节的比较

如:

typedef struct

{

char chValue;

int nCount;

char szBuffer[10];

}Buffer;

*不合理的代码:

bool IsEqual(const Buffer *pBuffer1, const Buffer *pBuffer2)

 {

    //因为默认情况想存在字节对齐,对齐的字节内存里填充什么内容和编译器有关系,它的行为未定义,所以这样比较是错的

    if(!memcmp(pBuffer1, pBuffer2, sizeof(Buffer)))

        return true;

    return false;

}

*合理的代码:

bool IsEqual(const Buffer *pBuffer1, const Buffer *pBuffer2)

{

    if(

        pBuffer1->chValue == pBuffer2->chValue &&

        pBuffer1->nCount == pBuffer2->nCount &&

`     strcmp(pBuffer1->szBuffer, pBuffer2->szBuffer) == 0

    )

        return true;

    return false;

}

3、避免执行零长度的内存分配

    *C99规定,程序试图调用malloc、calloc与realloc等系列内存分配函数分配大小为0的内存,其行为时有具体编译器所定义(可能返回NULL,可能返回长度为非零的值等),从而导致产生未定义的行为

4、内存常见检测问题:

    1)确保没有访问空指针

    2)分配和释放内存的操作是否配对

    3)出错处理时,内存是否有出现重复释放、漏释放、或者释放错误

    4)在指针赋值前,是否存在有内存数据丢失

    5)当释放数组元素或者struct类型的元素时,都应先遍历子元素进行释放,再遍历回父节点

    *注:如果class或者一些设计不好的接口,没有注意提供配对的内存操作接口时,会导致各种隐患,如:内存泄漏、野指针等,需要另外检查

    6)注意对函数返回值是动态分配内存的检查和内部状态的检查

    7)引用计数的检查

    8)内存填充是否越界、或数据类型不正确

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269

推荐阅读更多精彩内容

  • 题目类型 a.C++与C差异(1-18) 1.C和C++中struct有什么区别? C没有Protection行为...
    阿面a阅读 7,565评论 0 10
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,293评论 18 399
  • __block和__weak修饰符的区别其实是挺明显的:1.__block不管是ARC还是MRC模式下都可以使用,...
    LZM轮回阅读 3,204评论 0 6
  • 1 文件结构 每个C++/C程序通常分为两个文件。一个文件用于保存程序的声明(declaration),称为头文件...
    Mr希灵阅读 2,844评论 0 13
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,100评论 18 139