MISRA C2012学习笔记(9)-Rules 8.14

文章目录

    • 8.14控制语句表达式
      • 规则14.1循环计数器本质上不能是浮点型
      • [Rule 14.2 for 循环应为良好格式](#Rule 14.2 for 循环应为良好格式)
      • [Rule 14.3 控制表达式不得是值不变的](#Rule 14.3 控制表达式不得是值不变的)
      • [Rule 14.4 if 语句和循环语句的控制表达式的基本类型应为布尔型](#Rule 14.4 if 语句和循环语句的控制表达式的基本类型应为布尔型)

8.14控制语句表达式

本节中的一些规则使用了术语循环计数器。循环计数器需要定义为满足以下条件的对象、数组元素或结构体或联合的成员:

  1. 它具有标量类型;
  2. 其值在给定循环实例的每次循环中单调变化;
  3. 参与退出循环的判定。

注意:第二个条件意味着循环计数器的值必须在循环的每次迭代中改变,并且对于给定的循环实例,它必须总是在相同的方向上改变。但是,在不同的实例上,它可能在不同的方向上发生变化,例如,有时向后读取数组的元素,有时向前读取它们。

根据这个定义,一个循环不需要只有一个循环计数器:一个循环可以没有循环计数器,也可以有多个循环计数器 。有关for循环中循环计数器的更多限制,请参见规则14.2

下面的代码片段展示了循环及其对应的循环计数器的示例。

在这个循环中,i是一个循环计数器,因为它具有标量类型,单调变化(递增),并且涉及循环终止条件。

for ( uint16_t i = 0; a[ i ] < 10; ++i )
{
}

下面的循环没有循环计数器。对象计数具有标量类型且单调变化,但不涉及终止条件。

extern volatile bool_t b;
uint16_t  count = 0;
while ( b )
{
  count = count + 1U;
}

以下代码中,i 和 sum 均为标量且单调变化(分别减小和增大)。但 sum 并不是循环计数器,因为它不参与退出循环的判定。

uint16_t sum = 0;
for ( uint16_t i = 10U; i != 0U; --i )
{
  sum += i;
}

在接下来的循环中,p是一个循环计数器。它不涉及循环控制表达式,但它涉及通过break语句退出循环的决定。

extern volatile bool_t b;
extern char *p;
do
{
  if ( *p == '\0' )
  {
    break;
  }
  ++p;
} while ( b );

下面示例中的循环计数器是p->count。

struct s
{
  uint16_t count;
  uint16_t a[ 10 ];
};
extern struct s *p;
for ( p->count = 0U; p->count < 10U; ++( p->count ) )
{
  p->a[ p->count ] = 0U;
}

规则14.1循环计数器本质上不能是浮点型

等级:必要

分析:不可判定,系统范围

适用:C90,C99

原理:当使用浮点循环计数器时,舍入误差的累积可能导致预期迭代次数与实际迭代次数不匹配。当一个不是浮点基数幂的循环步长舍入到一个可以表示的值时,就会发生这种情况。

即使带有浮点循环计数器的循环在一个实现上表现正确,它也可能在另一个实现上给出不同的迭代次数。(不同编译器设置,浮点数取整方式可能不一样

示例:

在下面的违规示例中,在循环结束时counter的值可能不是1 000

uint32_t counter = 0u;
for ( float32_t f = 0.0f; f < 1.0f; f += 0.001f )
{
  ++counter;
}

下面的合规示例使用整数循环计数器来保证 1000 次循环,并在循环内使用它生成 f。

float32_t f; 
 
for (uint32_t counter = 0u; counter < 1000u; ++counter) 
{ 
f = (float32_t)counter * 0.001f; 
} 

下面的 while 循环违规,因为浮点数 f 被用作循环计数器

float32_t f = 0.0f; 
 
while (f < 1.0f) 
{ 
f += 0.001f; 
} 

下面的 while 循环合规,因为浮点数 f 未被用作循环计数器。

float32_t f; 
uint32_t u32a; 
f = read_float32(); 
do 
{ 
u32a = read_u32(); 
/* f 在循环内未被修改, 所以不作为循环计数器处理 */ 
} while (((float32_t)u32a - f) > 10.0f); 

解读:该规则需要被实施,不过应该一般编程中都不会使用浮点数作为循环的变量

Rule 14.2 for 循环应为良好格式

等级:必要

分析:不可判定,系统范围

适用:C90,C99

展开:for 循环语句包含三个子句,此规则对它们的要求为:第一个子句

不得为空 ,或

◆ 应为循环计数器分配一个值,或

◆ 应定义并初始化 循环计数器(C99)。

第二个子句

◆ 应该是没有持久副作用的表达式,并且

◆ 应使用循环计数器和可选的循环控制标志,并且

◆ 不得使用在 for 循环主体中修改的任何其他对象。

第三个子句

◆ 应该是一个表达式,其唯一的持久副作用是修改循环计数器 的值,并且

◆ 不得使用在 for 循环主体中修改 的对象。

for循环中只能有一个 循环计数器,不能在for循环体中修改。

循环控制标志定义为:在第二个子句使用的基本型为布尔型 的对象的单个标识符。

for 循环主体的行为包括了在该主体内调用的所有函数的行为

原理:for 循环语句提供了一种通用循环工具。使用形式受限的循环可使代码更易于查看和分析

例外:三个子句都可以为空 ,例如 for (;; ),以便允许无限循环。

示例:在下面的 C99 示例中,i 是循环计数器,flag 是循环控制标志。

bool_t flag = false;
for ( int16_t i = 0; ( i < 5 ) && !flag; i++ )
{
  if ( C )
  {
    flag = true;                /* 合规 - 允许提前终止循环                */
  }
  i = i + 3;                    /* 违规 - 循环主体内不允许改变循环计数器   */                     
}

解读:for循环格式必须要遵守,该规则需要被实施

Rule 14.3 控制表达式不得是值不变的

等级:必要

分析:不可判定,系统范围

适用:C90,C99

展开:此规则适用于:

◆ if,while,for,do...while 和 switch 语句的控制表达式;

◆ ?:运算符的第一个操作数。

原理:如果控制表达式的值不变,则意味着可能存在编程错误 。因存在不变表达式而无法到达的任何代码都可以由编译器删除。例如,这可能会导致从可执行文件中删除防御性代码。

例外:

  1. 允许使用用于创建无限循环的不变的控制表达式。
  2. 允许 do...while 循环使用值为 0 的布尔型控制表达式。

示例

s8a = ( u16a < 0u ) ? 0 : 1;/* 违规- u16a总是>= 0 */
{ 
} 
if ( u16a <= 0xffffu )
{
                                /* 违规 - always true      */
}
if ( 2 > 3 )
{
                                /* 违规 - always false     */
}
for ( s8a = 0; s8a < 130; ++s8a )
{
                                /* 违规 - always true      */
}
if ( ( s8a < 10 ) && ( s8a > 20 ) )
{
                                /* 违规 - always false     */
}
if ( ( s8a < 10 ) || ( s8a > 5 ) )
{
                                /* 违规 - always true      */
}
while ( s8a > 10 )
{
  if ( s8a > 5 ) 
  {
                                /* 违规 - s8a not volatile */
  }
}
while ( true )
{
  /* 例外1,合规 */
}
do
{
  /* 例外2,合规 */
} while ( 0u == 1u );
const uint8_t numcyl = 4u;
/*
 * 违规 - 条件一直满足
 */
if ( numcyl == 4u )
{
}
const volatile uint8_t numcyl_cal = 4u;
/*
 * 合规- volatile 属性可能导致外部修改其值
 */
if ( numcyl_cal == 4u )
{
}
uint16_t n;     /* 10 <= n <= 100 */
uint16_t sum;
sum = 0;
for ( uint16_t i = ( n - 6u ); i < n; ++i )
{
  sum += i;
}
if ( ( sum % 2u ) == 0u )
{
  /*
   * 违规 - sum是6个连续的非负整数的和, 因此必为奇数, if语句的控制表达式将始终为false  
   */
}

解读:该规则需要被实施,可以检查出异常的控制代码

Rule 14.4 if 语句和循环语句的控制表达式的基本类型应为布尔型

等级:必要

分析:可判定,单一编译单元

适用:C90,C99

展开:for 循环语句的控制表达式是可选的。 此规则不要求表达式存在,但如果存在,则要求表达式的基本类型为布尔型

原理:强类型要求if语句或迭代语句的控制表达式本质上具有布尔类型

示例:

int32_t *p, *q;
while ( p )           /* 违规 - p 为指针 */
{
}
whil e ( q != NULL )   /* 合规 */
{
}
while ( true )        /* 合规 */
{
}
extern bool_t flag;
while ( flag )        /* 合规 */
{
}
int32_t i;
if ( i )              /* 违规                   */
{
}
if ( i != 0 )         /*  合规 */
{
}

解读:该规则需要被实施,但在检查过程中,可能由于bool量无法被正确识别,导致可能会误报出该项。

相关推荐
SSL_lwz15 分钟前
P11290 【MX-S6-T2】「KDOI-11」飞船
c++·学习·算法·动态规划
垂杨有暮鸦⊙_⊙15 分钟前
阅读《先进引信技术的发展与展望》定装和探测部分_笔记
笔记
weixin_4786897624 分钟前
【二分查找】【刷题笔记】——灵神题单1
笔记
醉陌离1 小时前
渗透测试学习笔记—shodan(2)
笔记·学习
双手插兜-装高手1 小时前
Linux - 线程基础
linux·c语言·笔记
ZZZ_O^O2 小时前
【动态规划-卡特兰数——96.不同的二叉搜索树】
c++·学习·算法·leetcode·动态规划
澜世2 小时前
2024小迪安全基础入门第二课
网络·笔记·安全
冷心笑看丽美人2 小时前
Spring 框架七大模块(Java EE 学习笔记03)
学习·spring·架构·java-ee
huaqianzkh2 小时前
学习C#中的BackgroundWorker 组件
开发语言·学习·c#
清酒伴风(面试准备中......)2 小时前
操作系统基础——针对实习面试
笔记·面试·职场和发展·操作系统·实习