Pointer casting problem

I want to write integer values into a data byte array. This byte array is part of a struct.
In my program, this should be done inside a function.
The function parameter should be the pointer to the data byte array, not to the struct.

The pseudo code sketch below shows the problem.
The program crashes by writing the integer.
But if I use the pointer from the struct directly instead of the function parameter, it works.
The pointer "astruct.data" and the pointer "data" seem to have the same value.
Because of this both attempts should work.

Can anyone explain this?

typedef struct {
    uint8_t a;  
    uint8_t data[4];
} astruct_t;
astruct_t astruct;

void afunc(uint8_t* data) {
    //*((uint32_t*)(data)) = 1;  // does not work, program crashes 
    *((uint32_t*)(astruct.data)) = 2;  // this instead works
    Serial.println("Still running...");
    Serial.println((uint32_t)astruct.data);
    Serial.println((uint32_t)data);  // same adress as before
    Serial.println(*((uint32_t*)(astruct.data)));
}

void setup() {
    Serial.begin(9600); 
    while (!Serial); 
    
    afunc((uint8_t*)astruct.data);  // (uint8_t*)&astruct.data works equally
}

void loop() {
}

Arduino IDE 1.8.0
Arduino MKR1000

Did you mean afunc((uint8_t*)&astruct.data[0]);?

Your description isn't very clear to me.

I mean the byte array pointer "astruct.data" should go inside the function.

I have tried:

afunc((uint8_t*)&astruct.data);

as well as:

afunc((uint8_t*)astruct.data);

and also:

afunc((uint8_t*)&astruct.data[0]);

In all cases the program stopped inside afunc at the first line:

void afunc(uint8_t* data) {
    *((uint32_t*)(data)) = 1;
    ...

The interesting thing: with the clang compiler, the pseudo code above works.

RoberZ:
I mean the byte array pointer "astruct.data" should go inside the function.

I have tried:

afunc((uint8_t*)&astruct.data);

as well as:

afunc((uint8_t*)astruct.data);

and also:

afunc((uint8_t*)&astruct.data[0]);

In all cases the program stopped inside afunc at the first line:

void afunc(uint8_t* data) {

((uint32_t)(data)) = 1;
   ...




The interesting thing: with the clang compiler, the pseudo code above works.

I cannot see why your example doesn't work, and neither can Arduino 1.6.13, that is to say your program doesn't crash it.

astruct.data is a uint_t*, so the cast

(uint8_t*)astruct.data

is superfluous but harmless.

After I ran both versions one at a time as well as neither and together w/o incident I changed the 1 and 2 to numbers I like better, 42 and 107. Your code cut and paste.

-- a7

If I remove the slashes before
*((uint32_t*)(data)) = 1;
It compiles without complaint.
But it doesn't print "Still running..." if it runs on my mkl1000.
Have you run it on metal?

RoberZ:
…it doesn’t print “Still running…” if it runs on my mkl1000.
Have you run it on metal?

I don’t have an mkl1000. Hood Type Dishwasher?

I did run it on an Arduino UNO.

– a7

I have tried so many things to find the cause of the problem.
Thats why I double casted every thing and used brackets everywhere.

Now that you confirm that the code works.
I have no idea what to try next.
May be it is a compiler bug.

The next step is to post the entire program and see what can be found.

-jim lee

The sketch in the first post is the full program that reproduces the problem.
The sketch compiles without warning and error if I remove the slashes before
((uint32_t)(data)) = 1;
This line is valid code and should work.

But if I run it on my mkr1000 it doesn't print "Still running ..." over the serial port.
This means it crashed on this line.
I have tried the program without serial communication and switched to tcp communication.
The program showed the same symptoms.

Use a union for this type of thing.

typedef struct {
    uint8_t a; 
    union {
      uint8_t asBytes[4];
      uint32_t asLong;
    } data;
} astruct_t;

You KNOW that you are passing an array to the function. Why the hell wouldn't you just use array syntax in the function? Isn't data[0] a hell of a lot easier to understand (and get right) than ((uint32_t)(data))?

Alignment? When you use atruct.data, the compiler knows that the dest is not 32bit aligned, and fixes it. When you cast a runtime pointer, it can't tell, and attempts to do a 32bit store to an odd address, and fails...

There is no alignment problematic on 8-Bit processors.

@PaulS: Of course, you are right. But sometimes there are rationale for this kind of code.

@westfw: If it is true, how can I deal with this.

@Whandall: the mkr1000 is a 32 bit processor.

Questions:

  1. Why not pass the function a pointer to the structure and then use:
structPtr->data

inside the function?

  1. Is your goal to have an array of 4 uint8_t values, or are you trying to build a uint32_t by putting one byte into the array at a time? I think the latter is inadvisable due to considerations of endianness and word alignment. A more portable technique would be:
word32 = (uint32_t) byte3<<24 | byte2<<16 | byte1<<8 | byte0;

[how to deal with unaligned data]

That depends on what you are trying to do. If the structures are arbitrary, the easiest and fastest (code-wise) thing to do is let the compiler pad the structure so that things accesses as a long will be aligned properly.

typedef struct {
    uint8_t data[4];
    uint8_t a;  
} astruct_t;

would do that. I think the “union” example would also do that, or you could make “data” a long, and do your cast when needing to access it as bytes, instead. Or you can do it manually:
typedef struct {
uint8_t a;
uint8_t pad[3]; // make sure “data” is 32-bit aligned.
uint8_t data[4];
} astruct_t;

If your structure fields must be unaligned (ie they map onto some real-world data, like a network packet), you need to implement macros/functions like GETLONG() and GETSHORT():

static inline GETLONG(uint8_t *p) {
  uint32_t v ;
  v = (p[3] << 24) + (p[2]<<16) + (p[1]<<8) + p[0]);
  return v;
}

There may or may not be tricks or special instructions or built-in compiler functions that will do this as efficiently as possible. (I can’t think of any offhand, for gcc+ARMCM0. But if your memory is slow compared to the CPU cycle time, you may want to replace the 4 byte reads with two 32bit reads, somehow.)

@gfvalvo
Thank you, everything works with 32-bit aligment.

Solved.