C语言再学习 -- 编程规范

C语言编程规范这部分一直想总结一下。现在终于付诸行动了。
其实之前讲过一些面试题,参看:嵌入式面试知识点总结 – C语言篇

里面已经有包含一部分了,比如《高质量C++ C编程指南》.林锐着.pdf。

此次主要参考 华为技术有限公司c语言编程规范MISRA C2012 再详细讲一下C语言的编程规范。

下载:编程规范
在这里插入图片描述
这篇文章主要以MISRA C 2012 中文版为基础,再将华为C语言编程规范融入其中。
看来之前总结两年的 C语言再学习 专栏,又有用武之地了。

一、名词解释

1、声明(declare)和定义(define)

参看:C语言再学习 – 声明与定义

声明一个变量只是将变量名标识符的有关信息告诉编译器,使编译器“认识”该标识符,但声明不一定引起内存的分配。而定义变量意味着给变量分配内存空间,用于存放对应类型的数据,变量名就是对相应的内存单元的命名。在C/++程序中,大多数情况下变量声明也就是变量定义,声明变量的同时也就完成了变量的定义,只有声明外部变量时例外。函数类似,声明只是告诉编译器有这个名称、类型的函数,而定义则是函数的真实实现。

简单一句话,定义创建了对象并为这个对象分配了内存,声明没有分配内存

以下这些就是声明

extern int bar;
extern int g(int, int);
double f(int, double); // 对于函数声明,extern关键字是可以省略的。
class foo; // 类的声明,前面是不能加class的。

与上面的声明相应的定义如下:

int bar;
int g(int lhs, int rhs) {return lhs*rhs;} 
double f(int i, double d) {return i+d;} 
class foo {};

2、连接/链接(linkage)

参看:C语言再学习 – 存储类、链接
分为三类,外部连接(链接)(external linkage)、内部连接(链接)(internal linkage)和无连接(链接)(no linkage)。

  • 外部连接(链接)(external linkage):对于变量,即无“static”修饰的全局可访问的变量;对于函数,即无“static”修饰的全局可调用的函数。它们即使没有在头文件中用“extern”做外部声明,仍然被识别为外部连接(链接)(external linkage)。
  • 内部连接(链接)(internal linkage):即由“static”修饰的全局变量和函数,它们尽可在所在文件内访问和调用,无法被全局访问/调用。
  • 无连接(链接)(no linkage):即函数内部变量。所有函数都是有连接(链接,linkage)的。内部变量包含临时变量和静态变量两种,它们的共同特征是均无法在本函数外被访问。

外部链接:
一个具有外部链接的变量可以在一个多文件程序的任何地方使用。

int n = 5;  /*文件作用域,外部链接,未使用 static */
int main (void)
{...return 0;
}

内部链接:
一个具有内部链接的变量可以在一个文件的任何地方使用。

static int dodgers = 3; /*文件作用域,内部链接,使用了 static ,该文件所私有*/
int main (void)
{...return 0;
}

空链接:
具有代码块作用域或者函数原型作用域的变量有空链接,意味着它们是由其定义所在的代码块或函数原型所私有的。

double blocky (double cleo)  
{double patcrick = 0.0;    /*代码块作用域,空链接,该代码块所私有*/int i;for (i = 0; i < 10; i++){double q = cleo * i;   /*q作用域的开始*/...patrick * = q;         }                        /*q作用域的结束*/return patrick;
}

3、对象(object)

本规范的编制,具有普适性,故会出现如“对象”、“类”这些标准 C 中不提及的概念,对象在 C 语言中的直接对应是变量。当前对象不仅仅是变量,但本译文仅限考虑标准 C(准确的说是嵌入式 C),故不过多描述,我们将其当成“变量”理解即可。

二、规则

每条 MISRA C 准则都可以被归类为“规则”或“指令”。

  • 规则:仅对源代码进行分析,即可对规则进行合规性判定,静态分析工具应该具有判定规则的能力,不需要结合人工判定。
  • 指令:仅对源代码进行分析,无法对指令进行合规性判定,往往需要结合设计文档或开发人员的经验进行综合判定,静态分析工具可能提供辅助,但不同性能的工具提供的解释可能大不相同。

MISRA C2012将规则和指令均分为三个级别:

  • 强制类:必须满足;
  • 必要类:应该满足,若不满足应该说明原因;
  • 建议类:应该满足,若不满足应该说明原因。

1、标准 C 环境(A standard C environment)

Rule 1.1 程序不得违反标准 C 语法和约束,并且不得超出具体实现的编译限制

  • 级别:必要
  • 解读:程序应仅使用所选标准版本中指定的 C 语言及其库的功能
  • 示例:比如你用C90的编译器你就要符合C90的规则和特性,而不能去使用到C11的特性,其实即使你使用了,编译器也不支持,但不一定都能检测出来,具有一定的风险。

Rule 1.2 不应该使用语言扩展

  • 级别:建议
  • 解读:不要用编程语言扩展属性,否则会降低程序的可移植性
  • 示例: if ((NULL != FuncPointer) && (*FuncPointer())),这样的语句是符合语法的,且就是利用了“一旦确定结果立即停止评估”的特性,在 FuncPointer 值为
    NULL 时执行“&&”的右操作数会非常危险,程序会跑到哪完全不可预知。

Rule 1.3 不得发生未定义或严重的未指定行为

  • 级别:必要
  • 解读:一些未定义或未指定的行为有特定的规则处理。此规则意在防止其他未定义和关键的未指定行为。MISRA C 的许多准则旨在避免某些未定义和未指定的行为。 例如,遵守 Rule 11.4、Rule 11.8 和 Rule 19.2 的所有内容可确保在
    C 中不能创建指向使用 const 限定类型声明的对象的非 const 限定指针。这避免了 C90 [Undefined 39]和 C99
    [Undefined 61]。

2、未使用的代码(Unused code)

Rule 2.1 项目不得包含不可达代码(unreachable code)

  • 级别:必要
  • 解读:如果一个程序没有表现出任何未定义的行为,那么无法到达的代码就不能被执行,也不能对程序的输出产生任何影响。因此,无法到达的代码的存在可能表明程序逻辑中的错误。
    无法到达的代码会占用目标机器的内存空间,可能会导致编译器在围绕无法到达的代码传输控制时选择更长的、更慢的跳转指令。而且在循环中,它可以防止整个循环驻留在指令缓存中。
  • 示例:switch中某个case分支是永远运行不到的,程序员应该删除这种代码。
enum light { red, amber, red_amber, green };
enum light next_light ( enum light c )
{enum light res;switch ( c ){case red:res = red_amber;break;case red_amber:res = green;break;case green:res = amber;break;case amber:res = red;break;default:{/* 当参数 c 的值不是枚举型 light 的成员时, 此 default 分支才可达 */error_handler ( );break;}}return res;res = c; /* 违规 - 此语句肯定不可达 */
}

Rule 2.2 不得有无效代码(dead code)

  • 级别:必要
  • 解读:任何可以删除掉但是不影响程序正常运行的代码都是无效代码,由于无效代码可能被编译器删除,所以它的存在可能会引起混乱。
  • 示例: 函数 g 不包含无效代码,且其本身也不是无效代码,因为它不含任何操作。但是对它的调用无效,因为删除它不影响程序行为。
void g(void)
{/* 合规 - 此函数中无任何操作 */
}
void h(void)
{g(); /* 违规 - 该调用可以被移除 */
}

Rule 2.3 项目不应包含未被使用的类型(type)声明

  • 级别:建议
  • 解读:如果声明了类型但没有使用,那么审阅者就不清楚该类型是冗余的还是错误地未使用。
  • 示例:
int16_t unusedtype(void)
{typedef int16_t local_Type; /* 违规 */return 67;
}

Rule 2.4 项目不应包含未被使用的类型标签(tag)声明

  • 级别:建议
  • 解读:如果一个类型标签被声明但从未被使用过,对于审阅者来说,无法确定该类型标签是多余的还是被错 误闲置的。
  • 示例:类型标签 record_t 仅在 record1_t 的类型声明中使用,而在需要使用该类型的位置均使用了 record1_t。此时,我们可以以省略标签的方式声明类型以满足本规则要求,如 record2_t。
typedef struct record_t /* 违规 */
{uint16_t key;uint16_t val;
} record1_t;
typedef struct /* 合规 */
{uint16_t key;uint16_t val;
} record2_t;

Rule 2.5 项目不应包含未被使用的宏(macro)声明

  • 级别:建议
  • 解读:如果一个宏被声明但从未被使用过,对于审阅者来说,无法确定该宏是多余的还是被错误闲置的。
  • 示例:
void use_macro(void)
{#define SIZE 4#define DATA 3 /* 违规 - DATA 未被使用 */use_int16(SIZE);
}

Rule 2.6 函数不应包含未被使用的执行标签(label)声明

  • 级别:建议
  • 解读:如果一个执行标签(label)被声明但从未被使用过,对于审阅者来说,无法确定该执行标签是多余的还是被错误闲置的。
  • 示例:
void unused_label(void)
{int16_t x = 6;
label1: /* 违规 */use_int16(x);
}

tag 和 label,两者翻译为中文都是标签,差别在于tag为枚举、结构体、联合体类型的标签,label为goto语句执行目的地的标签,本文中为区分,将它们分别描述为了类型标签与执行标签。

Rule 2.7 函数中不应有未使用的变量

  • 级别:建议
  • 解读:绝大多数函数都将使用它们所定义的每一个参数。如果函数中的参数未被使用,则可能函数的实现与其预期定义不匹配。本规则强化描述了这一潜在的不匹配。
  • 示例
void withunusedpara(uint1 6_t *para1, int16_t unusedpara) /* 违规 - 参数未使用 */
{*para1 = 42U;
}

3、注释(Comments)

Rule 3.1 字符序列“/*”和“//”不得在注释中使用

  • 级别:必要
  • 解读:“/”和“//”均为注释起始的字符序列,如果在一段由“/”起始的注释中,又出现了“/”或“//”,那么很可能是由缺少“/”引起的。如果这两个注释起始的字符序列出现在由“//”起始的注释中,则很可能是因为使用“//”注释掉了代码。
  • 示例:在下面 C99 代码的示例中,“//”的出现改变了程序的含义:
x = y // /*+ z// */;

此示例得出的结果是 x=y+z,但在没有两个“//”的情况下,结果是 x=y。

Rule 3.2 “//”注释中不得使用换行(即“//”注释中不得使用行拼接符“\”)

  • 级别:必要
  • 解读:如果包含“//”注释的源代码行在源字符集中以“\”字符结尾,则下一行将成为注释的一部分。 这可能会导致意外删除代码。
  • 示例:在下面的违规示例中,包含 if 关键字的物理行在逻辑上是前一行的一部分,因此是注释。
extern bool_t b;
void f(void)
{uint16_t x = 0; // comment \if (b){++x; /* if 语句被作为注释处理, 这里无条件执行 */}
}

参看:C语言再学习 – 关于注释

**面试题:**以下注释哪条是错误的??

#include <stdio.h>int main (void)
{int /*...*/i;char* s = "abcd  //efg";//hello \world!//in/*...*/t i;return 0;

前三条注释都是对的,有没有想到。

4、字符集和词汇约定(Character sets and lexical conventions)

Rule 4.1 八进制和十六进制转译序列应有明确的终止识别标识

  • 级别:必要
  • 解读:若八进制或十六进制转译序列后跟随其他字符,会造成混淆。例如,字符串“\x1f”仅由一个字符组成,而字符串“\x1g”则是由两个字符“\x1”和“g”组成。如果给字符常量或字符串文字中的每个八进制或十六进制转义序列增加显示的终止标识,则可以减少混淆的可能性。
  • 示例:在此示例中,由 s1,s2 和 s3 指向的每个字符串都等效于字符串“Ag”。
const char *s1 = "\x41g"; /* 违规 - 无法区分哪个是转译序列, 哪个又是普通字符 */
const char *s2 = "\x41" "g"; /* 合规 - 以字符串结束标识转译序列的结束 */
const char *s3 = "\x41\x67"; /* 合规 - 以新的转译序列起始标识前一个转译序列的结束 */
int c1 = '\141t'; /* 违规 - 无法区分哪个是转译序列, 哪个又是普通字符 */
int c2 = '\141\t'; /* 合规 - 以新的转译序列起始标识前一个转译序列的结束 */

参看:C语言再学习 – 转义字符

在这里插入图片描述
建议,不使用八进制和十六进制转义序列。

Rule 4.2 禁止使用三字母词(trigraphs)

  • 级别:建议
  • 解读:三字母词(或叫三联符序列)由两个问号起始,后跟一个特定字符组成。截至目前(2020 年),三字母词只有 9 个: 在这里插入图片描述
    源代码中的“三字母词”,在编译阶段会被替换为“对应的字符”。 而它们会与两个问号的其他用法引起意外混淆。
  • 示例:
    例如,字符串
"(Date should be in the form ??-??-??)"

会被编译器解析为

"(Date should be in the form ~~]"

参看:C语言再学习 – 三字母词(转)

下面是我们很容易犯的一个错误(摘自《C和指针》):

#include <stdio.h>
int main (void)
{printf("??( \n");printf("??) \n");return 0;
}root@# gcc test.c
test.c: 在函数‘main’中:
test.c:4:10: 警告: 三元符 ??( 被忽略,请使用 -trigraphs 来启用 [-Wtrigraphs]
test.c:5:10: 警告: 三元符 ??) 被忽略,请使用 -trigraphs 来启用 [-Wtrigraphs]root@# gcc -trigraphs test.c 
输出结果:
[ 
] 

注意 :由于编译器的种类各样,对ANSI C的支持也不一样,所以可能会有些C编译器不处理“三字母词”,会将它们当做普通的字符串来处理。 以上测试是在VC++ 6.0下进行的,对于GCC编译器,需要在编译的时候添加选择"-ansi"或者"-trigraphs"。

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

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

相关文章

2024 年如何成为一名成功的漏洞赏金猎人?成长总结以及相关资料推荐

2024 年如何成为一名成功的漏洞赏金猎人?成长总结以及相关资料推荐。 很多狂热的黑客新手都很好奇,如何才能成为一名黑客。其实黑客也有黑帽、白帽、灰帽或红帽之类的称呼。黑帽黑客,指的是专门制造病毒木马、通过操作系统寻找漏洞牟取暴利,并且以个人意志为出发点,肆意攻…

青年作家考公引热议,体制内可能不是你想的那样

点击文末“阅读原文”即可参与节目互动 剪辑、音频 / 阿福 运营 / SandLiu 卷圈 监制 / 姝琦 封面 / 姝琦Midjourney 产品统筹 / bobo 场地支持 / 声湃轩天津录音间 最近&#xff0c;班宇、陈春成、王苏辛三名青年作家出现在了武汉市文联所属事业单位专项招聘拟聘用人员名…

15技术太卷我学APEX-curl请求apex的autoRest

0 curl概述 cURL无处不在。它几乎隐藏在所有设备中&#xff0c;例如汽车&#xff0c;蓝光播放器等。它通过互联网协议传输任意类型数据。 0.1 cURL是什么意思&#xff1f; cURL&#xff08;客户端URL&#xff09;是一个开放源代码的命令行工具&#xff0c;也是一个跨平台的库…

选择排序详解:直接选择排序+堆排序(思路+图解+代码)

文章目录 排序选择排序1.直接选择排序方法一方法二直接插入排序和直接排序的区别 2.堆排序 排序 选择排序 在待排序序列中&#xff0c;找到最小值&#xff08;大&#xff09;的下标&#xff0c;和排好序的末尾交换&#xff0c;放到待排序列的开头&#xff0c;直到全部待排序元…

php在线审稿系统mysql数据库web结构layUI布局apache计算机软件工程网页wamp

一、源码特点 php在线审稿系统是一套完善的web设计系统mysql数据库 &#xff0c;对理解php编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。 php在线审稿系统 代码 https://download.csdn.net/download/qq_41221322/885…

【C语言 | 数组】C语言数组详解(经典,超详细)

&#x1f601;博客主页&#x1f601;&#xff1a;&#x1f680;https://blog.csdn.net/wkd_007&#x1f680; &#x1f911;博客内容&#x1f911;&#xff1a;&#x1f36d;嵌入式开发、Linux、C语言、C、数据结构、音视频&#x1f36d; &#x1f923;本文内容&#x1f923;&a…

websocket学习笔记【springboot+websocket聊天室demo】

文章目录 WebSocket是什么&#xff1f;为什么需要WebSocket?WebSocket和Http连接的区别WebSocket的工作原理基本交互过程&#xff1a; Java中的WebSocket支持WebSocket的优势springboot websocket themlef 一个聊天室demopom.xmlWebSocketConfigChatControllerWebController…

PDF/X、PDF/A、PDF/E:有什么区别,为什么有这么多格式?

PDF 是一种通用文件格式&#xff0c;允许用户演示和共享文档&#xff0c;无论软件、硬件或操作系统如何。多年来&#xff0c;已经创建了多种 PDF 子类型来满足各个行业的不同需求。让我们看看一些最流行的格式&#xff1a;PDF/X、PDF/A 和 PDF/E。 FastReport .net下载 PDF/X …

火山引擎云原生存储加速实践

在火山引擎相关的业务中绝大部分的机器学习和数据湖的算力都运行在云原生 K8s 平台上。云原生架构下存算分离和弹性伸缩的计算场景&#xff0c;极大的推动了存储加速这个领域的发展&#xff0c;目前业界也衍生出了多种存储加速服务。但是面对计算和客户场景的多样性&#xff0c…

Go fsnotify简介

fsnotify是一个用Go编写的文件系统通知库。它提供了一种观察文件系统变化的机制&#xff0c;例如文件的创建、修改、删除、重命名和权限修改。它使用特定平台的事件通知API&#xff0c;例如Linux上的inotify&#xff0c;macOS上的FSEvents&#xff0c;以及Windows上的ReadDirec…

Leetcode——岛屿的最大面积

1. 题目链接&#xff1a;695. 岛屿的最大面积 2. 题目描述&#xff1a; 给你一个大小为 m x n 的二进制矩阵 grid 。 岛屿 是由一些相邻的 1 (代表土地) 构成的组合&#xff0c;这里的「相邻」要求两个 1 必须在 水平或者竖直的四个方向上 相邻。你可以假设 grid 的四个边缘都…

[Mac软件]Adobe XD(Experience Design) v57.1.12.2一个功能强大的原型设计软件

Adobe XD是一个直观、强大的UI/UX开发工具&#xff0c;旨在设计、原型设计、用户之间共享材料&#xff0c;以及通过数字技术设计交互。Adobe XD为您提供开发网站、应用程序、语音界面、游戏界面、电子邮件模板等所需的一切。 无限制地创建 设计各种互动&#xff0c;创建看起来…

01序列 卡特兰数

解法&#xff1a; 将01序列置于坐标轴上&#xff0c;起始点为原点。0表示向右走&#xff0c;1表示向上走。这样就可以将前缀0的个数不少于1的个数就可以转换为路径上的点&#xff0c;横坐标大于纵坐标&#xff0c;也就是求合法路径个数。 注意题目mod的数是质数&#xff0c;所…

YOLOv5独家原创改进:最新原创WIoU_NMS改进点,改进有效可以直接当做自己的原创改进点来写,提升网络模型性能精度

💡该教程为属于《芒果书》📚系列,包含大量的原创首发改进方式, 所有文章都是全网首发原创改进内容🚀 💡本篇文章为YOLOv5独家原创改进:独家首发最新原创WIoU_NMS改进点,改进有效可以直接当做自己的原创改进点来写,提升网络模型性能精度。 💡对自己数据集改进有效…

开发知识点-Vue-Electron

Electron ElectronVue打包.exe桌面程序 ElectronVue打包.exe桌面程序 为了不报错 卸载以前的脚手架 npm uninstall -g vue-cli安装最新版脚手架 cnpm install -g vue/cli创建一个 vue 随便起个名 vue create electron-vue-example (随便起个名字electron-vue-example)进入 创建…

Node.js详解

一、是什么 Node.js 是一个开源与跨平台的 JavaScript 运行时环境 在浏览器外运行 V8 JavaScript 引擎&#xff08;Google Chrome 的内核&#xff09;&#xff0c;利用事件驱动、非阻塞和异步输入输出模型等技术提高性能 可以理解为 Node.js 就是一个服务器端的、非阻塞式I/…

关于Chrome中F12调试Console输入多行

在chrome 浏览器中使用console调试的时&#xff0c;如果想在console中输入多行代码&#xff0c;需要进行换行。 这时我们可以使用 [ Shift Enter ] 。也叫&#xff1a; 软回车。

C 语言实现 UDP

广播 发送广播信息&#xff0c;局域网中的客户端都可以接受该信息 #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <arpa/inet.h>int main() {// 1.创建一个通信的socketint fd socket(PF_INET, …

S32DS踩坑日记五-bootloader跳转APP时触发DefaultISR

S32DS踩坑日记五-bootloader跳转APP时触发DefaultISR bootloader和APP由另一位同事开发过程中&#xff0c;被导师叫回去写论文了。 由于项目不急&#xff0c;接手后未作任何改动&#xff0c;后面硬件工程师手工焊了几块电路版&#xff0c;需要刷上程序测试电路板。然后就遇到了…

Java 通过POI快速导入带图片的excel并且图片不会丢失

## 通过POI快速导入带图片的excel并且图片不会丢失导入带图片的excel,这里也是研究了很久,在之前的博客中也有说明过,在项目使用过程中,发现很多时候导入响应很慢,而且每次导入图片都会丢失几张,所以又花了点时间研究修改了下,具体如下: 这边在导入时,通过自定义注解…