为什么最终指针要与 int 的大小对齐?
Why final pointer is being aligned to size of int?
这是正在考虑的代码:
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
char buffer[512];
int pos;
int posf;
int i;
struct timeval *tv;
int main(int argc, char **argv)
{
pos = 0;
for (i = 0; i < 512; i++) buffer[i] = 0;
for (i = 0; i < 4; i++)
{
printf("pos = %d\n", pos);
*(int *)(buffer + pos + 4) = 0x12345678;
pos += 9;
}
for (i = 0; i < 9 * 4; i++)
{
printf(" %02X", (int)(unsigned char)*(buffer + i));
if ((i % 9) == 8) printf("\n");
}
printf("\n");
// ---
pos = 0;
for (i = 0; i < 512; i++) buffer[i] = 0;
*(int *)(buffer + 4) = 0x12345678;
*(int *)(buffer + 9 + 4) = 0x12345678;
*(int *)(buffer + 18 + 4) = 0x12345678;
*(int *)(buffer + 27 + 4) = 0x12345678;
for (i = 0; i < 9 * 4; i++)
{
printf(" %02X", (int)(unsigned char)*(buffer + i));
if ((i % 9) == 8) printf("\n");
}
printf("\n");
return 0;
}
代码的输出是
pos = 0
pos = 9
pos = 18
pos = 27
00 00 00 00 78 56 34 12 00
00 00 00 78 56 34 12 00 00
00 00 78 56 34 12 00 00 00
00 78 56 34 12 00 00 00 00
00 00 00 00 78 56 34 12 00
00 00 00 00 78 56 34 12 00
00 00 00 00 78 56 34 12 00
00 00 00 00 78 56 34 12 00
我不明白为什么
*(int *)(buffer + pos + 4) = 0x12345678;
被放入与 int 大小(4 字节)对齐的地址中。我希望在执行此命令期间执行以下操作:
- 指向缓冲区的指针,即
char*
,增加pos
的值(0、9、18、27),然后增加4。结果指针为char*
指向 char 数组索引 [pos + 4]
;
char*
括号中的指针被转换为 int*
,导致指针在 base 位置 [=21] 处寻址 4 字节大小的整数=] 和整数数组索引 [0]
;
- 结果
int*
位置以字节 78 56 34 12 的顺序存储(小端系统)。
相反,我看到括号中的指针与 int
(4 字节)的大小对齐,但是使用常量的直接寻址(参见第二段代码)按预期正常工作。
- 目标 CPU 是 i.MX287 (ARM9);
- 目标操作系统是 OpenWrt
Linux [...] 3.18.29 #431 Fri Feb 11 15:57:31 2022 armv5tejl GNU/Linux
;
- 在
Linux [...] 4.15.0-142-generic #146~16.04.1-Ubuntu SMP Tue Apr 13 09:27:15 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux
上编译,安装在虚拟机中;
- GCC 编译器版本
gcc (Ubuntu 5.4.0-6ubuntu1~16.04.12) 5.4.0 20160609
- 我编译作为整个系统映像编译的一部分,标志是
CFLAGS = -Os -Wall -Wmissing-declarations -g3
。
更新:感谢Andrew Henle,我现在替换
*(int*)(buffer + pos + 4) = 0x12345678;
与
buffer[pos + 4] = value & 0xff;
buffer[pos + 5] = (value >> 8) & 0xff;
buffer[pos + 6] = (value >> 16) & 0xff;
buffer[pos + 7] = (value >> 24) & 0xff;
我不敢相信我必须在 32 位微处理器系统上做这件事,无论它有什么架构,而且 GCC 不能正确地将 int
分割成字节或部分 int
字并对这些部分执行 RMW。
char*
pointer in the brackets is being converted to the int*
, causing resulting pointer addressing integer of 4 bytes size at base location (buffer + pos + 4
) and integer array index [0]
当不满足 int *
的对齐要求时,这会导致 未定义的行为 (UB)。
而是用 memcpy()
复制。一个好的编译器会发出有效的优化代码。
// *(int*)(buffer + pos + 4) = 0x12345678;
memcpy(buffer + pos + 4, &(int){0x12345678}, sizeof (int));
这是正在考虑的代码:
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
char buffer[512];
int pos;
int posf;
int i;
struct timeval *tv;
int main(int argc, char **argv)
{
pos = 0;
for (i = 0; i < 512; i++) buffer[i] = 0;
for (i = 0; i < 4; i++)
{
printf("pos = %d\n", pos);
*(int *)(buffer + pos + 4) = 0x12345678;
pos += 9;
}
for (i = 0; i < 9 * 4; i++)
{
printf(" %02X", (int)(unsigned char)*(buffer + i));
if ((i % 9) == 8) printf("\n");
}
printf("\n");
// ---
pos = 0;
for (i = 0; i < 512; i++) buffer[i] = 0;
*(int *)(buffer + 4) = 0x12345678;
*(int *)(buffer + 9 + 4) = 0x12345678;
*(int *)(buffer + 18 + 4) = 0x12345678;
*(int *)(buffer + 27 + 4) = 0x12345678;
for (i = 0; i < 9 * 4; i++)
{
printf(" %02X", (int)(unsigned char)*(buffer + i));
if ((i % 9) == 8) printf("\n");
}
printf("\n");
return 0;
}
代码的输出是
pos = 0
pos = 9
pos = 18
pos = 27
00 00 00 00 78 56 34 12 00
00 00 00 78 56 34 12 00 00
00 00 78 56 34 12 00 00 00
00 78 56 34 12 00 00 00 00
00 00 00 00 78 56 34 12 00
00 00 00 00 78 56 34 12 00
00 00 00 00 78 56 34 12 00
00 00 00 00 78 56 34 12 00
我不明白为什么
*(int *)(buffer + pos + 4) = 0x12345678;
被放入与 int 大小(4 字节)对齐的地址中。我希望在执行此命令期间执行以下操作:
- 指向缓冲区的指针,即
char*
,增加pos
的值(0、9、18、27),然后增加4。结果指针为char*
指向 char 数组索引[pos + 4]
; char*
括号中的指针被转换为int*
,导致指针在 base 位置 [=21] 处寻址 4 字节大小的整数=] 和整数数组索引[0]
;- 结果
int*
位置以字节 78 56 34 12 的顺序存储(小端系统)。
相反,我看到括号中的指针与 int
(4 字节)的大小对齐,但是使用常量的直接寻址(参见第二段代码)按预期正常工作。
- 目标 CPU 是 i.MX287 (ARM9);
- 目标操作系统是 OpenWrt
Linux [...] 3.18.29 #431 Fri Feb 11 15:57:31 2022 armv5tejl GNU/Linux
; - 在
Linux [...] 4.15.0-142-generic #146~16.04.1-Ubuntu SMP Tue Apr 13 09:27:15 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux
上编译,安装在虚拟机中; - GCC 编译器版本
gcc (Ubuntu 5.4.0-6ubuntu1~16.04.12) 5.4.0 20160609
- 我编译作为整个系统映像编译的一部分,标志是
CFLAGS = -Os -Wall -Wmissing-declarations -g3
。
更新:感谢Andrew Henle,我现在替换
*(int*)(buffer + pos + 4) = 0x12345678;
与
buffer[pos + 4] = value & 0xff;
buffer[pos + 5] = (value >> 8) & 0xff;
buffer[pos + 6] = (value >> 16) & 0xff;
buffer[pos + 7] = (value >> 24) & 0xff;
我不敢相信我必须在 32 位微处理器系统上做这件事,无论它有什么架构,而且 GCC 不能正确地将 int
分割成字节或部分 int
字并对这些部分执行 RMW。
char*
pointer in the brackets is being converted to theint*
, causing resulting pointer addressing integer of 4 bytes size at base location (buffer + pos + 4
) and integer array index [0]
当不满足 int *
的对齐要求时,这会导致 未定义的行为 (UB)。
而是用 memcpy()
复制。一个好的编译器会发出有效的优化代码。
// *(int*)(buffer + pos + 4) = 0x12345678;
memcpy(buffer + pos + 4, &(int){0x12345678}, sizeof (int));