Can you always access suitably aligned buffer containing structure data in C?

279 Views Asked by At

I have data of a structure type in C:

typedef struct {
  type0 field0;
  type1 field1;
} foo_struct;

foo_struct foo;

Now let's say I have a buffer allocated in some fashion in virtual memory, of size sizeof(foo_struct)

char *buf = <some allocation method>

Then I copy the data from foo to buf, for example by memcpy.

Then I want to access the fields in buf like so:

((foo_struct *)buf)->fieldn

Is this guaranteed (by the C11 standard) to always work?

People in another question (on a different main topic) , seem to be saying, yes this is guaranteed to work, especially provided that buf is well-aligned, like, on a page boundary.

Yes it is practically assured. But I think, no matter how well it is "aligned", page boundary, or what not, there is no 100% guarantee by the standard. Is there?

3

There are 3 best solutions below

0
On

It kind of depends on what <some allocation method> is. Here is what the standard [7.22.3] says about malloc :

The pointer returned if the allocation succeeds is suitably aligned so that it may be assigned to a pointer to any type of object with a fundamental alignment requirement and then used to access such an object or an array of such objects in the space allocated (until the space is explicitly deallocated).

So according to the standard, you can do what you're asking when using malloc. Most other well written memory allocators should satisfy this requirement as well.

6
On

Can you always access suitably aligned buffer containing structure data in C?

If the buffer's in dynamic memory, then yes.

char *buf = malloc(1000000);
if(buf)
   ((foo_struct *)buf)->fieldn

is essentially like

foo_struct *buf = malloc(1000000);
if(buf)
    buf->fieldn

This is guaranteed to work.

If the buffer is statically allocated or automatic, then no. Aliasing rules (6.5p7) prevent you from doing:

/*static*/ _Alignas(foo_struct) char buf[100000000];
foo_struct* foo_p = &buf;
if(foo_p)
    foo_p->fieldn

even when the alignment of the buffer is sufficient.

(Note: 1 followed by many 0 == large enough)

0
On

The aliasing rules of C89 were historically interpreted as merely identifying the minimum cases where all conforming implementations would be required to recognize aliasing, which they in turn limited to cases which would be useful on all implementations. It was obvious to everyone involved at the time that other forms of aliasing are useful on many platforms, and that quality implementations should recognize such forms when practical.

While C99 and C11 have pretended to describe more fully the cases where quality implementations should recognize aliasing, the authors have never made any real effort to handle some cases that should be easy to handle, and are absolutely vital in system programming. Since even pessimistic handling of such cases would generally have only a small impact on performance, compiler writers used to approach such things with caution, and the notion that such code should work used to be viewed as common sense.

Memory management with approaches like you describe (rather than malloc/free) will work reliably if compiler writers use some common sense, since most useful aliasing optimizations (as opposed to code-breaking aliasing "optimizations") aren't going to require moving object accesses across pointer type conversions. Unfortunately, for whatever reason, it isn't fashionable for compilers to include a "common-sense" aliasing mode.

While it would be rather useless to specify alignment of objects' storage if one can't use that storage in a way that benefits from it, the Standard doesn't mandate that compilers allow such use of the storage underlying objects declared with alignment directives. Because it is fashionable for compiler writers to pay more attention to what the Standard doesn't require than to what usefulness would require, the only way to be assured with many compilers that any kind of custom memory-management code will work will be to either use compiler-specific directives or else to disable aliasing optimizations altogether.