指针减法,32 位 ARM,负距离报告为正
Pointer subtraction, 32-bit ARM, negative distance reported as postive
当执行指针减法并且第一个指针小于第二个时,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;
}
IAR编译器生成的汇编代码为:
807 static _Bool
808 Flush_Buffer_No_Checksum(void)
809 {
\ Flush_Buffer_No_Checksum:
\ 00000000 0xE92D4070 PUSH {R4-R6,LR}
\ 00000004 0xE24DD008 SUB SP,SP,#+8
810 _Bool system_failure_occurred = false;
\ 00000008 0xE3A04000 MOV R4,#+0
811 p_formatted_data_end = 0; // For demonstration purposes.
\ 0000000C 0xE3A00000 MOV R0,#+0
\ 00000010 0x........ LDR R1,??DataTable3_7
\ 00000014 0xE5810000 STR R0,[R1, #+0]
812 const signed int length =
813 p_formatted_data_end - &formatted_text_buffer[0];
\ 00000018 0x........ LDR R0,??DataTable3_7
\ 0000001C 0xE5900000 LDR R0,[R0, #+0]
\ 00000020 0x........ LDR R1,??DataTable7_7
\ 00000024 0xE0505001 SUBS R5,R0,R1
814 if (length < 0)
\ 00000028 0xE3550000 CMP R5,#+0
\ 0000002C 0x5A000009 BPL ??Flush_Buffer_No_Checksum_0
815 {
816 system_failure_occurred = true;
\ 00000030 0xE3A00001 MOV R0,#+1
\ 00000034 0xE1B04000 MOVS R4,R0
减法指令SUBS R5,R0,R1
等价于:
R5 = R0 - R1
如果结果为负,CPSR
寄存器中的 N
位将被设置。
参考:ARM Architecture Reference Manual 的 A4.1.106 子部分
设:
R0 == 0x00000000
R1 == 0x802AC6A5
注册 R5
将具有值 0x7FD5395C
。
CPSR
寄存器的N
位为0,表示结果不为负。
Windows 7 计算器应用程序报告负数,但仅当表示为 64 位时:FFFFFFFF7FD5395C
.
作为实验,我使用 ptrdiff_t
类型作为长度,生成了相同的汇编语言。
问题:
- 指针减法的结果是否有效
下溢?
- 将距离视为负数的推荐数据类型是什么?
平台:
目标处理器:ARM Cortex A8 (TI AM3358)
编译器:IAR 7.40
开发平台:Windows7.
Is this valid behavior, to have the result of pointer subtraction to underflow?
是的,因为您的案例中的行为未定义。 任何 行为在那里都是有效的。正如评论中所观察到的,两个指针之间的差异仅针对指向同一数组对象元素的指针或指向数组对象最后一个元素的指针定义(C2011,6.5.6/9)。
What is the recommended data type to view the distance as negative?
定义的地方,指定两个指针相减的结果为ptrdiff_t
类型,大小为implementation-defined的有符号整数类型。如果计算 p1 - p2
,其中 p1
指向一个数组元素,而 p2
指向同一数组的后面的元素,那么结果将是一个负数,表示为 ptrdiff_t
.
虽然这是另一个答案中所述的 UB,但大多数 C 实现将简单地减去这些指针 ptrdiff_t
大小(或者可能使用适当的算法来计算它们的字长,如果两个操作数都是near
/far
/huge
指针)。结果应该适合 ptrdiff_t
, which is usually a typedef-ed int
on ARM:
typedef int ptrdiff_t;
因此,在这种特殊情况下,您的代码存在的问题只是您将 unsigned int
值视为已签名,但它不适合。正如您在问题中指定的那样,formatted_text_buffer
的地址是 0x802AC6A5
,它位于 unsigned int
内,但 (int)0x802AC6A5
的二进制补码形式实际上是 negative 数 (-0x7FD5395B
)。因此,从 0
中减去一个负数将 return 正数 int
正如预期的那样。
如果两个操作数相距小于 0x7FFFFFFF
,则带符号的 32 位整数减法将正常工作,并且可以合理地期望您的数组小于此值:
// this will work
const int length = &formatted_text_buffer[0] - &formatted_text_buffer[100];
或者,如果您确实需要减去不适合带符号的 32 位整数的指针,请改用 long long
:
// ...but I doubt you really want this
const long long length = (long long)p_formatted_data_end -
(long long)&formatted_text_buffer[0];
当执行指针减法并且第一个指针小于第二个时,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;
}
IAR编译器生成的汇编代码为:
807 static _Bool
808 Flush_Buffer_No_Checksum(void)
809 {
\ Flush_Buffer_No_Checksum:
\ 00000000 0xE92D4070 PUSH {R4-R6,LR}
\ 00000004 0xE24DD008 SUB SP,SP,#+8
810 _Bool system_failure_occurred = false;
\ 00000008 0xE3A04000 MOV R4,#+0
811 p_formatted_data_end = 0; // For demonstration purposes.
\ 0000000C 0xE3A00000 MOV R0,#+0
\ 00000010 0x........ LDR R1,??DataTable3_7
\ 00000014 0xE5810000 STR R0,[R1, #+0]
812 const signed int length =
813 p_formatted_data_end - &formatted_text_buffer[0];
\ 00000018 0x........ LDR R0,??DataTable3_7
\ 0000001C 0xE5900000 LDR R0,[R0, #+0]
\ 00000020 0x........ LDR R1,??DataTable7_7
\ 00000024 0xE0505001 SUBS R5,R0,R1
814 if (length < 0)
\ 00000028 0xE3550000 CMP R5,#+0
\ 0000002C 0x5A000009 BPL ??Flush_Buffer_No_Checksum_0
815 {
816 system_failure_occurred = true;
\ 00000030 0xE3A00001 MOV R0,#+1
\ 00000034 0xE1B04000 MOVS R4,R0
减法指令SUBS R5,R0,R1
等价于:
R5 = R0 - R1
如果结果为负,CPSR
寄存器中的 N
位将被设置。
参考:ARM Architecture Reference Manual 的 A4.1.106 子部分
设:
R0 == 0x00000000
R1 == 0x802AC6A5
注册 R5
将具有值 0x7FD5395C
。
CPSR
寄存器的N
位为0,表示结果不为负。
Windows 7 计算器应用程序报告负数,但仅当表示为 64 位时:FFFFFFFF7FD5395C
.
作为实验,我使用 ptrdiff_t
类型作为长度,生成了相同的汇编语言。
问题:
- 指针减法的结果是否有效 下溢?
- 将距离视为负数的推荐数据类型是什么?
平台:
目标处理器:ARM Cortex A8 (TI AM3358)
编译器:IAR 7.40
开发平台:Windows7.
Is this valid behavior, to have the result of pointer subtraction to underflow?
是的,因为您的案例中的行为未定义。 任何 行为在那里都是有效的。正如评论中所观察到的,两个指针之间的差异仅针对指向同一数组对象元素的指针或指向数组对象最后一个元素的指针定义(C2011,6.5.6/9)。
What is the recommended data type to view the distance as negative?
定义的地方,指定两个指针相减的结果为ptrdiff_t
类型,大小为implementation-defined的有符号整数类型。如果计算 p1 - p2
,其中 p1
指向一个数组元素,而 p2
指向同一数组的后面的元素,那么结果将是一个负数,表示为 ptrdiff_t
.
虽然这是另一个答案中所述的 UB,但大多数 C 实现将简单地减去这些指针 ptrdiff_t
大小(或者可能使用适当的算法来计算它们的字长,如果两个操作数都是near
/far
/huge
指针)。结果应该适合 ptrdiff_t
, which is usually a typedef-ed int
on ARM:
typedef int ptrdiff_t;
因此,在这种特殊情况下,您的代码存在的问题只是您将 unsigned int
值视为已签名,但它不适合。正如您在问题中指定的那样,formatted_text_buffer
的地址是 0x802AC6A5
,它位于 unsigned int
内,但 (int)0x802AC6A5
的二进制补码形式实际上是 negative 数 (-0x7FD5395B
)。因此,从 0
中减去一个负数将 return 正数 int
正如预期的那样。
如果两个操作数相距小于 0x7FFFFFFF
,则带符号的 32 位整数减法将正常工作,并且可以合理地期望您的数组小于此值:
// this will work
const int length = &formatted_text_buffer[0] - &formatted_text_buffer[100];
或者,如果您确实需要减去不适合带符号的 32 位整数的指针,请改用 long long
:
// ...but I doubt you really want this
const long long length = (long long)p_formatted_data_end -
(long long)&formatted_text_buffer[0];