Here's the code under consideration:
#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(" X", (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(" X", (int)(unsigned char)*(buffer i));
if ((i % 9) == 8) printf("\n");
}
printf("\n");
return 0;
}
And the output of code is
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
I can not get why
*(int *)(buffer pos 4) = 0x12345678;
is being placed into the address aligned to size of int (4 bytes). I expect the following actions during the execution of this command:
- pointer to buffer, which is
char*
, increased by the value ofpos
(0, 9, 18, 27) and then increased by 4. The resulting pointer ischar*
pointing to char array index[pos 4]
; 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]
;- resulting
int*
location is being stored with bytes 78 56 34 12 in this order (little endian system).
Instead I see pointer in brackets being aligned to size of int
(4 bytes), however direct addressing using constants (see second piece of code) works properly as expected.
- target CPU is i.MX287 (ARM9);
- target operating system is OpenWrt
Linux [...] 3.18.29 #431 Fri Feb 11 15:57:31 2022 armv5tejl GNU/Linux
; - compiled on
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
, installed in Virtual machine; - GCC compiler version
gcc (Ubuntu 5.4.0-6ubuntu1~16.04.12) 5.4.0 20160609
- I compile as a part of whole system image compilation, flags are
CFLAGS = -Os -Wall -Wmissing-declarations -g3
.
Update: thanks to Andrew Henle, I now replace
*(int*)(buffer pos 4) = 0x12345678;
with
buffer[pos 4] = value & 0xff;
buffer[pos 5] = (value >> 8) & 0xff;
buffer[pos 6] = (value >> 16) & 0xff;
buffer[pos 7] = (value >> 24) & 0xff;
and can't believe I must do it on 32-bit microprocessor system, whatever architecture it has, and that GCC is not able to properly slice int
into bytes or partial int
words and perform RMW for those parts.
CodePudding user response:
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]
This incurs undefined behavior (UB) when the alignments requirements of int *
are not met.
Instead copy with memcpy()
. A good compiler will emit valid optimized code.
// *(int*)(buffer pos 4) = 0x12345678;
memcpy(buffer pos 4, &(int){0x12345678}, sizeof (int));