黑客24小时在线接单网站

黑客在线接单,网站入侵,渗透测试,渗透网站,入侵网站

代码安全性和健壮性:如何在if和assert中做选择?

                   
  • 一、前言
  •                
  • 二、assert 断言
  •                
  • 三、if VS assert
  •                
  • 四、总结

一、前言

当我们滚动代码时,我们经常需要检查代码的安全性,例如:

1. 指针是空的吗?

2. 除数是否为 0

3. 函数调用的返回结果是否有效?

4. 打开文件成功吗?

检查这种边界条件的手段一般是 if 或者 assert 断言,无论使用哪一种,都可以达到检查的目的。那么这是否意味着两者可以随意使用,想到哪一个就用哪一个呢?

我们来谈谈这篇短文:在不同的场景下,应该用 if,还是要用 assert 断言?

当我写这篇文章时,我想起了孔乙己先生的问题:茴香豆“茴”有有多少种写法?

我们似乎没有必要担心如何选择,因为我们都可以实现我们想要的功能。我以前也这么认为,但现在我不这么认为了。

成为技术大牛,获得更好offer,也许就在这些细节之间,胜负就分开了。

二、assert 断言

刚才,我问了一位嵌入式开发者,他在附近工作了5 多年:if 和 assert 如何选择?assert 是干什么的?

看来有必要简单说一下 assert 断言。

assert() 原型为:

  • voidassert(intexpression);
  • 1. 如果宏的参数值为非零值,则不进行任何操作(no action);

    2. 如果宏的参数为零,打印诊断信息并呼叫abort()。

    例如,以下代码:

  • #include<assert.h>
  • intmy_div(inta,intb)
  • {
  • assert(0!=b);
  • returna/b;
  • }
  • 1. 当 b 不是 0 ,assert 断言什么都不做,程序向下执行;

    2. 当 b 0 ,assert 断言打印错误信息,终止程序;

    在功能方面,assert(0 != b); 等价于以下代码:

  • if(0==b)
  • {
  • fprintf(stderr,"biszero...");
  • abort();
  • }
  • assert 是宏,不是函数

    在 assert.h 头文件中有以下定义:

  • #ifdefNDEBUG
  • #defineassert(condition)((void)0)
  • #else
  • #defineassert(condition)/*implementationdefined*/
  • #endif
  • 既然是宏定义,说明宏替换是在预处理时进行的。

    从以上定义可以看出:

                   
  • 若定义宏 NDEBUG,那么 assert() 宏不会做任何动作,相当于一句空话:(void)0;,当在 release 在编译选项中编译代码时(Makefile)定义这个宏。
  •                
  • 若宏 没有定义NDEBUG,那么 assert() 宏将替换一些检查代码,我们将在开发阶段执行 debug 模式编译时,一般都会屏蔽掉这 NDEBUG 这个宏。
  • 三、if VS assert

    用代码片段来描述问题更容易理解。

  • //brief:将两个短字符串成一个字符串
  • char*my_concat(char*str1,char*str2)
  • {
  • intlen1=strlen(str1);
  • intlen2=strlen(str2);
  • intlen3=len1 len2;
  • char*new_str=(char*)malloc(len3 1);
  • memset(new_str,0len3 1);
  • sprintf(new_str,"%s%s",str1,str2);
  • returnnew_str;
  • }
  • 如果开发人员写上面的代码,肯定会被领导面试!存在以下问题:

                   
  • 输入参数无有效性检查;
  •                
  • 没有对 malloc 检查结果;
  •                
  • sprintf 效率很低;
  •                
  • ...
  • 1. 使用 if 检查句子

  • char*my_concat(char*str1,char*str2)
  • {
  • if(!str1||!str2)//参数错误
  • returnNULL;
  • intlen1=strlen(str1);
  • intlen2=strlen(str2);
  • intlen3=len1 len2;
  • char*new_str=(char*)malloc(len3 1);
  • if(!new_str)///申请堆空间失败
  • returnNULL;
  • memset(new_str,0len3 1);
  • sprintf(new_str,"%s%s",str1,str2);
  • returnnew_str;
  • }
  • 2. 使用 assert 检查断言

  • char*my_concat(char*str1,char*str2)
  • {
  • //确保参数正确
  • assert(NULL!=str1);
  • assert(NULL!=str2);
  • intlen1=strlen(str1);
  • intlen2=strlen(str2);
  • intlen3=len1 len2;
  • char*new_str=(char*)malloc(len3 1);
  • ///确保申请堆空间成功
  • assert(NULL!=new_str);
  • memset(new_str,0len3 1);
  • sprintf(new_str,"%s%s",str1,str2);
  • returnnew_str;
  • }
  • 3. 你喜欢哪一个?

    首先,上述 2 种检验 *** 在实际代码中非常常见,在功能上似乎没有影响。因此,没有严格的错误和正确的区别,其中许多取决于每个人不同的偏好和习惯。

    (1) assert 支持者

    我作为 my_concat() 函数的实现者的目的是拼接字符串,所以输入的参数必须合法有效,调用者需要对此负责。如果输入的参数无效,我会非常惊讶!我该怎么办:崩溃给你看!

    (2)if 支持者

    我写的 my_concat() 函数很强,所以我预计调用器会乱搞,故意引入一些无效参数来测试我的编码水平。没事,来吧,我可以处理任何事情!

    这两个派别的理由似乎都很充分!到底该怎么选择?真的跟着感觉走吗?

    假设我们严格按照常规流程开发一个项目:

    1. 在开发阶段,编译选项中不定义 NDEBUG 这个宏,那么 assert 发挥作用;

    2. 项目发布时,在编译选项中定义了 NDEBUG另一个宏,那么 assert 相当于空语句;

    也就是说,只有 debug 开发阶段,用 assert 断言可以正确检查参数无效。release 阶段,assert 不起作用。如果调用器传递无效参数,程序只会崩溃。

    这是什么意思?代码中存在 bug?还是代码不够强壮?

    从我个人的理解来看,这是单元测试写得不好,参数无效的 case!

    4. assert 的本质

    assert 是为了验证有效性。它更大的作用是让我们的程序在开发阶段尽可能 crash。每一次的 crash,代码中存在 bug,我们需要纠正它。

    写一个 assert 断言时,说明断言失败是不允许的,也是不允许的。功,程序才能继续执行。

    5. if-else 的本质

    if-else 语句用于逻辑处理,用于处理各种可能的情况。也就是说,每个分支都是合理的,是允许的,我们必须处理这些分支。

    6. 我喜欢的版本

  • char*my_concat(char*str1,char*str2)
  • {
  • ///参数必须有效
  • assert(NULL!=str1);
  • assert(NULL!=str2);
  • intlen1=strlen(str1);
  • intlen2=strlen(str2);
  • intlen3=len1 len2;
  • char*new_str=(char*)malloc(len3 1);
  • //申请堆空间失败是可能的,也是允许的。
  • if(!new_str)
  • returnNULL;
  • memset(new_str,0len3 1);
  • sprintf(new_str,"%s%s",str1,str2);
  • returnnew_str;
  • }
  • 对于参数:我认为输入的参数必须有效。如果有无效参数,则代码中存在 bug,这种情况不允许发生,必须解决。

    资源分配结果(malloc 函数):我认为资源分配失败是合理的,是可能的,是允许的,我也处理过这种情况。

    当然,这并不意味着要使用 进行参数检查assert,主要根据不同的场景和语义来判断。例如,以下示例:

  • intg_state;
  • voidget_error_str(boolflag)
  • {
  • if(TRUE==flag)
  • {
  • g_state=1;
  • assert(1==g_state);///确保赋值成功
  • }
  • else
  • {
  • g_state=0;
  • assert(0==g_state);///确保赋值成功
  • }
  • }
  • flag 参数代表不同的分支,给 赋值g_state 使用后,必须保证赋值结果的正确性assert 断言。

    四、总结

    本文分析了 C 语言中晦涩模糊的概念似乎有有点虚幻,但我们确实需要停下来仔细考虑。

    如果有些场景真的处理不好,我会问自己一个问题:

    这种情况允许吗?

    不允许:用 assert 断言,在开发阶段尽量发现所有错误;

    允许:用 if-else,说明这是一个需要下一步处理的合理逻辑。

    本文转载自微信公众号「IOT物联网小镇」,请联系以下二维码。IOT物联网小镇微信官方账号。

       
    • 评论列表:
    •  孤央离鸢
       发布于 2022-05-29 18:44:08  回复该评论
    • 时,我们经常需要检查代码的安全性,例如:1. 指针是空的吗?2. 除数是否为 03. 函数调用的返回结果是否有效?4. 打开文件成功吗?检查这种边界条件的手段一般是 if 或者 assert 断言,无论使用哪一种,
    •  夙世节枝
       发布于 2022-05-30 00:34:24  回复该评论
    • {g_state=1;assert(1==g_state);///确保赋值成功}else{g_state=0;assert(0==g_state);///确保赋值成功}}flag 参数代
    •  囤梦竹祭
       发布于 2022-05-30 00:11:20  回复该评论
    • cat(char*str1,char*str2){if(!str1||!str2)//参数错误returnNULL;intlen1=strlen(str1);intlen2=strlen(str2);intlen3=

    发表评论:

    Powered By

    Copyright Your WebSite.Some Rights Reserved.