Reputation: 26661
#include <stdio.h>
void main(void)
{
char array[4] = {0, 1, 2, 3};
float *fpek;
int i;
for(i=0;i<4;i++)
{
fprintf(stderr,"i = %d ", i);
fpek = (float *)(&array[i]);
fprintf(stderr, "fpek = %lx ", (unsigned long) fpek);
*fpek = (float) i + 10;
fprintf(stderr, "*fpek = %.2f\n", *fpek);
}
}
$ cc alignment.c
$ a.out
i = 0 fpek = effff0fc *fpek = 10.00
i = 1 fpek = effff0fd Bus error
The code above was found in C programming practice material. I understand the statements per se but I don't really see what the author is trying to illustrate. Why is there a bus error?
Upvotes: 3
Views: 2071
Reputation: 66244
Aligned or not, this is just plain wrong:
fpek = (float *)(&array[i]); // invalid for i>0, questionable for i==0
fprintf(stderr, "fpek = %lx ", (unsigned long) fpek);
*fpek = (float) i + 10;
Since array
is at-best four chars wide, even if your system-float
is 4 bytes you're immediately addressing (likely unaligned) data i
bytes out of range of your array when assigning with i>0
. As soon as you dereference that pointer you're either going to bus-error due to misalignment or, on the off-chance your architecture has no alignment restrictions for float
(and most do) you'll start stomping on stack variables with the assignment that follows.
To highlight the error the author is probably trying to get across (that alignment can be important) without introducing undefined behavior aside from that, consider something like this instead:
#include <stdlib.h>
#include <stdio.h>
int main()
{
/* note: defined to hold enough bytes for *two* floats. */
float *fpek = NULL;
char array[2*sizeof(*fpek)];
int i;
/* fill with incrementing values */
for (i=0; i<sizeof(array)/sizeof(array[0]);++i)
array[i] = (i+1);
// now walk the array, one char at a time,
// casting the address of the current element
// to a float pointer and try to read/write it.
fprintf(stderr, "array = %p, size=%lu\n", array, sizeof(array));
for(i=0;i<sizeof(*fpek);++i)
{
fprintf(stderr,"i = %d, ", i);
fpek = (void*)(array+i);
fprintf(stderr, "fpek = %p, ", fpek);
*fpek = i+10;
fprintf(stderr, "*fpek = %.2f\n", *fpek);
}
return 0;
}
No matter how wide/narrow a float
is on your system, this will work without introducing undefined behavior specific to walking past the end of your char array. It will quite possibly still bus-error, but at least the UB for exceeding your array subscript is solved.
Running this on my Mac Air (Intel 64bit CPU) produces no bus error:
array = 0x7fff5fbff868, size=8
i = 0, fpek = 0x7fff5fbff868, *fpek = 10.00
i = 1, fpek = 0x7fff5fbff869, *fpek = 11.00
i = 2, fpek = 0x7fff5fbff86a, *fpek = 12.00
i = 3, fpek = 0x7fff5fbff86b, *fpek = 13.00
As you can see, my platform is not particularly finicky about float
alignment. your results can (and judging by your prior output, likely will) vary.
Note: the (void*)
cast of fpek = (void*)(array+i);
may look odd, but this is C, so I can get away with it. The reason I did it was to allow you to demonstrate the other floating point types and see if they have alignment restrictions. As-written you can change just the declaration of fpek
at the top of the function to double
:
double *fpek = NULL;
Then rerun the program. On my system this produces:
array = 0x7fff5fbff860, size=16
i = 0, fpek = 0x7fff5fbff860, *fpek = 10.00
i = 1, fpek = 0x7fff5fbff861, *fpek = 11.00
i = 2, fpek = 0x7fff5fbff862, *fpek = 12.00
i = 3, fpek = 0x7fff5fbff863, *fpek = 13.00
i = 4, fpek = 0x7fff5fbff864, *fpek = 14.00
i = 5, fpek = 0x7fff5fbff865, *fpek = 15.00
i = 6, fpek = 0x7fff5fbff866, *fpek = 16.00
i = 7, fpek = 0x7fff5fbff867, *fpek = 17.00
Upvotes: 4
Reputation: 39
The problem is that you create a char array (the memory allocated is 4 bites) and you point float pointer to the beginning of the array. You are trying to read after add 10 to the pointer (because the pointer is from the type float it increments with sizeof(float)*10 and this is already outside of the array. Therefore it is expected to generate error.
Try removing this line *fpek = (float) i + 10;
. And try it.
Another thing to change could be fprintf(stderr, "*fpek = %.2f\n", *fpek);
to fprintf(stderr, "*fpek = %c\n", *fpek);
Upvotes: 0
Reputation: 409432
Not all systems can do unaligned access to memory, for example the old Motorola M68000 could not do it. The typical error when that happened would be a bus error. Those systems are getting uncommon today though, and modern (and not so modern) compilers should be able to handle it properly.
Upvotes: 3