1、ICS 29.240 Q/GDW 国 家 电 网 公 司 企 业 标 准 Q/GDW 11680 2017 智能电能表软件可靠性技术规范 The technical specification of software reliability for smart electricity meters 2018 - 01 - 18 发布 2018 - 01 - 18实施 国家电网公司 发布 Q/GDW 116802017 I 目 次 前 言 . II 1 范围 . 1 2 规范性引用文件 . 1 3 术语和定义 . 1 4 缩略语 . 2 5 可靠性要求 . 2 6 检测项目及要求 . 8 附录
2、A(资料性附录) C语言编码规则 11 附录 B(规范性附录) 圈复杂度计算 . 32 附录 C(规范性附录) 数据重要等级 . 33 附录 D(规范性附录) 时钟数据非法判断原则 . 34 编制说明 . 35 Q/GDW 116802017 II 前 言 为规范智能电能表软件设计,提高电能表软件可靠性,减少由电能表软件缺陷导致的采集系统现场故障,制定本标准。 本标准由国家电网公司营销部提出并解释。 本标准由国家电网公司科技部归口。 本标准 起草单位:中国电力科学研究院、国网北京市电力公司、国网冀北电力有限公司、国网宁夏电力公司、国网辽宁省电力有限公司、国 网福建省电力有限公司、国网河南省电力
3、公司、国网黑龙江省电力有限公司、国网浙江省电力公司、国网河北省电力公司、国网四川省电力公司。 本标准主要起草人: 吕英杰、 刘鹰、 杜蜀薇、杜新纲、葛得辉、赵兵、彭楚宁、周晖、翟峰、付义伦、梁晓兵、 李保丰、 孔令达、 徐英辉、章欣、孙志强、 岑炜、曹永峰、徐文静、许斌、张庚、韩文博、卢艳、袁泉、冯占成、任博、杨全 萍、周琪、徐萌、祝恩国、李冀、袁瑞铭、巨汉基、王玮、李伟、马晓奇、李丹、高琛、钟小强、秦楠、邵淮岭、侯慧娟、王雍、刘华、徐永进、曹宏宇、赵宇东、叶强、张颖琦、陶鹏、夏桦裕、 王晨丞。 本标准首次发布。 本标准在执行过程中的意见或建议反馈至国家电网公司科技部。 Q/GDW 11680
4、2017 1 智能电能表软件可靠性技术规范 1 范围 本标准规定了智能电能表(以下简称“电能表”)软件的编程要求、可靠性要求以及测试方法。 本标准适用于符合国家电网公司智能电能表系列标准的智能电能表的软件设计、检测。 2 规范性引用文件 下列文件对于本文件的应用是必不可少的。凡是注日期的引用文件,仅 注日期的版本适用于本文件。凡是不注日期的引用文件,其最新版本(包括所有的修改单)适用于本文件。 GB/T 11457 2006 软件工程术语 GB/T 16649.4 2010 识别卡 集成电路卡 第 4部分:用于交换的结构、安全和命令 Q/GDW 1354 2013 智能电能表功能规范 Q/GD
5、W 1365 2013 智能电能表信息交换安全认证技术规范 3 术语和定义 GB/T 11457 2006、 Q/GDW 1354 2013和 Q/GDW 1365 2013界定的以及 下列术语和定义适用于本文件。 3.1 圈复杂度 cyclomatic complexity 一种代码复杂度的衡量标准,用来衡量一个模块判定结构的复杂程度,数量上表现为独立线性路径条数,即合 理的预防错误所需测试的最少路径条数。 3.2 码距 code distance 两组编码之间不同位的个数称为这两组编码的码距。 3.3 同步 synchronous 保证同一数据在不同存储区域数据相同的操作。 3.4 测试用
6、例 test case 为某个特殊目的编制的一组测试输入、执行条件以及预期结果的集合。 Q/GDW 116802017 2 3.5 非易失性存储器 nonvolatile memory 断电后仍能保证存储数据不丢失的存储器,如 EEPROM、 FLASH以及 ESAM等。 4 缩略语 下列 缩略语 适用于本文件。 MCU: 微控制单元 ( Micro Controller Unit) RAM: 随机存取存储 器( Random Access Memory) APDU:应用层协议数据单元 ( Application Protocol Data Unit) ESAM:嵌入式安全存取模块 ( Emb
7、edded Security Access Module) 5 可靠性要求 5.1 编程语言 5.1.1 C语言 使用 C语言编程 ,应遵循附录 A的要求。 5.1.2 其它语言 其它语言暂不作要求。 5.2 一般要求 5.2.1 圈复杂度 应满足如下要求: a) 软件模块的平均圈复杂度应不大于 10; b) 软件模块的最大圈复杂度应不大于 80; c) 软件模 块的圈复杂度大于 10但不大于 40的比例应不大于 30%,大于 40 的比例应不大于 10%; d) 圈复杂度计算公式见附录 B。 5.2.2 保护机制 应满足如下要求: a) 若中断服务程序和主程序共用同一全局变量或硬件外设等全局
8、共享资源时,应有互锁保护机制; b) 若不同优先级中断服务程序存在嵌套,并共用同一全局变量或硬件外设等全局共享资源时,应有互锁保护机制; c) 函数或子程序中不宜有递归和重入,若存在重入,应做相应保护措施。 5.3 数据存储、备份与校验 应满足如下要求: a) 数据根据重要级别分为 A、 B、 C、 D四类等级,见附录 C; b) A、 B、 C类数据应保存于 非易失性存储器,应有校验码用于数据正确性检测,校验算法不应仅使用单字节校验和方式; Q/GDW 116802017 3 c) A、 B类数据在非易失性存储器中应有备份,宜具有纠错功能; d) 不应使用受损且未恢复的数据。 5.4 费率、
9、阶梯、时段处理 5.4.1 费率查询操作 应满足如下要求: a) 查询费率时,应按附录 D原则判断时钟是否异常; b) 使用节假日、周休日、时区表、时段表时,应判断参数合法性。 5.4.2 阶梯查询操作 应满足如下要求: a) 查询阶梯时,应按附录 D原则判断时钟是否异常; b) 查询阶梯时,应进行参数的合法性判断。 5.4.3 时区表、时段表、费率表、阶梯表切换操作 应满足如下要求: a) 时区表、 时段表、费率表、阶梯表切换前,应按附录 D原则判断时钟是否异常。当时钟数据异常且不能恢复时,上报错误,不切换; b) 时区表、时段表、费率表、阶梯表切换前,应判断参数合法性。若出错无法恢复时,上
10、报错误,不切换。 5.5 电能量计算处理 5.5.1 数脉冲方式 应满足如下要求: a) 在施加的功率方向固定的情况下,电表应在 3秒内确定脉冲的电能量方向; b) 电能量或者脉冲累加前应确认待修改数据的有效性; c) 电能量累加前应判断脉冲常数的正确性; d) 脉冲累计时应检查费率号的正确性。 5.5.2 读能量寄存器方式 应满足如下要求: a) 读能量寄存器周期不宜大于 3秒; b) 每次电 能量累加应判断电能量方向; c) 电能量累加前应确认待修改数据的有效性; d) 电能量累加前应判断脉冲常数的正确性; e) 电 能 量累计时应检查费率号的正确性。 5.6 非易失存储器处理 应满足如下
11、要求: a) 存储器写入次数不能超过全温度范围的最低擦写次数; b) 存储器写操作的处理,宜有防止子程序非法调用造成数据被破坏的机制; c) 写入数据不应超出定义的存储边界。 5.7 程序存储器处理 Q/GDW 116802017 4 5.7.1 未用程序存储器单元的处理 未用程序存储器单元应填充不引起程序或 MCU异常的数据,如单字节空操作指令。 5.7.2 未用中断处理 应满足如下要求: a) 关闭未用中断,同时对寄存器采用定 时校验,定时校验的时间不大于 60 分钟; b) 未用的中断,其中断向量应指向确定的位置,中断服务程序应采用不会引起异常运行的操作。 5.8 看门狗处理 合理设计看
12、门狗复位时间和喂狗位置,确保系统能正常运行。 5.9 本地费控智能电能表扣费 应满足如下要求: a) 扣费前 应 先验证当前阶梯值、费率号、费率电价、阶梯电价的正确性; b) 扣钱包文件宜使用带校验和扣费 APDU命令; c) 未使用带校验和的扣费命令,应有确认机制。 5.10 通信处理 5.10.1 外部通讯 应满足如下要求: a) 各通道收发缓存及状态机应保证互不干扰; b) 接收缓存应避免越界造成数据错误; c) 应有通讯相关寄存 器及状态机工作状态监视功能,出现异常能及时恢复; d) 正常供电下,模块通讯接口连续空闲超过 36小时后应有定时复位机制,防止模块死机。 5.10.2 MCU
13、与外设通讯 应满足如下要求: a) 应定时监视通讯接口寄存器配置,出错后能自动恢复; b) 通信数据宜使用硬件校验机制; c) 同一总线上有多器件时,应避免多器件相互干扰。 5.11 ESAM的处理 应满足如下要求: a) 应严格遵循 ESAM 接口的时序和要求。 MCU 与 ESAM 的操作流程应严格按照 智能电能表系列标准的 相关规定执行; b) 若连续多次软复位 ESAM均出错时,应对 ESAM进行硬复位处理; c) ESAM 异 常无法恢复的情况下,除 ESAM 对应的功能外,其它功能应正常运行,同时上报 ESAM故障。 5.12 上电处理 5.12.1 初始化操作 应满足如下要求:
14、Q/GDW 116802017 5 a) 当上电初始化程序有不同执行路径时,程序进入各路经分支的条件编码应具有适当的码距,提高抗干扰能力,防止进入错误路径; b) 当初始化程序具有初次上电初始化路径时,初次上电初始化程序应有保护措施,确保在非初次上电时程序不进入该路径执行。 5.12.2 对 RAM的操作 应满足如下要求: a) 应对存储在 RAM中的 A、 B、 C类数据以及保证程序正确执行的运行数据等进行正确性检验; b) 当 RAM中的数据被破坏,相关数据应能 够从非易失性存储中恢复。 5.12.3 对计量芯片的操作 应使用正确的校表参数重新设置计量芯片的校表寄存器、控制寄存器。 5.1
15、2.4 对时钟芯片的操作 应判断时钟数据的正确性。 5.13 正常供电时处理 5.13.1 电能量存储处理 5.13.1.1 电能量存储 应满足如下要求: a) 电能量应定时或定量存储到非易失性存储器中; b) 单相表存储间隔应不大于 1kWh; c) 三相表存储间隔见表 1。 表 1 三相表存储间隔 准确度等级 最大存储电能量间隔 (kWh/kvarh) 1 1(直接式接入) 0.1(经互感器接入)(增加答疑,变比、存储间隔的考虑) 0.5S 0.01(经互感器接入) 0.2S 0.01(经互感器接入) 5.13.1.2 电能量数据备份 应满足如下要求: a) 电能量数据应有备份机制; b)
16、 电能量数据应有检查和纠错机制。 5.13.2 同步策略 5.13.2.1 RAM和非易失性存储器的同步 应满足如下要求: a) 若 RAM区 A、 B、 C 类数据异常时,应具有从非易失性存储器恢复机制,恢复前应确认数据源的有效性; b) RAM中保存的 A、 B类数据在使用前 ,应先验证 RAM区数据的正确性。如果不正确应从非易失性存储器中进行恢复,恢复后再使用; Q/GDW 116802017 6 c) 修改 RAM 中保存的 A、 B 类数据时,如果待修改数据仅是受保护数据的局部时,应先验证 RAM区数据的正确性。如果 不正确应从非易失性存储器中进行恢复,恢复后再修改; d) 带备份的
17、数据,在同步非易失性存储器时,应更新主存储区和备份区; e) 本地费控智能电能表电能量、扣费数据应同时同步; f) RAM中电能量数据在显示、被通信读出前,如果发生了改变,应先同步到非易失性存储器中。 5.13.2.2 费控相关的同步 应满足如下要求: a) RAM 中的扣费信息应定时或定量存储到非易失性存储器中,且对应电能量数据应同时同步; b) 剩余金额不应出现回退; c) 当更新 ESAM剩余金额后, RAM、非易失性存储器和 ESAM中剩余金额应保持一致。 5.13.3 时钟芯片操作 5.13.3.1 异常值剔除 程序应能剔 除附录 D.3、 D.4所列出的异常值,在 5分钟内恢复时钟
18、到正常值。若时钟在出现异常值后的 60分钟内连续 3次恢复不成功,应上报时钟故障。 5.13.3.2 中断 源 异常检测 当使用了时钟芯片提供的中断信号作为中断源,应能够在三个中断周期内判断出中断异常,并能恢复正常中断。异常包括时钟芯片中断异常或 MCU接收相应中断的机构异常。 5.13.3.3 控制、状态和参数寄存器异常检测 程序应监测时钟芯片的参数寄存器、状态寄存器以及控制字寄存器,当发现参数值与设置不符时,应能在在 5分钟内恢复到正确参数值。不宜无条件重新写入控制字寄存器。 5.13.3.4 温度补偿 当时钟芯 片的温度补偿需要 MCU参与时,应对输入到时钟芯片的温度补偿参数进行合法性检
19、测, 避免错误的补偿参数写入导致时钟误差增大。 5.13.4 计量芯片操作 5.13.4.1 数脉冲计量 应满足如下要求: a) 有效脉冲宽度:最小脉冲宽度 30ms 5ms,最大脉冲宽度 80ms 16ms; b) 正常供电运行时,应检测脉冲有效性,不应丢掉一个有效脉冲 、不应 多计一个无效脉冲。 5.13.4.2 读寄存器计量 应满足如下要求: a) 程序应能发现和剔除计量芯片中能量寄存器中偶发的异常值 ,且能量寄存器的偶发异常值被剔除后 ,不影响随后的计量; b) 电能量采用读累加方式计量时,程序不应采用能量寄存器 的值直接刷新 RAM 中相应电能量存储区的方式; c) 采取读后清零寄存
20、器方式读取能量寄存器时,程序应有数据传输可靠性确认机制; d) 采取读后不清零寄存器方式, 应注意计量芯片能量寄存器的溢出,溢出后电能量应能正常累加; e) 电能量不宜使用读累加的方式计量。 5.13.4.3 计量芯片控制寄存器监测 应适时检测控制寄存器,当控制寄存器发生变化时,应能在 60秒内恢复正确值。 Q/GDW 116802017 7 5.13.4.4 计量芯片校表参数寄存器监测 应适时检测计量芯片的校表参数寄存器,当校表参数寄存器发生变化时,应能在 60 秒内恢复正确值。 5.13.4.5 计量芯片写保护 若计量芯片具有写保护功能,在修改校表 参数后,应使计量芯片处于写保护状态。 5
21、.14 掉电处理 应满足如下要求: a) 应将 RAM中的 A、 B、 C类未保存的数据保存到非易失性存储器中; b) 用于上电恢复的重要数据,在掉电保存时宜用单独存储区存储。 5.15 故障处理 5.15.1 计量芯片故障处理 应满足如下要求: a) 当前计量芯片的校表参数和存储在存储器中的校表参数同时损坏,且无法重置计量芯片时,应记录时间和上报错误,并按校表缺省值重置计量芯片的校表寄存器。在此故障下,液晶显示按故障前的电能量值显示、本地费控智能电能表不扣费、形成从故障时间开始到换表前累计的电能量的事件记录,且不应覆 盖故障前的电能量值; b) 当计量芯片故障或 MCU 与计量芯片通信故障致
22、使无法重置校表参数时或无法读取计量寄存器值时,应停止计量、记录时间、上报错误,形成事件记录。 5.15.2 费率和阶梯故障处理 应满足如下要求: a) 节假日、周休日时段表损坏且不能恢复时, 形成事件记录并上报 ,等待参数重设,在参数重设前,不作节假日、周休日处理; b) 当时钟数据异常导致不能确定当前费率号时, 形成事件记录并上报 ,等待时间重设,在时间恢复正常前, 费率号不变 ; c) 当时区表数据异常无法确定费率号时, 形成事件记录并上报 ,等待时区表重设,在时区表恢复正常前, 时段表号 不变 ; d) 当时段表数据异常无法确定费率号时, 形成事件记录并上报 ,等待时段表重设,在时段表恢
23、复正常前, 费率号不变 ; e) 当阶梯参数异常不能得到阶梯值时, 形成事件记录并上报 ,等待重设,在数据恢复正常前,本地费控智能电能表不应扣 减 阶梯 电 价。 5.15.3 费控参数数据恢复 应满足如下要求: a) RAM区扣费信息数据异常,非易失性存储器扣费信息数据正常,应以非易失性存储器中为准恢复 RAM区数据; b) RAM区剩余金额数据异常,非易失性存储器剩余金额数据异常,应以 ESAM 中数据为准恢复 RAM区和非易失性存储器中数据; c) RAM区剩余金额数据正常, 非易失性存储器剩余金额数据正常,但不满足 RAM区非易失性存储器 ESAM要求, 应以剩余金额小的为准同步三个存
24、储位置的数据 。 Q/GDW 116802017 8 6 检测项目及要求 6.1 重要数据备份试验 根据提供的非易失性存储器存储分配方案,验证 A、 B类数据应符合 5.3的要求。 6.2 重要数据损坏与恢复试验 试验在下列条件下进行: a) 被测对象正常供电; b) 改变 A、 B、 C类数据主存储区、备份数据区的数据; c) 试验中,软件应符合 5.3d要求。 6.3 费率、阶梯、时段处理试验 试验在下列条件下进行: a) 被测对象正常供电; b) 修改费率、阶梯、时段以及时钟等参数; c) 试验中,软件 应符合 5.4的要求。 6.4 电能量方向判定时间试验 (数 脉冲方式) 试验在下列
25、条件下进行: a) 被测对象正常供电; b) 发送有效脉冲; c) 改变电能量方向; d) 试验中,软件应符合 5.5.1的要求。 6.5 非易失性存储器写入寿命试验 试验在下列条件下进行: a) 被测对象正常供电; b) 根据厂商提供的存储芯片的数据手册,统计写入次数; c) 试验中,软件应符合 5.6.a的要求。 6.6 非易失性存储器数据 存储越界 试验 试验在下列条件下进行: a) 被测对象正常供电; b) 根据厂商提供的存储方案,触发对应的存储条件; c) 试验中,软件应符合 5.6.c要求。 6.7 扣费机制试 验 试验在下列条件下进行: a) 被测对象正常供电; b) 累计电能量
26、到最小扣费所需的量; c) 在电源正常、电源掉电过程中或电源掉电恢复正常时,软件应满足 5.9.a 要求。 Q/GDW 116802017 9 6.8 电能量存储试验 试验在下列条件下进行: a) 被测对象正常供电; b) 试验中,软件应符合 5.13.1.1的要求。 6.9 时钟异常值检测和恢复试验 试验在下列条件下进行: a) 被测对象正常供电; b) 改变时钟芯片寄存器数据使其出现附录 A.3、 A.4所示的异常值; c) 试验中,软件应符合 5.13.3.1的要求。 6.10 时钟芯片中断异常试验 使用了时钟芯片信号作为中断源的电表方案,做此试 验。试验在下列条件下进行: a) 被测对
27、象正常供电; b) 停止使用的中断信号; c) 试验中,软件应符合 5.13.3.2的要求。 6.11 时钟芯片控制寄存器试验 试验在下列条件下进行: a) 被测对象正常供电; b) 改变时钟芯片控制寄存器; c) 试验中,软件应符合 5.13.3.3的要求。 6.12 有效脉冲试验 试验在下列条件下进行: a) 被测对象正常供电; b) 发送脉冲宽度小于 25ms的计量脉冲; c) 试验中,软件应符合 5.13.4.1的要求。 6.13 脉冲能量累计试验 试验在下列条件下进行: a) 被测对象正常供电; b) 不间断向所有通信口发送通信帧; c) 高频次或长时触发按 键; d) 向本地费控智
28、能电能表高频次插购电卡; e) 发送小于 25ms的计量脉冲和最短有效的计量脉冲,脉冲占空比 50%; f) 试验时长 60min; g) 试验中,软件应符合 5.13.4.2的要求。 6.14 计量芯片计量数据异常值剔除试验 试验在下列条件下进行: a) 被测对象正常供电; Q/GDW 116802017 10 b) 改变计量芯片计量寄存器数据; c) 试验中,软件应符合 5.13.4.2a的要求。 6.15 计量芯片电能量累加寄存器数据溢出试验 试验在下列条件下进行: a) 被测对象正常供电; b) 改变计量芯片计量寄存器数据使其溢出; c) 试验中,软件应符合 5.13.4.2d的要求。
29、 6.16 计量芯片控 制寄存器试验 试验在下列条件下进行: a) 被测对象正常供电; b) 改变计量芯片控制寄存器的数据; c) 试验中,软件应符合 5.13.4.3的要求。 6.17 计量芯片校表参数出错恢复试验 试验在下列条件下进行: a) 被测对象正常供电; b) 改变计量芯片内校表参数寄存器的数据; c) 试验中,软件应符合 5.13.4.4的要求。 6.18 计量芯片写保护试验 试验在下列条件下进行: a) 被测对象正常供电; b) 试验中,软件应符合 5.13.4.5的要求。 6.19 掉电时 A、 B类数据备份保存试验 试验在下列条件下进行: a) 主电源按不同的梯度降低主电源
30、; b) 电池电源正常供电和停 止供电; c) 抄表电池正常供电和停止供电; d) 根据厂商的存储方案,在组合上述条件的情况下,软件应满足 5.14要求。 6.20 其他 根据生产厂商软件实现的方案,编制特殊测试用例,软件应能正常运行,满足智能电能表相关系列标准的要求。 Q/GDW 116802017 11 A A B附 录 A (资料性附录) C语言编码规则 A.1 语言规则 应满足如下要求: a) 汇编语言代码应封装起来并同 C语言隔离; b) 字符串 /*不应出现在注释中; c) 源代码应该使用 /* */ 类型的注释,不宜使用 /类型注释。 A.2 文档 应满足如下要求: a) 所有使
31、用的 #pragma 指令的应该有文档记录并给出详细解释。这项规则 提出了使用的任何pragma 的要求。每个 pragma的含义要写成文档,文档中应当包含对 pragma 行为及其在应用中之含义的详细描述。应当尽量减少任何 pragma 的使用,尽可能地把它们本地化和封装成专门的函数; b) 如果做为其他特性的支撑,实现定义( implementation-defined)的行为和位域( bitfields)集合应当有文档记录; c) 产品代码中使用的所有库都要遵守本文档给出的要求,并且要经过验证; d) 所有实现定义( implementation-defined)的行为的使用都应该有文档
32、记录; e) 应 该确定编译器中整数除法的实现,并以文档说明; f) 当两个有符号整型数做除法时, ISO 兼容的编译器的运算可能会为正或为负。首先,它可能以负余数向上四舍五入(如, -5/3 = -1,余数为 -2),或者可能以正余数向下四舍五入(如,-5/3 = -2,余数为 +1)。重要的是要确定这两种运算中编译器实现的是哪一种,并以文档方式提供给编程人员,特别是第二种情况(通常这种情况比较少)。 A.3 字符集 不能使用三字母词( trigraphs)。三字母词由 2 个问号序列后跟 1 个确定字符组成(例如, ?- 代表“ ”(非)符号)。它 们可能会对 2 个问号标记的其他使用造成
33、意外的混淆,例如字符串 (Date is in the form ?-?-?)将不会表现为预期的那样,实际上它被编译器编译为 (Date is in the form )。 A.4 标识符 A.4.1 标识符的有效字符 标识符(内部的和外部的)的有效字符不能多于 31。不允许使用中文拼音首字母,建议不要用中文拼音,除了注释不要有中文; Q/GDW 116802017 12 A.4.2 标识符的重名 具有内部作用域的标识符不应与具有外部作用域的标识符重名,这会隐藏了外部标识符。 外部作用域和内部作用域的定义为:文件范围内的标识符 可以看做是具有最外部的作用域;块范围内的标识符看做是具有更内部的作
34、用域;连续嵌套的块,其作用域更深入。本规则只是不允许一个第二深层的定义隐藏其外层的定义。 在嵌套的范围中,使用相同名称的标识符隐藏其他标识符会使得代码非常混乱。例如: int32_t j; int32_t j; /* This is a different variable from the outside variable j */ j = 3; /* It could be confusing as to which j this refers */ 。 A.4.3 标识符的唯一性 typedef的名称应当是唯一的标识符, 标签( tag)名称必须是唯一的标识符。 A.4.4 具有静态存储
35、期的对象或函数标识符 具有静态存储期的对象或函数标识符 不能重用。不管作用域如何,具有静态存储期的标识符都不应在系统内的所有源文件中重用。它包含带有外部链接的对象或函数,及带有静态存储类标识符的任何对象或函数。 由于编译器能够理解这一点而且决不会发生混淆,那么对用户来说就存在着把不相关的变量以相同名字联系起来的可能性。 这种混淆的例子之一是,在一个文件中存在一个具有内部链接的标识 符,而在另外一个文件中存在着具有外部链接的相同名字的标识符。 一个命名空间中标识符不应与另外一个命名空间中的标识符重名,除了结构和联合中的成员名字,不能重用标识符名称。 不考虑作用域,系统内任何文件中不应重用标识符。
36、 本规则和规则 A.4.2、 A.4.3一同使用。 structair_speed uint16_t ui16Speed; /* knots */ *x; structground_speed uint16_t ui16Speed; /* mph */ /* Not Compliant speed is in different units */ *y; x-ui16Speed = y-ui16Speed; 当标识符名字用在头文件且头文件包含在多个源文件中时,不算违背本规则。使用严格的命名规范可以遵守本规则。 A.5 类型 应满足如下要求: Q/GDW 116802017 13 a) char
37、 类型应该只用做存储和使用字符值; b) signed char 和 unsigned char 类型应该只用做存储和使用数字值。 有三种不同的 char 类型:char、 unsigned char、 signed char。 unsigned char 和 signedchar 用于数字型数据, char 用于字符型数据。 char 类型(也可称为单纯 char)的符号是实现定义的,不应依赖。单纯 char 类型所能接受的操作只有赋值和等于操作符( =、 =、 !=)。 c) 应该使用指示了大小和符号的 typedef 以替代基本数据类型。 不应使用基本数值类型 char、int、 sho
38、rt、 long、 float 和 doulbe,而应使用特定长度( specific-length)的 typedef。规则 A.5.c 帮助我们认清存储类型的大小,却不能保证可移植性,这是因为不同平台中整数int大小会不同,字节对齐也会不同。 A.6 声明与定义 A.6.1 函数的原型声明 函数应当具有原型声明,且原型在函数的定义和调用范围内都是可见的。 原型的使用使得编译器能够检查函数定义和调用的完整性。如果没有原型,编译器就不能检查出函数调用当中的错误(比如,函数体具有不同的参数数目,调用和定义之间参数类型的不匹配)。事实证明,很多问题和函数接口相关,因此本规则是很重要的。 对外部函数
39、来说,我们建议采用这样方法:在头文件中声明函数(即给出其原型),并在所有需要该函数原型的代码文件中包含这个头文件(见规则 A.6.5)。 为具有内部链 接的函数给出其原型也是良好的编程做法。 A.6.2 函数的 显式 声明 不论何时声明或定义了一个对象或函数,它的类型都应显式声明,示例如下 : extern i16width; /* Non-compliant implicit int type */ extern int16_t i16width ; /* Compliant explicit type */ const i16y ; /* Non-compliant implicit in
40、t type */ const int16_t i16y ; /* Compliant explicit type */ static foo (void) ; /* Non-compliant implicit type */ static int16_t foo (void) ; /* Compliant explicit type */ A.6.3 函数参数类型和返回类型 函数的每个参数类型、函数的返回类型在声明和定义中必须是相同的。 参数与返回值的类型在原型和定义中必须相同,这不仅要求同样的基本类型,也要求包含 typedef 名称和限定词在内的类型 也要相同。 A.6.4 对象或函数
41、的兼容性 如果对象或函数被声明了多次,那么它们的类型应该是兼容的。 兼容类型的定义是冗长复杂的(详见 ISO 9899 :2011 2,节 6.1.2.6、 6.5.2、 6.5.3、 6.5.4)。两个相同的类型必然是兼容的,而两个兼容的类型不需要相同。例如,下面的类型对是兼容的: signed int16 int16 char10 char signed short int sigend short Q/GDW 116802017 14 A.6.5 头文件 头文件中不应有对象或函数的定义。 头文件应该用于声明对象、函数、 typedef 和宏,而不应该有需要分配存储空间的对象或函数(或它们
42、的片断)的定义。这样就清晰地划分了只有 C 文件才包含可执行的源代码,而头文件只能包含声明。 A.6.6 函数的文件作用域 函数应该声明为具有文件作用域, 在块作用域中声明函数会引起混淆并可能导致未定义的行为。 A.6.7 对象的块范围内声明 如果对象的访问只是在单一的函数中,那么对象应该在块范围内声明。 可能的情况下,对象的作用域应该限制在函数内。只有当对象需要具有内部或外部链接时才能为其使用文件作用域。良好的编程做法是,在不必要的情况下避免使用全局标识符。 A.6.8 外部对象声 明 外部对象或函数应该声明在唯一的文件中。 通常这意味着在一个头文件中声明一个外部标识符,而在定义或使用该标识
43、符的任何文件中包含这个头文件。例如,在头文件 featureX.h 中声明: extern int16_t i16width ; 然后对 i16width 进行定义: #include int16_t i16width = 0 ; 工程中存在的头文件可能是一个或多个,但是任何一个外部对象或函数都只能在一个头文件中声明。 A.6.9 具有外部链接的标识符 具有外部链接的标识符应该具有外部定义。 一个 标识符不能有多个定义,也不能不定义。 A.6.10 具有外部链接的数组 当一个数组声明为具有外部链接,它的大小应该显式声明或者通过初始化进行隐式定义。 int16 aWidth110 ; /* Co
44、mpliant */ extern int16 aWidth2 ; /* Not compliant */ int16 aWidth2 = 0, 10, 15 ; /* Compliant */ 尽管可以在数组声明不完善时访问其元素,然而仍然是在数组的大小可以显式确定的情况下,这样做才会更为安全。 A.7 初始化 A.7.1 变 量的赋值 所有变量在使用前都应被赋值。 A.7.2 初始化构造 应该使用大括号以指示和数组和结构匹配的初始化构造。 Q/GDW 116802017 15 ISO C 要求数组、结构和联合的初始化列表要以一对大括号括起来。本规则更进一步地要求,使用附加的大括号来指示嵌套
45、的结构。它迫使程序员显式地考虑和描述复杂数据类型元素(比如,多维数组)的初始化次序。 例如,下面的例子是二维数组初始化的有效(在 ISO C 中)形式,但第一个与本规则相违背: int16_t x42 = 1, 2, 3, 4, 5, 6,7,8 ; /* not compliant */ int16_t x42 = 1, 2 , 3, 4 , 5, 6 ,7,8 ; /* compliant */ 在结构中以及在结构、数组和其他类型的嵌套组合中,规则类似。 另外要注意的是,数组或结构的元素可以通过只初始化其首元素的方式初始化(为 0 或 NULL)。如果选择了这样的初始化方法,那么首元素应该
46、被初始化为 0(或 NULL),此时不需要使用嵌套的大括号。 A.7.3 枚举列表 在枚举列表中,“ =”不能显式用于除首元素之外的元素上,除非所有的元素都是显式初始化的。 如果枚举列表的成员没有显式地初始化,那么 C 将为其分配一个从 0 开始的整数序列,首元素为 0,后续元素依次加 1。 如上规则允许的,首元素的显式初始化迫使整数的分配从这个给定的值开始。当采用这种方法时,重要的是确保所用初始化值一定要足够小,这样列表中的后续值就不会超出该枚举常量所用的 int存储量。 列表中所有项目的显式初始化也是允许的,它防止了易产生错误的自动与手动分配的混合。然而,程序员就该担负职责以保证所有值都处
47、在要求的范围内以及值不是被无意复制的。 enumTimeSeg Zone = 3, TimeSegTable, Week,Holiday = 5 ; /* not compliant */ enumcolour Zone = 3, TimeSegTable = 4, Week = 5, Holiday = 5 ; /* compliant */ /* Week and Holiday represent the same value this is duplication */ A.8 数值类型转换 A.8.1 整型表达式 下列条件成立时,整型表达式的值不应隐式转换为不同的基本类型: a) 转
48、换不是带符号的向更 宽整数类型的转换; b) 表达式是复杂表达式; c) 表达式不是常量而是函数参数; d) 表达式不是常量而是返回的表达式。 A.8.2 浮点型表达式 下列条件成立时,浮点类型表达式的值不应隐式转换为不同的类型: a) 转换不是向更宽浮点类型的转换; b) 表达式是复杂表达式; c) 表达式是函数参数; d) 表达式是返回表达式。 还要注意,在描述整型转换时,始终关注的是基本类型而非真实类型。 这两个规则广泛地封装了下列原则: a) 有符号和无符号之间没有隐式转换; Q/GDW 116802017 16 b) 整型和浮点类型之间没有隐式转换; c) 没有从宽类型向窄类型的隐式
49、转换; d) 函数参数没有隐式转换; e) 函数的返回表 达式没有隐式转换; f) 复杂表达式没有隐式转换。 A.8.3 整型复杂表达式 整型复杂表达式的值只能强制转换到更窄的类型且与表达式的基本类型具有相同的符号。 A.8.4 浮点类型复杂表达式 浮点类型复杂表达式的值只能强制转换到更窄的浮点类型。 A.8.5 位运算符 如果位运算符 和 get_task_fn (p+); A.10.1.5 函数调用 函数在被调用时可能会同时受到附加的影响(如,修改某些全局数据)。可以通过在使用函数的表达式之前,采用临时变量来存储函数输出值的方法进行规避。 例如 y= f (i) + g (i); 可以写成 y= f (i); y+= g (i); 下面的表达式是一个可能会出错的例子,它从堆栈中取出两个值,从第一个值中减去第二个值,再把结果放回栈中: push ( pop () pop () ); 根据哪一个 pop () 函数先进行计算(因为 pop()具有副作用)会产生不同 的结果。 A