0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

MISRA—C关于数据类型的规则及陷阱有哪些

电子设计 来源:单片机与嵌入式系统应用 作者:张乐平,邵贝贝 2020-09-28 10:06 次阅读

数据类型是编程语言中最基本的构成元素,但却是最易被忽略的一环,程序员愿意把几乎100%的精力都花在算法研究、程序流控制等大环节上,却很少在数据类型问题上反复斟酌。

细节决定成败,一个螺丝钉的失误可能导致一个飞行器的毁灭,一个数据类型的错误同样可以让庞大的软件系统崩溃。

MISRA—c中关于数据类型的规则主要分为两个方面。一是数据类型相关的编程风格;二是不同数据类型之间的转换,后者是重点。这里介绍MISRA_C关于数据类型的部分规则,更多的规则请参考《MISRA-C:2OO4)》一书。

下文中凡是未加特殊说明的都是强制(required)规则.个别推荐advisory)规则加了“推荐”标识。

在展开论述之前,先看两个问题,读者可以带着疑问阅读完本章内容。

问题1:执行以下程序,result_8的值是多少?

ulnt8_t porI=0x5a;

uint8一t resuh_8;

result_8=(~port)》》4;

/*注:uint8_t表示8位无符号整型*/

问题2:执行以下程序,d的值是多少?

uintl6_t a=10;

uin|16_t b=6553l;

uint32_t c=0;

uint32_t d;

d=a+b+c;

/*注:uintl6_t表示16位无符号整型,uint32_t表示32位无符号整型*/

1 数据类型相关的编程风格

规则6.3(推荐):必须用typedef显式标识出各数据

类型的长度和符号特性,避免直接使用标准数据类型。

例如,一个32位的整数系统,可定义如下:

MISRA—C关于数据类型的规则及陷阱有哪些

之所以用intl6_t和uint32_t等代替signed short和unsigned int等标准数据类型标识符,是由于不同的编译器对标准数据类型的长度定义是不一样的。比如说一个16位系统,很可能就把short和int都定义成16位,long定义成32位,这与上文32位系统中标准数据类型的长度就不一致。用intl6_t和uint_32等标识符来定义变量,一方面增加了程序的可读性,使得程序员本人或其他读者都能对程序中数据的具体信息胸有成竹;另一方面也有助于程序在不同系统之间的移植,节省开发时间,减少隐患。规则7 1:不得使用八进制常数(O除外)或八进制转义符。

思考如下数组:

code=109;

code=100;

code=O52

code=O71;

/*注:八进制常数须在最高位加O*/

code的实际值是42(十进制),code的实际值是57(十进制);但估计很多读者会把code认成是52(十进制),code认成是7l(十进制)。

八进制数在C程序中使用的频率远小于十进制数和十六进制数,为了保证程序的可读性和安全性,程序员不允许使用八进制数以及八进制转义符。

2 数据类型转换

如果程序员对数据类型的转换有很清晰的认识,并且在必要的地方做了正确的显式强制转换,那程序是安全的。但有时由于程序员的疏忽,或者是过于相信编译器的“智慧”程度,导致表达式中有很多隐式转换(即没有显式地强制转换),而这些隐式数据类型转换很可能就构成致命的漏洞。MISRA—C中数据类型转换规则的着眼点,即是避免有漏洞的隐式数据转换。

在介绍MISRA—C关于数据类型转换的部分规则之前,先介绍整型操作数的“平衡(balance)”原则。所谓整型操作数“平衡”原则,即对于隐式表达式,编译器会按照既定规则对操作数进行位数扩充,其中int和unsiglled int在整型表达式“平衡”过程中占重要地位。

下面分析一个简单的隐式整型表达式c=a+b(假设a的存储位数不大于b的存储位数),编译器是这样来处理这个表达式的:

如果b是短整型(即位数少于int,比如char、short等)或者整型(int或unsigned int),那a也是短整型或者整型,执行“+”运算之前,a和b都将被扩充为整型(int或者unsigned int),然后相加的结果赋给c(如果c不是int或者unsigned int类型,则这个赋值操作也会包含隐式的扩充或截断操作)。

如果b是长整型(存储位数多于int),则a会被扩充为与b相当的长整型,再执行“+”运算,所得结果赋给c(可能包含隐式的扩充或截断操作)。

绝大部分的操作符用于整型运算的时候,都遵循上述“平衡”原则,比如:算术操作符、位操作符和关系运算符。

但逻辑操作符不遵循上述“平衡”原则。此外左移(》)运算符也不遵循“平衡”原则,只和移位操作符左边的整型操作数相关。假设一个8位的短整型变量值为Oxf5(十六进制),则右移4位所得结果是O xof(十六进制)。

明确了上述背景后,下面来关注本文一开始提出的“问题1”(代码参见前文)。绝大部分拥有嵌人式C程序开发经验的人都明白这段代码的原意是将port的值取反后右移4位赋值给result_8(在用I/O口控制共阳的LED时经常这么做),程序员期望的结果显然是resuIt_8=0xof。然而,由于整型的“平衡”原则,在16位编译器中,~port的值是Oxffa5;在32位编译器中,~pott的值是Oxffffffa5。无论哪种情况,最后结果(右移4位后赋值给result_8的时候有一个截断操作)都是resuIt_8=Oxfa,而非程序员预期的result_8=OxOf。

倘若将最后一行代码改成result一8=((uin8_t)(~port))》》4,则result_8可取得预期的值。

针对以上情况,MISRA-c提出了相应规则。

规则10.5:如果位操作符~和移位操作符》)联合作用于unsigned char或者unsigned short类型的操作数时,中间运算步骤的结果必须立刻显式强制转换为预期的短整型数据类型。

为了加深对“平衡”原则的理解,再来分析一下“问题2”。

如果用一个32位的编译器来编译这段程序,最终结果是d=6554l,程序员“幸运地”得到了预期的结果。如果是16位的编译器,得到的结果却是d=5。

由于“+”运算是左结合的,所以d=a+b+c等效于d=(a+b)+c,即先执行a+b,所得的和再与c相加.最后结果赋值给d。问题就出在a+b这个中间步骤中。由于a和b都是16位整型(注意编译器也是16位的),故而a+b的结果也是16位整型,则a+b的值是Ox0005(有溢出);再扩充为32位整型Ox00000005和c相加赋值给d,d=5,这并非程序员预期的结果。

所以,在16位编译器中,问题2的那段代码很可能导致严重错误。当然,如果程序员用()指定了运算优先级的话,即最后一行代码写成d=a+(b+c),也可以避免上述溢出错误,然而,这终究不是治本的办法。只有明确每一个操作数的实际数据类型,才能保障代码的安全性。

MISRA-C中对于表达式中存在隐式数据类型转换的情况作了严格的限制。

规则10.1:以下情况下,整型表达式中不允许出现隐式数据类型转换。

①整型操作数不是被扩充为更多位数的同符号整数;

②表达式是复杂表达式;

③表达式不是常数表达式,且是函数的参数

④表达式不是常数表达式,且是函数的返回表达式。。

规则10.2:以下情况下,浮点数表达式中不允许出现隐式数据类型转换。

①浮点型操作数不是被扩充为更多位数的同符号浮点数;

②表达式是复杂表达式;

③表达式是函数的参数;

④表达式是函数的返回表达式。

整型表达式规则和浮点数表达式规则基本类似,只是浮点数表达式规则更为苛刻一些,对浮点型的常数也作了严格的限定。

这两条规则中,出现了“复杂表达式”的概念。请注意,MISRA—C中“复杂表达式”的概念和其他介绍C编程规范书籍中“复杂表达式”的概念是不一样的。在MISRA-C中,非“复杂表达式”基本只限制在常数表达式或者函数的返回值。为了明确上述规则中关于“复杂表达式”和“返回表达式”的概念,此处举一例子。定义一个函数uintl6_t foo(void),函数体如下:

uintl6_t foo(void){

return(a+b+c);

函数体中最后一句return(a+b+c)中的a+b+c是返回表达式。倘若在C程序的其他地方有a=foo()这样的语句,则用的是foo()函数的返回值。在MISRA-c中,的资源,完成了采用USB接口技术的热敏打印机的开发,并对打印头作了充分的保护。通过采用相应的算法实现这个赋值表达式不是“复杂表达式”。

至于表达式作为函数参数等情况,碍于篇幅的原因,此处就不再详细展开了。

权衡一下利弊,在涉及到数据类型转换的时候,与其花很大力气去区分一个隐式表达式是否在MISRA—C规则的“黑名单”中,还不如用强制转换符显式地标识出每个操作数的实际数据类型,这是最为稳妥的方法。总而言之,MISRA—C关于数据类型转换规则的中心意思,是要求程序员明确任意一个操作数的实际数据类型。

3 小 结

作为一名优秀程序员,第一步就是以严谨的态度对待程序中的每一个数据,明白任何一个数据操作的关键,从而能写出最清晰易懂而又安全的代码。MISRA—C关于数据类型的规则可保障程序员在迈出这一步的时候不会摔倒。

责任编辑:gt

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 数据
    +关注

    关注

    8

    文章

    6786

    浏览量

    88701
  • 软件
    +关注

    关注

    69

    文章

    4677

    浏览量

    87059
  • 飞行器
    +关注

    关注

    13

    文章

    713

    浏览量

    45443
收藏 人收藏

    评论

    相关推荐

    关于Labview数据类型的扩充

    众所周知,关于C++,C#,Java等高级程序设计语言,大都实现了对STL等泛型编程模块的添加,譬如vector, array, List, queue,iterator...等多种成熟数据类
    发表于 12-15 10:38

    Java基本数据类型之间的运算规则是什么?

    Java基本数据类型Java变量的使用说明Java基本数据类型之间的运算规则
    发表于 11-04 09:59

    C语言的数据类型哪些?

    C语言的数据类型(基础类型+结构体)
    发表于 12-25 07:06

    MISRA C编程规范标准什么规则要求?

    如何衡量代码是否满足某些标准?MISRA C编程规范标准什么规则要求?
    发表于 04-19 07:20

    CODESYS支持的数据类型哪些

    CODESYS支持的数据类型哪些?CODESYS关于数据类型的相关规则是什么?
    发表于 09-30 09:01

    C预处理与C语言基本数据类型

    指令表:注意:宏名的书写由标识符与两边各两条下划线构成。C语言基本数据类型不同操作系统中数据类型所占字节数图解数据类型的其他分类:变量常量(字面量和const常量)void(特殊
    发表于 12-21 08:29

    跨越数据类型的重重陷阱

    数据类型是编程语言中最基本的构成元素,但却是最易被忽略的一环,程序员愿意把几乎100 %的精力都花在算法研究、程序流控制等大环节上,却很少在数据类型问题上反复斟酌。
    发表于 04-22 16:52 9次下载

    结构数据类型(Struct)及应用案例

    Struct数据类型使用非常灵活,随时可以使用,但是相对于PLC数据类型 (UDT) 以下缺点,所以建议需要使用Struct类型时,可以使用PLC
    的头像 发表于 07-27 16:10 1709次阅读

    C语言-基本数据类型与位运算

    这篇文章作为基础知识点,总结C语言的基本数据类型哪些,浮点数的精度,整数变量的空间范围,变量定义语法,变量命名规则,浮点数打印格式,基本数据类型
    的头像 发表于 08-14 09:56 1511次阅读

    关于符号数据类型的示例

    我们学习一下Systemverilog中的符号数据类型的赋值。
    的头像 发表于 10-17 14:40 954次阅读

    MISRA C在安全可靠编程中的地位

    C编程语言的普及,以及它的许多陷阱陷阱,导致了MISRA CC用于高完整性软件的领域取得了巨
    的头像 发表于 11-23 11:55 926次阅读
    <b class='flag-5'>MISRA</b> <b class='flag-5'>C</b>在安全可靠编程中的地位

    Redis的数据类型哪些

    Redis的数据类型哪些?五种常用数据类型:String、Hash、Set、List、SortedSet。以及三种特殊的数据类型:Bit
    的头像 发表于 10-09 10:51 736次阅读

    数据的位是什么?C语言中常见的数据类型哪些?

    本文介绍关于C语言中数据类型的相关知识,比如常见的数据类型哪些,怎么定义和使用数据类型等。 1
    的头像 发表于 11-08 15:55 1350次阅读
    <b class='flag-5'>数据</b>的位是什么?<b class='flag-5'>C</b>语言中常见的<b class='flag-5'>数据类型</b><b class='flag-5'>有</b>哪些?

    oracle的数据类型哪些

    Oracle数据库中有许多数据类型可供选择,每种数据类型都有其各自的特点和适用场景。下面是对Oracle数据库中最常用的数据类型的详尽说明,
    的头像 发表于 12-05 16:45 2205次阅读

    C语言数据类型哪些

    C 语言中,数据类型指的是用于声明不同类型的变量或函数的一个广泛的系统。变量的类型决定了变量存储占用的空间,以及如何解释存储的位模式。
    发表于 03-20 10:56 402次阅读
    <b class='flag-5'>C</b>语言<b class='flag-5'>数据类型</b><b class='flag-5'>有</b>哪些