C 指针减法,32位ARM,负距离报告为正

C 指针减法,32位ARM,负距离报告为正,c,pointers,assembly,embedded,cortex-a8,C,Pointers,Assembly,Embedded,Cortex A8,当执行指针减法且第一个指针小于第二个指针时,ARM处理器会出现下溢错误 示例代码: #include <stdint.h> #include <stdbool.h> uint8_t * p_formatted_data_end; uint8_t formatted_text_buffer[10240]; static _Bool Flush_Buffer_No_Checksum(void) { _Bool system_failure_occu

当执行指针减法且第一个指针小于第二个指针时,ARM处理器会出现下溢错误

示例代码:

#include <stdint.h>
#include <stdbool.h>

uint8_t * p_formatted_data_end;
uint8_t   formatted_text_buffer[10240];

static _Bool
Flush_Buffer_No_Checksum(void)
{
    _Bool       system_failure_occurred = false;
    p_formatted_data_end = 0; // For demonstration puposes.
    const signed int  length =
        p_formatted_data_end - &formatted_text_buffer[0];
    if (length < 0)
    {
        system_failure_occurred = true;
    }
    //...
    return true;
}
如果结果为负值,将设置
CPSR
寄存器中的
N
位。 参考:第A4.1.106小节

让我们:

寄存器
R5
将具有值
0x7FD5395C
CPSR
寄存器的
N
位为0,表示结果不是负值

Windows 7计算器应用程序报告为负数,但仅当表示为64位时:
ffffffffff7fd5395c

作为一个实验,我使用
ptrdiff\u t
类型作为长度,并生成了相同的汇编语言

问题:

  • 这是有效的行为,将指针减法的结果 下溢
  • 建议将距离视为负值的数据类型是什么
  • 平台:
    目标处理器:ARM Cortex A8(TI AM3358)
    编译器:IAR 7.40
    开发平台:Windows7

    这是有效的行为,使指针减法的结果变为下溢吗

    是的,因为您案例中的行为未定义。在那里,任何行为都是有效的。正如在注释中所观察到的,两个指针之间的差异仅针对指向同一数组对象的元素的指针,或指向数组对象最后一个元素的指针(C2011,6.5.6/9)

    建议将距离视为负值的数据类型是什么


    在定义的地方,减去两个指针的结果被指定为
    ptrdiff\u t
    类型,一种实现定义大小的有符号整数类型。如果计算
    p1-p2
    ,其中
    p1
    指向一个数组元素,而
    p2
    指向同一数组的后一个元素,则结果将是一个负数,表示为
    ptrdiff\u t

    ,尽管这是另一个答案中所述的UB,大多数C实现都会简单地减去这些指针的大小(或者可能对它们的字大小使用适当的算法,如果两个操作数都是
    近的
    /
    远的
    /
    大的
    指针,它们的字大小也可能不同)。结果应该适合内部,通常是手臂上的:

    因此,在这种特殊情况下,代码的问题只是将一个
    无符号int
    值视为有符号,它不适合。如您问题中所述,
    格式化文本缓冲区的地址是
    0x802AC6A5
    ,它位于
    无符号int
    中,但两个补码形式的
    (int)0x802AC6A5
    实际上是一个负数(
    -0x7FD5395B
    )。因此,从
    0
    中减去一个负数将返回一个正数
    int

    如果两个操作数之间的距离小于
    0x7FFFFFFF
    ,则有符号32位整数减法将正常工作,并且可以合理预期数组小于该值:

    // this will work
    const int length = &formatted_text_buffer[0] - &formatted_text_buffer[100];
    
    或者,如果确实需要对不适合有符号32位整数的指针进行减法运算,请改用
    long

    // ...but I doubt you really want this
    const long long length = (long long)p_formatted_data_end - 
         (long long)&formatted_text_buffer[0];
    

    p\u格式化的数据\u end=0
    显然没有指向数组
    格式化的\u text\u buffer
    (或后面的字节)中的任何位置。无关实体上的指针算法是未定义的行为。注意,处理器标志寄存器中的符号位反映了登记器或操作的MS位,而不管您是否考虑到未签名或登记的值。但是,否定结果是否仍然是可行的?如果从指向
    格式化文本缓冲区[0]
    (8位数组)的指针中减去指向
    格式化文本缓冲区[3]
    的指针,则得到的
    -3
    会告诉您它们之间相隔多少个元素。不指向同一数组的两个指针之间的算术公式为UB。您取的是负数,从零减去它,得到一个正数。哪个是正确的,有什么问题吗?0 - (-5) = +5. 小学数学。你说的下溢是什么意思?这个数学既不会产生无符号溢出(C),也不会产生有符号溢出(V),正如您所指出的,N是零,Z是零。所有这些都与ARM无关,这是一个简单的数学。当我与同事讨论这个问题时,没有足够的位。如果我有一个巨大的数组,那么我需要32位的绝对差和一个额外的符号位。IAR编译器仅使用32位作为其ptrdiff_t类型,这是不够的。由于使用32位,最大数组大小将为31位,以便为符号保留一位(当使用32位作为ptrdiff_t类型时)。@ThomasMatthews,要么
    ptrdiff_t
    有足够的位来表示行为已定义的所有指针差分操作的结果,要么实现不一致。实际上,(一致性)实现根据支持的最大数组大小为
    ptrdiff\u t
    选择一个大小。如果您的实施事实上不符合,那么我们没有任何基础来回答这个问题;否则,我给出的答案适用。@ThomasMatthews,或者,从另一个角度来看,标准还规定“如果[指针差异的]结果不能在[type
    ptrdiff\u t
    ]的对象中表示,则行为是未定义的。”如果
    ptrdiff\u t
    不够宽,无法表示所有指针差异,否则将定义行为,这将导致某些差异未定义。在适用的情况下,没有理由假设有任何方法可以将差值作为负数。
    R0 == 0x00000000
    R1 == 0x802AC6A5
    
    typedef int ptrdiff_t;
    
    // this will work
    const int length = &formatted_text_buffer[0] - &formatted_text_buffer[100];
    
    // ...but I doubt you really want this
    const long long length = (long long)p_formatted_data_end - 
         (long long)&formatted_text_buffer[0];