c指针递减safe/unsafe?

c pointer decrement safe/unsafe?

正在分析一些代码:

static volatile UCHAR *pucSndBufferCur;

eMBErrorCode eMBRTUSend( UCHAR ucSlaveAddress, const UCHAR * pucFrame, USHORT usLength )
{
    if( eRcvState == STATE_RX_IDLE )
    {
        /* First byte before the Modbus-PDU is the slave address. */
        pucSndBufferCur = ( UCHAR * ) pucFrame - 1;

        /* Now copy the Modbus-PDU into the Modbus-Serial-Line-PDU. */
        pucSndBufferCur[0] = ucSlaveAddress;

看起来不安全,如果地址(pucFrame - 1)中的内存已经被其他变量使用并且覆盖它可能会导致故障怎么办。

您怎么看,这样的代码可以使用还是错误的方式永远不应该使用?

语句 pucSndBufferCur = ( UCHAR * ) pucFrame - 1; 的行为将是 undefined 除非 pucFrame 是一个指向数组中元素的指针,或者就在数组,并且它前面有一个元素。为此,标量可以被视为长度为 1 的数组。

这是因为指针算法只在数组中有效。

您也放弃了 const,这可能会再次将程序置于未定义状态,具体取决于您对指针所做的操作(C 和 C++ 之间的规则不同)。

请澄清您问题的意义。 无论如何,代码有点不安全,但不是因为你所说的原因。 为了使其更健壮,您应该针对 null 进行测试。 指针运算有点危险,但我认为这段旧代码会根据上下文做出一些假设。

它似乎来自:

FreeModbus 库:Modbus ASCII/RTU 的可移植 Modbus 实现。 3 * 版权所有 (c) 2006 Christian Walter

所以是的,2006 年的代码可能很危险,但另一方面它已经使用了 15 年......所以它可以使用。 (旧代码通常可以工作,但如果你不修改它..)

来自这条评论

    /* First byte before the Modbus-PDU is the slave address. */
    pucSndBufferCur = ( UCHAR * ) pucFrame - 1;

我们可能会推断该函数希望调用者保证这一点。在这种情况下,if 调用者满足要求,then 代码是安全的。

如果调用者保证这一点,则函数安全。这就像您无法有效检查的任何其他 pre-requisite:如果违反要求,他们就会有 UB,这是调用者的错

作为比较,如果您违反 pre-requisite 其指针参数是从 malloc()/realloc() 返回的,free() 可能会有类似的行为(并且可能更糟 side-effects)。

It looks like unsafe, what if memory in address (pucFrame - 1 ) is already used for other variable and overwriting it could cause malfunctions

不安全 - 它是 C。正确使用它的责任在你。如果你用错了(传递了一个不符合规定要求的指针),就会出现故障,那就是你的错。

What do you think, is code like this can be used or its wrong way and never should be used ?

可以放心使用,就像free()可以放心使用一样。它可以被错误地使用,就像 free() 可以被误用一样。 从不 使用这样的代码的唯一原因是您不相信自己会正确使用它。


实用的建议是查看这个 pucFrame 指针的来源,验证它始终保证满足要​​求,然后确保在指针通过您的过程中没有破坏任何东西代码。

永远不要假设某些东西位于特定地址之前或之后,除非您正在检查数组范围内的东西。

MISRA-C:2004,规则 17.4(必需)或 MISRA-C:2012,规则 18.4(必需)数组索引应是指针运算的唯一允许形式。

强烈建议您使用像 PC-Lint 这样的静态分析器来检查代码的完整性,以避免未定义的行为。

此外,您可以将所有必需的东西放入一个结构中,然后将一个结构指针传递给该函数。但即便如此,也不要使用 ++ 或 -- 访问结构成员,因为你不知道添加的填充是依赖于编译器的