Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
528 views
in Technique[技术] by (71.8m points)

memory management - Opaque types allocatable on stack in C

When designing a C interface, it is common to let into the public interface (.h) only what needs to be known by the user program.

Hence for example, the inner components of structures should remain hidden if the user program does not need to know them. This is indeed good practice, as the content and behavior of the struct could change in the future, without affecting the interface.

A great way to achieve that objective is to use incomplete types.

typedef struct foo opaqueType;

Now an interface using only pointers to opaqueType can be built, without the user program ever needing to know the inner working of struct foo.

But sometimes, it can be required to allocate such structure statically, typically on stack, for performance and memory fragmentation issues. Obviously, with above construction, opaqueType is incomplete, so its size is unknown, so it cannot be statically allocated.

A work around is to allocate a "shell type", such as :

typedef struct { int faketable[8]; } opaqueType;

Above construction enforces a size and an alignment, but doesn't go farther into describing what the structure really contains. So it matches the objective of keeping the type "opaque".

It mostly works. But in one circumstance (GCC 4.4), the compiler complains that it breaks strict-aliasing, and it generates buggy binary.

Now, I've read a ton of things about strict aliasing, so I guess I understand now what it means.

The question is : is there a way to define an opaque type which can nonetheless be allocated on stack, and without breaking strict aliasing rule ?

Note that I've attempted the union method described in this excellent article but it still generates the same warning.

Note also that visual, clang and gcc 4.6 and later don't complain and work fine with this construction.

[Edit] Information complement :

According to tests, the problem only happens in the following circumstances :

  • Private and public type different. I'm casting the public type to private inside the .c file. It doesn't matter apparently if they are part of the same union. It doesn't matter if the public type contains char.
  • If all operations on private type are just reads, there's no problem. Only writes cause problems.
  • I also suspect that only functions which are automatically inlined get into trouble.
  • Problem only happens on gcc 4.4 at -O3 setting. -O2 is fine.

Finally, my target is C90. Maybe C99 if there really is no choice.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

You can force the alignment with max_align_t and you can avoid the strict aliasing issues using an array of char since char is explicitly allowed to alias any other type.

Something along the lines of:

#include <stdint.h>
struct opaque
{
    union
    {
        max_align_t a;
        char b[32]; // or whatever size you need.
    } u;
};

If you want to support compiler that do not have the max_align_t, or if you know the alignment requirements of the real type, then you can use any other type for the a union member.

UPDATE: If you are targetting C11, then you may also use alignas():

#include <stdint.h>
#include <stdalign.h>
struct opaque
{
    alignas(max_align_t) char b[32];
};

Of course, you can replace the max_align_t with whatever type you think appropriate. Or even an integer.

UPDATE #2:

Then, the use of this type in the library would be something along the lines of:

void public_function(struct opaque *po)
{
    struct private *pp = (struct private *)po->b;
    //use pp->...
}

This way, since you are type-punning a pointer to char you are not breaking the strict aliasing rules.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...