MISRA C2012学习笔记(8)-Rules 8.13

文章目录

    • 8.13 副作用(Side effects)
      • Rule 13.1 初始化程序列表不得包含持久性副作用
      • Rule 13.2 在所有合法的评估命令下,表达式的值应与其持续的副作用相同
      • Rule 13.3 包含自增(++)或自减(--)运算符的完整表达式,除由自增或自减运算符引起的副作用外,不应有其他潜在的副作用
      • Rule 13.4 不得使用赋值运算符的结果
      • Rule 13.5 逻辑与(&&)和逻辑或(||)的右操作数不得含有持久性副作用
      • Rule 13.6 sizeof 运算符的操作数不得包含任何可能产生副作用的表达式

8.13 副作用(Side effects)

Rule 13.1 初始化程序列表不得包含持久性副作用

等级:必要

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

适用:C99

原理:C90标准将聚合类型的自动对象的初始化程序有约束,使其仅包含常量表达式。但是,C99允许自动聚合初始化器包含在运行时求值的表达式。它还允许作为匿名初始化对象的复合字面量。在初始化程序列表中的表达式求值过程中,副作用的发生顺序是不确定的,因此,如果这些副作用持续存在,则初始化行为是不可预测的
示例:

volatile uint16_t v1;
void f ( void )
{/* 违规 - 易失性访问是持久的副作用 */uint16_t a[ 2 ] = { v1, 0 };
}
void g ( uint16_t x, uint16_t y )
{/* 合规 - 无副作用                               */uint16_t a[ 2 ] = { x + y, x - y };
}
uint16_t x = 0u;
extern void p ( uint16_t a[ 2 ] );
void h ( void )
{/* 违规 - 两个副作用                         */p ( ( uint16_t[ 2 ] ) { x++, x++ } );
}

解读:全局变量不能作为初始化赋值,可能导致意外的初始化。该规则有必要检查

Rule 13.2 在所有合法的评估命令下,表达式的值应与其持续的副作用相同

等级:必要

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

适用:C90,C99

展开:在任何两个相邻序列点之间或在任何完整表达式内:

1.不得修改任何对象超过一次;

2. 不得修改和读取对象,除非对对象值的任何此类读取对计算要存储到对象中的值有帮助;

3. 最多只能有一个易失性(volatile)限定类型的修改访问权限;

4. 易失性(volatile)限定类型的读取访问不得超过一个。

注意:可以通过指针或被调用的函数间接访问对象,也可以通过表达式直接访问对象。

注意:此展开有意描述的比规则的标题严格。作为其结果,表达式:

  x = x = 0; 

即使值和持久副作用(只要x不是易失性的)与求值顺序或副作用无关,也不允许使用此规则。

序列点在C90和C99标准的附录C中进行了总结。C90序列点是C99序列点的一个子集。

完整表达式的定义见C90标准第6.6节和C99标准第6.8节。

原理:该标准在评估表达式时为编译器提供了相当大的灵活性。 大多数运算符可以按任何顺序对其操作数求值。 主要的例外是:

•逻辑与&&仅当第一个操作数的计算结果为非零时才计算第二个操作数;

•逻辑或:只有当第一个操作数的值为零时,第二个操作数才会被求值;

•条件操作符:总是先求第一个操作数,然后再求第二个或第三个操作数;

•先对第一个操作数求值,再对第二个操作数求值的操作符。

注意:括号的存在可能会改变操作符的应用顺序。然而,这并不影响最低层操作数的求值顺序,它们可以按任何顺序求值。

许多与表达式求值相关的不可预测行为的常见实例可以通过遵循规则13.3和规则13.4给出的建议来避免。

示例:

此违规示例中,调用 COPY_ELEMENT 宏时,i 被读取两次并被修改两次。 无法确定 i 的操作顺序是否为:

•读取、修改、读取、修改或

•读取,读取,修改,修改。

#define COPY_ELEMENT ( index ) ( a[( index )] = b[( index )] )
COPY_ELEMENT ( i++ );

此违规示例中,未指定 v1 和 v2 的读取顺序。

extern volatile uint16_t v1, v2; 
uint16_t t; t = v1 + v2; 

此合规示例中,将读取并修改 PORT。

extern volatile uint8_t PORT; PORT = PORT & 0x80u; 

此违规示例中展示的,未指定函数参数的评估顺序,副作用的发生顺序也无法确定。

uint16_t i = 0; /* 
* 无法确定此次调用是否等效于: 
* f(0, 0) 
* 或 f(0, 1) 
*/  
f(i++, i);

函数指示符和函数实参的相对求值顺序是未指定的,在这个不兼容的示例中,如果调用g修改了p的值,则不确定函数指示符p->f是在调用g之前还是之后使用p的值。

p->f(g(&p)); 

解读:表达式的计算或者调用应该清楚表明,上述违规项不仅不便于阅读,而且很容易出错。该规则需要被执行。

Rule 13.3 包含自增(++)或自减(–)运算符的完整表达式,除由自增或自减运算符引起的副作用外,不应有其他潜在的副作用

等级:建议

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

适用:C90,C99

展开: 函数调用在本规则被认为是一种副作用。(具体见下面示例描述)

为了本规则的目的,完整表达式的所有子表达式都被视为已求值,即使标准指定不求值。

原理:

不建议将自增和自减操作符与其他操作符结合使用,因为:

•它会严重损害代码的可读性;

•它在语句中引入了额外的副作用,可能导致未定义的行为
(见规则13.2)。

将这些操作与其他操作符隔离使用会更清楚。

示例:

下面的表达式违规:

u8a = u8b++

下面的违规表达式:

u8a = ++u8b + u8c--; 

按下述顺序编写时,其行为会更清晰:

++u8b; 
u8a = u8b + u8c; 
u8c--; 

以下均为合规示例,因为每个表达式中唯一的副作用是由自增或自减运算符引起的。

x++; 
a[i]++; 
b.x++; 
c->x++; 
++(*p); 
*p++; 
(*p)++; 

以下示例均违规,因为它们同时包含函数调用以及自增或自减运算符:

if ( ( f ( ) + --u8a ) == 0u )
{
}
g ( u8b++ );

即使不计算包含自增或自减操作符或其他副作用的子表达式,以下代码也都是违规的:

u8a = (1u == 1u) ? 0u : u8b++; if (u8a++ == ((1u == 1u) ? 0u : f())) 
{ 
} 

解读:自增或者自减运算不应该与其他表达式一起使用,可能导致意外的行为。该规则应被检查

Rule 13.4 不得使用赋值运算符的结果

等级:建议

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

适用:C90,C99

展开:即使包含赋值运算符的表达式不被评估,此规则也适用

原理 :我们不建议将赋值运算符(简单或复合)与其他算术运算符结合使用,因为:

•它会严重损害代码的可读性;

•它引入了额外的副作用,这是在规则13.2所涵盖的未定义的行为中引入的。

示例:

x = y;                          /* 合规 */
a[ x ] = a[ x = y ];             /* 违规 - x = y 的值被使用                       */
/*
* 违规 - bool_var = false 的结果被使用 
* 可能 bool_var == false 才是本意*/
if ( bool_var = false )
{
}
/* 违规 - 即使 bool_var = true 不会被评估            */
if ( ( 0u == 0u ) || ( bool_var = true ) )
{
}
/* 违规 - x = f() 的结果被使用                              */
if ( ( x = f ( ) ) != 0 )
{
}
/* 违规 - b += c 的结果被使用                              */
a[ b += c ] = a[ b ];
/* 违规 - c = 0 和 b = c = 0 的结果被使用                */
a = b = c = 0;

解读:赋值运算的结果不应该直接被其他地方使用,一方面不符合习惯,另一方面代码不好理解。这种操作一般都是代码写错导致的。是需要被检查的。

Rule 13.5 逻辑与(&&)和逻辑或(||)的右操作数不得含有持久性副作用

等级:必要

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

适用:C90,C99

原理:逻辑运算符“&&”和“||”的右侧操作数是否求值取决于左侧操作数的值。如果右侧操作数包含副作用,则可能会发生与程序员期望相反的那些副作用

如果对右操作数求值会产生副作用,而这些副作用在程序中出现表达式的地方并不持久,那么是否对右操作数求值并不重要。

“持续性副作用”一词的定义见附录J。

示例:

uint16_t f ( uint16_t y )
{/* 这些副作用仅调用者可见, 所以他们不持久 */uint16_t temp = y;tem p = y + 0x8080U;return temp;
}
uint16_t h ( uint16_t y )
{static uint16_t temp = 0;/* 此为持久副作用                              */temp = y + temp;return temp;
}
void g ( void )
{/* 合规 - f() 具有非持久副作用            */if ( ishigh && ( a == f ( x ) ) ){}/* 违规 - h() 具有持久副作用           */if ( ishigh && ( a == h ( x ) ) ){}
}
volatile uint16_t v;uint16_t x;
/* 违规 - 对易失性(volatile)对象 v 的访问属于持久副作用 */
if ( ( x == 0u ) || ( v == 1u ) )
{
}
/* 如果fp指向的是具有持久副作用的函数, 此处就是违规的 */
( fp != NULL ) && ( *fp ) ( 0 );

解读:&&或||运算的右操作数不能具有持久副作用,一般都不会违反规则。需要进行检查。

Rule 13.6 sizeof 运算符的操作数不得包含任何可能产生副作用的表达式

等级:强制

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

适用:C90,C99

展开:sizeof 运算符的操作数中出现的任何表达式通常都不会被求值。此规则要求对任何此类表达式的评估都不应包含副作用,无论是否实际对其进行评估。

在此规则中,函数调用被认为是副作用。

原理:sizeof 运算符的操作数可以是表达式,也可以某个指定的类型。如果操作数包含一个表达式,则有可能发生了编程错误:当在大多数情况下实际上不对表达式进行求值时,程序员期望该表达式被求值。

C90 标准指出,在运行时不会评估出现在操作数中的表达式。

C99 标准中,通常不在运行时评估在操作数中出现的表达式。但是,如果操作数包含长度可变的数组类型,则将在必要时对数组大小表达式进行求值。如果可以在不评估数组大小表达式的情况下确定结果,则不确定是否评估结果。
例外

在 sizeof(V)表达式中,若 V 是“volatile”修饰的非变长数组类型的左值,这种情况是被认可的。
示例 :

volatile int32_t i;int32_t j;size_t  s;
s = sizeof ( j );         /* 合规 */
s = sizeof ( j++ );       /* 违规,使用表达式        */
s = sizeof ( i );         /* 合规 - 例外 */
s = sizeof ( int32_t );   /* Compliant             */

在此示例中,最终的 sizeof 表达式说明了可变长度数组size 表达式如何对类型的大小没有影响。操作数的类型为“指向形参为 int32_t 型长度为 v 的数组的函数的 n 个指针的数组”。由于操作数具有可变长度数组类型,因此将对其求值。但是,n个函数指针的数组大小不受那些函数指针类型的参数列表的影响。因此,是否会评估易失性的合格对象(volatile-qualified object)v 并不确定,并且是否出现其副作用也不确定

volatile uint32_t v; void f(int32_t n) 
{ size_t s; 
s = sizeof(int32_t[n]); /* 合规 */ 
s = sizeof(int32_t[n++]); /* 违规 */ 
s = sizeof(void (*[n])(int32_t a[v])); /* 违规 */ 
} 

解读:sizeof用来求字节长度,一般不会使用表达式。如果使用的话,需要进行评审。该规则需要进行检查

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/53168.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

QT QGraphicsView实现预览图片显示缩略图功能

QT QGraphicsView实现预览图片显示缩略图功能QT creator Qt5.15.2 头文件&#xff1a; #ifndef TGRAPHICSVIEW_H #define TGRAPHICSVIEW_H#include <QGraphicsView> #include <QMainWindow> #include <QObject> #include <QWidget>class TGraphicsVie…

TCP的传输速度

如何确定TCP最大传输速度&#xff1f; TCP 的传输速度&#xff0c;受限于发送窗⼝&#xff0c;接收窗⼝以及⽹络设备传输能⼒。 其中&#xff0c;窗⼝⼤⼩由内核缓冲区⼤⼩决定。如果缓冲区与⽹络传输能⼒匹配&#xff0c;那么缓冲区的利⽤率就达到了最⼤化。 如何计算网络传…

vue transition组件

可能不生效的几个注意点 选择器的优先级谨慎合并样式 显示三阶段和隐藏三阶段的class名 1、vue2中显示的初始阶段类名是&#xff1a;v-enter&#xff1b;隐藏的初始阶段类名是&#xff1a;v-leave2、v-enter-active、v-leave-active这两个 class 可以被用来定义动画的持续时间…

设计模式1:C#开发中使用创建型的工厂模式和行为型的策略模式

一、接口设计的好处 三大好处&#xff1a;解耦、可复用、可扩展。 二、简单工厂模式 【三要素】能创建具体产品的工厂、抽象产品&#xff08;接口&#xff09;、具体产品 【基本用法】字符串>创建对象>调用其方法 // 产品接口 public interface IProduct {void Opera…

应用开发---VTK放大镜(区域放大)功能实现

VTK 医学图像处理---放大镜/区域放大功能 本博文主要内容为:实现放大镜的源代码;实现思路;具体代码说明。 目录 VTK 医学图像处理---放大镜/区域放大功能 简介: 1 放大镜源代码 1 wxInteractorStyleImage 类源代码 2 wxMagnifierAcotor类源代码 3 Magnifier.cpp 源…

速盾:低成本防御DDoS最佳方案是高防cdn吗?

随着互联网的快速发展&#xff0c;网络攻击也变得越来越普遍和严重。分布式拒绝服务攻击&#xff08;DDoS&#xff09;是一种常见的网络攻击方式&#xff0c;它利用大量的请求来淹没目标服务器&#xff0c;使其无法正常工作。为了保护服务器免受DDoS攻击的影响&#xff0c;使用…

2024高教杯数学建模A题思路

问题1:舞龙队沿螺距为55 cm 的等距螺线顺时针盘入 分析: 龙头速度:龙头前把手的行进速度始终保持1 m/s。螺线参数:螺距为55 cm,即0.55 m。初始条件:龙头位于螺线第16圈A点处。思路: 确定螺线方程:根据螺线的性质,建立极坐标方程,表示螺线各点的位置。计算时间步长:…

Android 打开 GBK项目如何设置成UTF-8

1.标题 今天打开一个eclipse老项目&#xff0c;编码格式为GBK&#xff0c;Android studio导入项目报错&#xff0c;本人想到一个方案就是批量修改文件格式从 GBK到 UTF-8&#xff0c;这样可以一键解决问题 2.开发脚本 使用前请备份代码 使用前请备份代码 使用前请备份代码…

构建STM32智能平衡车项目:PID控制算法与蓝牙通信技术

一、项目概述 项目目标和用途 本项目旨在设计和实现一款基于STM32单片机的平衡车。平衡车是一种新型的个人交通工具&#xff0c;广泛应用于短途出行、休闲娱乐等场景。通过本项目&#xff0c;我们希望能够实现一款具备良好稳定性和操控性的平衡车&#xff0c;能够在不同的地形…

vue-组件传值总结

Vue.js中实现组件间传值的方法有多种。以下是几种常见的传值方式的详细讲解和示例&#xff1a; 1.父组件向子组件传值&#xff08;props&#xff09; 父组件通过props向子组件传递数据&#xff0c;子组件可以接收并使用这些数据。当父组件重新渲染时&#xff0c;数据会被覆盖…

Python Opencv鼠标回调

使用 OpenCV 的 cv2.setMouseCallback() 方法来捕捉鼠标事件&#xff0c;并实现以下功能&#xff1a; 实时在鼠标指针附近显示其位置的像素坐标。通过左键双击&#xff0c;将像素坐标记录到数组中。通过右键点击&#xff0c;取消上一次添加的坐标。 下面是实现代码的示例&…

NLP从零开始------文本中阶处理之序列到序列模型(完整版)

1. 序列到序列模型简介 序列到序列( sequence to sequence, seq2seq) 是指输入和输出各为一个序列(如一句话) 的任务。本节将输入序列称作源序列&#xff0c;输出序列称作目标序列。序列到序列有非常多的重要应用&#xff0c; 其中最有名的是机器翻译( machine translation), 机…

WebRTC协议下的视频汇聚融合技术:EasyCVR视频技术构建高效视频交互体验

视频汇聚融合技术是指将来自不同源、不同格式、不同网络环境的视频流进行集中处理、整合和展示的技术。随着视频监控、远程会议、在线教育、直播娱乐等领域的快速发展&#xff0c;视频数据的规模急剧增长&#xff0c;对视频处理能力和效率提出了更高要求。视频汇聚融合技术通过…

思科IP访问控制列表3

#网络安全技术实现# #任务三扩展访问控制列表的控制3# #1配置计算机的IP 地址、子网掩码和网关 #2配置Switch-A的主机名称&#xff0c;创建vlan 10,20,30,并将Fa0/1划入vlan 10&#xff0c;Fa0/2划入vlan 20&#xff0c;G0/1划入vlan 30 Switch(config)#hostname Switch-A S…

「OC」iOS事件处理流程

「OC」初识iOS事件处理流程 文章目录 「OC」初识iOS事件处理流程触摸事件触摸事件的响应周期事件 响应者UIEventUITouchUIResponder 触摸流程系统响应阶段APP响应阶段寻找最佳响应者 构成响应链 寻找最佳响应者和响应链的区别总结参考资料 触摸事件 iOS的事件有好几种&#xf…

DriveLM的baseline复现

DriveLM是一篇很有意思的工作&#xff0c;把自动驾驶跟MLLM结合到一起了&#xff0c;实现端到端的感知or决策规划。 Repo&#xff1a;https://github.com/OpenDriveLab/DriveLM 该工作是基于nuScenes数据集做的&#xff0c;官方paper里给出了数据的具体构建方式&#xff0c;感…

ElasticSearch-关联关系

Elasticsearch并不擅长处理关联关系&#xff0c;一般会采用以下四种方法处理关联 对象类型嵌套对象 (Nested Object)父子关联关系 (Parent / Child)应用端关联 对象类型 在每一博客的文档中都保留作者的信息 如果作者信息发生变化&#xff0c;需要修改相关的博客文档 包含对象…

SpringBoot依赖之Spring Boot DevTools热部署开发增效工具

摘要&#xff1a;Spring项目又大又重&#xff0c;依赖多&#xff0c;编译启动慢&#xff0c;怎么提高研发效率呢&#xff1f;方法之一热部署&#xff01; 概念 Spring Boot DevTools 依赖名称: Spring Boot DevTools功能描述: Provides fast application restarts, LiveRelo…

softmax里边的exp用拟合验证精度。

文章目录 要验证Softmax函数中的指数运算&#xff08;exp函数&#xff09;对精度的影响&#xff0c;可以通过拟合一个函数来近似Softmax函数&#xff0c;并比较两者的输出结果。 import numpy as np import matplotlib.pyplot as plt# Softmax函数 def softmax(x):e_x np.exp…

25k的自动化测试面试题,原来都是这样~

小编热衷于收集整理资源&#xff0c;记录踩坑到爬坑的过程。希望能把自己所学&#xff0c;实际工作中使用的技术、学习方法、心得及踩过的一些坑&#xff0c;记录下来。也希望想做软件测试的你一样&#xff0c;通过我的分享可以少走一些弯路&#xff0c;可以形成一套自己的方法…