2

由于 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); 也不行。

4

2 回答 2

4

void Foo(uint8_t (*pu8Buffer)[], uint16_t u16Len),pu8Buffer是指向 的(不完整)数组的指针uint8_tpu8Buffer类型不完整;它是一个指向大小未知的数组的指针。它不能用于需要大小的表达式(例如指针算术;pu8Buffer+1不允许)。

然后*pu8Buffer是一个大小未知的数组。由于它是一个数组,因此在大多数情况下它会自动转换为指向其第一个元素的指针。因此,*pu8Buffer成为指向第一个uint8_t数组的指针。转换的类型*pu8Buffer是完整的;它是一个指向 的指针uint8_t,因此它可以用于地址算术;*(*pu8Buffer + 1), (*pu8Buffer)[1], 和1[*pu8Buffer]都是uint8_t超出的有效表达式*pu8Buffer

于 2013-09-30T19:07:57.967 回答
3

我认为您指的是 MISRA-C:2004 规则 17.4(或 2012 规则 18.4)。即使是像我这样喜欢 MISRA 的人也觉得这条​​规则完全是胡说八道。该规则的基本原理是这样的(MISRA-C:2012 18.4):

“使用数组下标语法 ptr[expr] 的数组索引是指针算术的首选形式,因为它通常比指针操作更清晰,因此更不容易出错。任何显式计算的指针值都有可能访问意外或无效的内存地址. 数组索引也可能出现这种行为,但下标语法可能会减轻手动审查的任务。

C 中的指针运算可能会让新手感到困惑 该表达式 ptr+1可能会被错误地解释为将 1 加到ptr. 事实上,新的内存地址取决于指针目标的字节大小。如果 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% 相同。

于 2013-10-02T14:44:09.760 回答