由于Misra C要求,我的一位同事想要使用指针声明我遇到了一些麻烦。 Misra(安全关键指南)不会让程序员使用指针,而是让我们操作数组字节。他打算生成一个指向字节数组的指针(因此我们不会在堆栈上传递实际的数组。)
// This is how I would normally do it
//
void Foo(uint8_t* pu8Buffer, uint16_t u16Len)
{
}
// This is how he has done it
//
void Foo(uint8_t (*pu8Buffer)[], uint16_t u16Len)
{
}
调用函数看起来像;
void Bar(void)
{
uint8_t u8Payload[1024]
uint16_t u16PayloadLen;
// ...some code to fill said array...
Foo(u8Payload, u16PayloadLen);
}
但是,当在Foo()中访问pu8Buffer时,数组是错误的。显然没有超越预期。数组在调用函数中是正确的,但不在Foo()
中我认为他创建了一个指向字节的指针数组,而不是指向字节数组的指针。
有人在意澄清吗? Foo(& u8Payload,u16PayloadLen);也不起作用。
答案 0 :(得分:4)
在void Foo(uint8_t (*pu8Buffer)[], uint16_t u16Len)
中,pu8Buffer
是指向uint8_t
的(不完整)数组的指针。 pu8Buffer
的类型不完整;它是指向大小未知的数组的指针。它可能不适用于需要大小的表达式(例如指针算术;不允许pu8Buffer+1
)。
然后*pu8Buffer
是一个大小未知的数组。由于它是一个数组,因此在大多数情况下会自动转换为指向其第一个元素的指针。因此,*pu8Buffer
成为指向数组的第一个uint8_t
的指针。已转换*pu8Buffer
的类型已完成;它是指向uint8_t
的指针,因此可用于地址算术; *(*pu8Buffer + 1)
,(*pu8Buffer)[1]
和1[*pu8Buffer]
都是uint8_t
以外*pu8Buffer
的有效表达式。
答案 1 :(得分:3)
我认为你指的是MISRA-C:2004规则17.4(或2012规则18.4)。甚至像我这样忠于MISRA的人也会发现这条规则完全是胡说八道。该规则的基本原理是这一点(MISRA-C:2012 18.4):
“使用数组下标语法的数组索引,ptr [expr],是 指针算术的首选形式,因为它通常更清晰 因此比指针操作更不容易出错。任何明确的 计算的指针值有可能无意中访问或 内存地址无效。阵列也可以实现这种行为 索引,但下标语法可以简化人工审查的任务。
C中的指针运算可能会让新手混淆表达式
ptr+1
可能被错误地解释为添加1ptr
中的地址。实际上新的内存地址取决于 指针目标的大小(以字节为单位)。这种误解可能导致 如果未正确应用sizeof,则出现意外行为。“
所以这一切都归结为MISRA担心初学者程序员会混淆ptr + 1来获得我们在编写(uint8_t*)ptr + 1
时会得到的结果。在我看来,解决方案是教育新手程序员,而不是限制专业程序员(但是如果你聘请新手程序员来编写符合MISRA标准的安全关键软件,那么理解指针算法可能是你问题中最不重要的)。
通过写出与此规则的永久偏差来解决此问题!
如果您因为未知的原因不想偏离,但要使您当前的代码符合MISRA,只需将该功能重写为
void Foo(uint8_t pu8Buffer[], uint16_t u16Len)
然后用pu8Buffer[something]
替换所有指针算术。然后突然根据MISRA:2004示例套件,代码100%兼容MISRA。它在功能上也与你已有的100%相当。