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() {
}
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.
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?
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.
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...
Why not pass the function a pointer to the structure and then use:
structPtr->data
inside the function?
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:
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.
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():
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.)