前言
今天在检查代码QAC时发现一个之前未见过的rule:3306,提示The implementation of the struce/union type should be hide,当时并不知道什么意思,回来查资料有点像用C语言实现C++的封装特性。
Misra C
MISRA C:2012 standard中对3306做出以下解释:
The typedef name of a structure shall be a unique identifier and shall be used only in the definition of the structure. The contents of any structure shall not be accessed directly, but only through functions.
我的理解是MISRA C建议结构体的访问最好使用不透明的指针,这有助于确保结构的实现细节对程序的其他部分隐藏,从而提高可维护性并降低错误风险。
我们通常会将结构体的定义和声明分别放在不同的头文件中。这样可以确保只有实现该结构体的源文件才能访问结构体的实际定义,而其他源文件只能使用结构体的指针。
例子
以下是一个示例,演示如何使用不透明的结构体指针来隐藏结构体的实现细节:
首先,在头文件中声明结构体类型和一组公共接口函数,如下所示:
// employee.h
// 不透明的指针类型,用于表示 Employee 结构体
typedef struct Employee* EmployeePtr;
// 公共接口函数,用于创建和销毁 Employee 对象
EmployeePtr createEmployee(const char* name, const char* hireDate, double salary);
void destroyEmployee(EmployeePtr employee);
// 公共接口函数,用于获取和设置 Employee 对象的属性
const char* getEmployeeName(EmployeePtr employee);
const char* getEmployeeHireDate(EmployeePtr employee);
double getEmployeeSalary(EmployeePtr employee);
void setEmployeeSalary(EmployeePtr employee, double salary);
然后,在实现文件中定义结构体类型和公共接口函数的实现,如下所示:
// employee.c
#include "employee.h"
#include <string.h>
#include <stdlib.h>
// Employee 结构体的实际定义
struct Employee {
char name[256];
char hireDate[256];
double salary;
};
// 创建 Employee 对象并返回指针
EmployeePtr createEmployee(const char* name, const char* hireDate, double salary) {
// 分配内存以存储 Employee 结构体
EmployeePtr employee = (EmployeePtr) malloc(sizeof(struct Employee));
// 将参数复制到结构体中
strncpy(employee->name, name, sizeof(employee->name));
strncpy(employee->hireDate, hireDate, sizeof(employee->hireDate));
employee->salary = salary;
// 返回指针
return employee;
}
// 销毁 Employee 对象
void destroyEmployee(EmployeePtr employee) {
// 释放内存
free(employee);
}
// 获取 Employee 的姓名
const char* getEmployeeName(EmployeePtr employee) {
return employee->name;
}
// 获取 Employee 的雇佣日期
const char* getEmployeeHireDate(EmployeePtr employee) {
return employee->hireDate;
}
// 获取 Employee 的薪资
double getEmployeeSalary(EmployeePtr employee) {
return employee->salary;
}
// 设置 Employee 的薪资
void setEmployeeSalary(EmployeePtr employee, double salary) {
employee->salary = salary;
}
在上述示例中,Employee 结构体的实际定义和实现被隐藏在实现文件中,只有通过使用不透明的指针类型(EmployeePtr)才能访问结构体的公共接口函数。这有助于确保只有实现文件能够直接访问 Employee 结构体的实现细节,并提高了代码的可维护性和可重用性。