I want to detect if a value of a void* type input function argument is an integer or a pointer. Currently, I'm passing the integer only (this is Midnight Commander source):
mc_event_raise (MCEVENT_GROUP_CORE,
"clipboard_file_from_ext_clip",
(void*)(intptr_t)clip_id);
However I would also like to sometimes pass a string (char *) in the same parameter, as extending the API would be difficult. I know that this is wrong, however, maybe there is a fully legal way to obtain such effect ? My ideas:
– the addresses are in general aligned to 4 or 8 bytes, so I've could skip such values from the clip_id (jump over them) and then decode the value? if (data % 8) then clip_id = decode_clip_id(data); ?
– maybe pointers cannot be as small as 10…20 and a simple check would be sufficient?
– other ideas…?
A problem like this can be solved with a structure containing a union and a tag.
struct EventData {
enum { EV_IS_INT, EV_IS_PTR } type;
union {
intptr_t v_int;
void * v_ptr;
};
};
So, you can then pass a pointer to this EventData, and the callback can decode whether to access v_int or v_ptr, based on the value of type.
struct EventData *ed = makeEventData (EV_IS_INT, clip_id);
mc_event_raise (MCEVENT_GROUP_CORE,
"clipboard_file_from_ext_clip",
ed);
If the event is completed asynchronously, you may need to create the event dynamically, and then the callback function would have to release the memory when the callback is done with the argument.
Windows does a lot of this hackery with their HANDLEs and resource IDs.
Your integer needs to have a defined valid range. Anything outside of that range is probably a pointer.
Almost all operating systems reserve low memory areas. You can probably assume that the 16-bit range 0 to 65535 is not a pointer. Beyond that gets iffy.
If your code needs to be portable to things like embedded RTOS or freestanding environments then you cannot assume anything about pointers.
In any case you will probably want some sort of function to create values to pass into your void* which will have an assert or abort to avoid accidentally creating invalid values.
Related
I recently wrote a wrapper for LevelDB in C and stumbled about the following problem. The LevelDB function to store data in a database looks like this:
leveldb_put(leveldb_t* db, const leveldb_writeoptions_t* options, const char* key, size_t keylen, const char* val, size_t vallen, char** errptr);
For the key and value, they use a char*. That means I would have to cast arguments that aren't char pointers. This happens often because I often store structs in the database.
After thinking about this I decided to use a void* for key and data in my wrapper function. It then looks something like this:
int db_put(db_t db, void *key, size_t keylen, void *value, size_t valuelen)
{
char *k = (char*)key;
char *v = (char*)value;
/* Call leveldb_put() here with k and v as parameters. */
return 0;
}
This way I don't have to cast the arguments I pass to my db_put() function. I think this solution is more elegant, but I guess LevelDB knew what they were doing when they choose the char pointers.
Is there a reason not to use void* to pass arbitrary data to a function?
Is there a reason not to use void* to pass arbitrary data to a
function?
No. In fact, void * exists to facilitate passing arbitrary data without the need for ugly casting. That's why ptr-to-void was standardized. In C at least. C++ is a different beast.
At LevelDB they have to deal with historical code born with char * , or pre C89 compilers, or any other veiled reason causing refactoring-inertia. Their code would work with ptrs-to-void just as well.
Note that in your version of db_put the casts should be removed as they are redundant.
The current accepted answer exists only to flatter the OP; It's actually slightly invalid.
Consider that your arbitrary struct may (most likely) have padding bytes somewhere. The value of those padding bytes is indeterminate and may or may not be insignificant.
Consider what might happen if you put a struct as key that has padding bytes, and you then attempt to get the value for that key which is otherwise equal except for the padding bytes.
Consider also how you might handle pointer members, if you choose to do so in the future.
If you intend to use a struct as key, it would be a good idea to serialise it, so you can guarantee retrieval of the corresponding value without worrying about those padding bits.
Perhaps you could pass a function pointer telling your wrapper how to serialise the key into a string...
A void* can be considered a black box that holds a pointer. By holding it
in a void* you are effectively saying that you don't care or it contains at that point, so this will allow you to make any assumption about it.
Void* is a "true" generic pointer, and can be directly assign to any particular data type without using cast.
Meanwhile, a char* explicitly specify the type of the respective object. Initially there was no char* and char* was also used to represent generic pointers. When char* is used an explicit cast is required, however the usage of char* as a generic pointer is not recommanded, because it may create confusion, like it did back there when it was hard to tell if a char* contains a string or some generic data.
Also, is legal to perform arithmeticon a char*, but not on a void*.
The downside of using void*, is given by their main usage, they can hide the actual type of the data you're storing, which prevents the compiler and other stuff to detect type errors.
In your specific situation there is no problem in using void* instead of char*, so you can use void* without worries.
Edit: Updated and reformuled the answer to correct some wrong info
You should be serializing to some standard format like json instead of dealing with raw data like that. It looks very error prone unless you always assume that the arbitrary data is just a byte buffer. In which case I would use uint8_t pointer (which is an unsigned char*) and cast all data structures to it so that the routine just thinks that it is dealing with a byte buffer.
A note on void*: i almost never ever use them. Think carefully when you introduce void pointers because in most cases you can do away with the right way of doing things which takes advantage of the standard types to avoid future bugs. The only places where you should use void* is in places like malloc() where there is not really a better way.
I have a callback function written in C that runs on a server and MUST be crash proof. That is, if expecting an integer and is passed a character string pointer, I must internal to the function determine that, and prevent getting Segmentation faults when trying to do something not allowed on the incorrect parameter type.
The function protoype is:
void callback_function(parameter_type a, const b);
and 'a' is supposed to tell me, via enum, whether 'b' is an integer or a character string pointer.
If the calling function programmer makes a mistake, and tells me in 'a' that 'b' is an integer, and 'b' is really a character string pointer, then how do I determine that without crashing the callback function code. This runs on a server and must keep going if the caller function made a mistake.
The code has to be in C, and be portable so C library extensions would not be possible. The compiler is: gcc v4.8.2
The sizeof an integer on the platform is 4, and so is the length of a character pointer.
An integer could have the same value, numerically, as a character pointer, and vice versa.
If I think I get a character pointer and its not, when I try to find the content of that, I of course get a Segmentation Fault.
If I write a signal handler to handle the fault, how do I now "clear" the signal, and resume execution at a sane place?
Did I mention that 'b' is a union defined as:
union param_2 {
char * c;
int i;
} param_to_be_passed;
I think that's about it.
Thank You for your answers.
That is not possible.
There's no way to "look" at at pointer and determine if it's valid to de-reference, except for NULL-checking it of course.
Other than that, there's no magic way to know if a pointer points at character data, an integer, a function, or anything else.
You are looking for a hack.
What ever proposal comes, do not use such things in production.
If late binding is needed take a different, a fail-safe approach.
If you're writing code for an embedded device, you would expect that all variables would reside in RAM. For example, you might have 128 kB of RAM from addresses 0x20000000 to 0x20020000. If you were passed a pointer to a memory address without this range, in regard to c, that would be another way to determine something was wrong, in addition to checking for a NULL address.
if((a == STRING) && ((b.c == NULL) || (b.c < 0x20000000) || (b.c > 0x20020000)))
return ERROR;
If you're working in a multithreaded environment, you may be able to take this a step further and require all addresses passed to callback_function come from a certain thread's memory space (stack).
If the caller says in a that the result is int, there is no great risk of crash, because:
in your case both types have the same length (be aware that this is NOT GUARANTEED TO BE PORTABLE!)
The C standards says (ISO - sect.6.3.2.3): "Any pointer type may be converted to an integer type. Except as previously specified, the result is implementation-defined. If the result cannot be represented in the integer type, the behavior is undefined.
But fortunately, most 32 bit values will be a valid integer.
Keep in mind that in the worst case, the value could be meaningless. So you it's up to you to avoid the crash, by systematically verifying consistency of the value (for example do bound controls if you use the integer to adress some array elements)
If the caller says in "a" that the result is a pointer but provides an int, it's much more difficult to avoid a crash in a portable manner.
The standard ISO says: An integer may be converted to any pointer type. Except as previously specified, the result is implementation-defined, might not be correctly aligned, might not point to an entity of the referenced type, and might be a trap representation.
In practice most of these errors are trapped by memory access exceptions at a very low system level. The behaviour being implementation defined, there's no portable way of doing it.
NOTE: This doesn't actually attempt to make the function "crash-proof", because I suspect that thats not possible.
If you are allowed to change the API, one option may be to combine the union only use an api for accessing the type.
typedef enum Type { STRING, INT } Type;
typedef struct StringOrInt {
Type type;
union { int i; char* s } value;
} StringOrInt;
void soi_set_int(StringOrInt* v, int i) {
v->type = INT;
v->value.i = i;
}
void soi_set_string(StringOrInt* v, char* s) {
v->type = STRING;
v->value.s = s;
}
Type soi_get_type(StringOrInt cosnt* v) {
return v->type;
}
int soi_get_int(StringOrInt const* v) {
assert(v->type == INT);
return v->value.i;
}
char* soi_get_string(StringOrInt const* v) {
assert(v->type == STRING);
return v->value.s;
}
While this doesn't actually make it crash proof, users of the API will find it more convenient to use the API than change the members by hand, reducing the errors significantly.
Run-time type checking in C is effectively impossible.
The burden is on the caller to pass the data correctly; there's no (good, standard, portable) way for you to determine whether b contains data of the correct type (that is, that the caller didn't pass you a pointer value as an integer or vice versa).
The only suggestion I can make is to create two separate callbacks, one of which takes an int and the other a char *, and put the burden on the compiler to do type checking at compile time.
I'm currently making a small library which can access random.org and get random strings or integers. I've run into a slight problem now though, design wise, and I can't decide which of my two approaches that would be the best, so I will write down my thoughts about both and ask the questions that I find relevant to both.
I currently have a struct like this:
typedef struct {
char **array;
size_t row;
size_t col;
size_t size;
} MemoryStruct;
And this is the source of my "problem", as I now have added in integer handling into my library.
As it can be seen, the pointer is currently a char pointer, and as I want to be able to handle blot integers and chars, should I add another pointer - an integer pointer - or should I instead make it a void pointer and make a function that will return the correct type of pointer?
The addition of an integer pointer would be the easiest, but I'm not sure how obvious it would seem to the guy who might use my library at some point in the future, that this is why his program segfaults, because he used the wrong pointer in the struct.
However, adding in a void pointer instead means that I will have to do a context based function that will return a pointer of the correct type (if this is even possible - I quite honestly don't know if it is), which leads to the question:
Which return type would a function that can return different kinds of pointers have? (... it's obvious, a void pointer!) but then I still don't have the wanted type included for easy use for the programmer who're using my library - here is it put into pseudo code what I would like to do.
pointer-with-type-info *fun(MemoryStruct *memory, RandomSession *session)
switch case on session->type
return pointer of appropriate type
It should be added here, that from session->type it can inferred which type the pointer should be.
Thank you in advance for reading this.
The way I understood your question, you know the pointer data type. I would use a void* instead of two pointers, because you may add more data types later without creating more fields.
To retrieve the correct value, one can create two functions: int getAsString(MemoryStruct, char** value) and int getAsInteger(MemoryStruct, int &value). These functions would return a non-zero value in case of success (true). This way, you will be able no only to retrieve the correct value, but also you will have a way to know whether the value can be retrieved at as this data type.
So I'm trying to write a buffering library for the 64th time and I'm starting get into some pretty advanced stuff. Thought I'd ask for some proffesional input on this.
In my first header file I have this:
typedef struct StdBuffer { void* address; } StdBuffer;
extern void StdBufferClear(StdBuffer);
In another header file that #includes the first header file I have this:
typedef struct CharBuffer { char* address; } CharBuffer;
void (*CharBufferClear)(CharBuffer) = (void*) StdBufferClear;
Will declaring this function pointer void interfere with the call? They have matching by value signatures. I have never seen a function pointer declared void before, but its the only way to get it to compile cleanly.
Stackwise it should not make any difference at all from what I learned in assembler coding.
irrelevent OMG! I just said Stackwise on StackOverflow!
Hmm.. Looks like I've assumed too much here. Allow me to reclarify if I may. I don't care what 'type' of data is stored at the address. All that I am concerned with is the size of a 'unit' and how many units are at the address. Take a look at the interface agreement contract for the API if you will:
typedef struct StdBuffer {
size_t width; ///< The number of bytes that complete a data unit.
size_t limit; ///< The maximum number of data units that can be allocated for this buffer.
void * address; ///< The memory address for this buffer.
size_t index; ///< The current unit position indicator.
size_t allocated; ///< The current number of allocated addressable units.
StdBufferFlags flags;///< The API contract for this buffer.
} StdBuffer;
You see, memcpy, memmove and the like don't really care whats at an address all they want is the specifics which I'm clearly keeping track of here.
Have a look now at the first prototype to follow this contract:
typedef struct CharBuffer {
size_t width; ///< The number of bytes that complete a data unit.
size_t limit; ///< The maximum number of data units that can be allocated for this buffer.
char * address; ///< The memory address for this buffer.
size_t index; ///< The current unit position indicator.
size_t allocated; ///< The current number of allocated addressable units.
CharBufferFlags flags;///< The API contract for this buffer.
} CharBuffer;
As you an clearly see the data type is irrelevant in this context. You can say that C handles it differently depending on the case, but at the end of the day, an address is an address, a byte is byte and a long is a long for as long as we are dealing with memory on the same machine.
The purpose of this system when brought together is to remove all of this type based juggling C seems to be so proud of (and rightfully so...) Its just pointless for what I would like to do. Which is create a contract abiding prototype for any standard size of data (1, 2, 4, 8, sizeof(RandomStruct)) located at any address.
Having the ability to perform my own casting with code and manipulate that data with api functions that operate on specific length blocks of memory with specific length memory units. However, the prototype must contain the official data pointer type, because it just doesn't make sense for the end user to have to recast their data every time they would like to do something with that address pointer. It would not make sense to call it a CharBuffer if the pointer was void.
The StdBuffer is a generic type that is never EVER used except within the api itself, to manage all contract abiding data types.
The api that this system will incorporate is from my latest edition of buffering. Which is quite clearly documented here #Google Code I am aware that some things will need to change to bring this all together namely I won't have the ability to manipulate data directly from within the api safely without lots of proper research and opinion gathering.
Which just brought to my attention that I also need a Signed/Unsigned bit flag in the StdBufferFlags Members.
Perhaps the final piece to this puzzle is also in order for your perusal.
/** \def BIT(I)
\brief A macro for setting a single constant bit.
*
* This macro sets the bit indicated by I to enabled.
* \param I the (1-based) index of the desired bit to set.
*/
#define BIT(I) (1UL << (I - 1))
/** \enum StdBufferFlags
\brief Flags that may be applied to all StdBuffer structures.
* These flags determine the contract of operations between the caller
* and the StdBuffer API for working with data. Bits 1-4 are for the
* API control functions. All other bits are undefined/don't care bits.
*
* If your application would like to use the don't care bits, it would
* be smart not to use bits 5-8, as these may become used by the API
* in future revisions of the software.
*/
typedef enum StdBufferFlags {
BUFFER_MALLOCD = BIT(1), ///< The memory address specified by this buffer was allocated by an API
BUFFER_WRITEABLE = BIT(2), ///< Permission to modify buffer contents using the API
BUFFER_READABLE = BIT(3), ///< Permission to retrieve buffer contents using the API
BUFFER_MOVABLE = BIT(4) ///< Permission to resize or otherwise relocate buffer contents using the API
}StdBufferFlags;
This code requires a diagnostic:
void (*CharBufferClear)(CharBuffer) = (void*) StdBufferClear;
You're converting a void * pointer to a function pointer without a cast. In C, a void * pointer can convert to pointers to object types without a cast, but not to function pointer types. (In C++, a cast is needed to convert void * to object types also, for added safety.)
What you want here is just to cast between function pointer types, i.e.:
void (*CharBufferClear)(CharBuffer) = (void (*)(CharBuffer)) StdBufferClear;
Then you are still doing the same type punning because the functions are different types. You are trying to call a function which takes a StdBuffer using a pointer to a function which takes a CharBuffer.
This type of code is not well-defined C. Having defeated the type system, you're on your own, relying on testing, examining the object code, or obtaining some assurances from the compiler writers that this sort of thing works with that compiler.
What you learned in assembler coding doesn't apply because assembly languages have only a small number of rudimentary data types such as "machine address" or "32 bit word". The concept that two data structures with an identical layout and low-level representation might be incompatible types does not occur in assembly language.
Even if two types look the same at the low level (another example: unsigned int and unsigned long are sometimes exactly the same) C compilers can optimize programs based on the assumption that the type rules have not been violated. For instance suppose that A and B point to the same memory location. If you assign to an an object A->member, a C compiler can assume that the object B->member is not affected by this, if A->member and B->member have incompatible types, like one being char * and the other void *. The generated code keeps caching the old value of B->member in a register, even though the in-memory copy was overwritten by the assignment to A->member. This is an example of invalid aliasing.
The standard does not define the results of casting a function-pointer to void *.
Equally, converting between function pointers and then calling through the wrong one is also undefined behaviour.
There are some constructs which any standards-conforming C compiler are required to implement consistently, and there are some constructs which 99% of C compilers do implement consistently, but which standards-conforming compilers would be free to implement differently. Attempting to cast a pointer to a function which takes one type of pointer, into a pointer to a function which takes another type of pointer, falls into the latter category. Although the C standard specifies that a void* and a char* must be the same size, there is nothing that would require that they share the same bit-level storage format, much less the parameter-passing convention. While most machines allow bytes to be accessed in much the same way as words, such ability is not universal. The designer of an application-binary-interface [the document which specifies among other things how parameters are passed to routines] might specify that a char* be passed in a way which maximizes the efficiency of byte access, while a void* should be passed in a way that maximizes the efficiency of word access while retaining the ability to hold an unaligned byte address, perhaps by using a supplemental word to hold a zero or one to indicate LSB/MSB). On such a machine, having a routine that expects a void* called from code that expects to pass a char* could cause the routine to access arbitrary wrong data.
No, it doesn't matter what data type is used to store the data. It only matters the type C uses to read and write that data, and that the data is of sufficient size.
I have a linked list, which stores groups of settings for my application:
typedef struct settings {
struct settings* next;
char* name;
char* title;
char* desc;
char* bkfolder;
char* srclist;
char* arcall;
char* incfold;
} settings_row;
settings_row* first_profile = { 0 };
#define SETTINGS_PER_ROW 7
When I load values into this structure, I don't want to have to name all the elements. I would rather treat it like a named array -- the values are loaded in order from a file and placed incrementally into the struct. Then, when I need to use the values, I access them by name.
//putting values incrementally into the struct
void read_settings_file(settings_row* settings){
char* field = settings + sizeof(void*);
int i = 0;
while(read_value_into(field[i]) && i++ < SETTINGS_PER_ROW);
}
//accessing components by name
void settings_info(settings_row* settings){
printf("Settings 'profile': %s\n", settings.title);
printf("Description: %s\n", settings.desc);
printf("Folder to backup to: %s\n", settings.bkfolder);
}
But I wonder, since these are all pointers (and there will only ever be pointers in this struct), will the compiler add padding to any of these values? Are they guaranteed to be in this order, and have nothing between the values? Will my approach work sometimes, but fail intermittently?
edit for clarification
I realize that the compiler can pad any values of a struct--but given the nature of the struct (a struct of pointers) I thought this might not be a problem. Since the most efficient way for a 32 bit processor to address data is in 32 bit chunks, this is how the compiler pads values in a struct (ie. an int, short, int in a struct will add 2 bytes of padding after the short, to make it into a 32 bit chunk, and align the next int to the next 32 bit chunk). But since a 32 bit processor uses 32 bit addresses (and a 64 bit processor uses 64 bit addresses (I think)), would padding be totally unnecessary since all of the values of the struct (addresses, which are efficient by their very nature) are in ideal 32 bit chunks?
I am hoping some memory-representation / compiler-behavior guru can come shed some light on whether a compiler would ever have a reason to pad these values
Under POSIX rules, all pointers (both function pointers and data pointers) are all required to be the same size; under just ISO C, all data pointers are convertible to 'void *' and back without loss of information (but function pointers need not be convertible to 'void *' without loss of information, nor vice versa).
Therefore, if written correctly, your code would work. It isn't written quite correctly, though! Consider:
void read_settings_file(settings_row* settings)
{
char* field = settings + sizeof(void*);
int i = 0;
while(read_value_into(field[i]) && i++ < SETTINGS_PER_ROW)
;
}
Let's assume you're using a 32-bit machine with 8-bit characters; the argument is not all that significantly different if you're using 64-bit machines. The assignment to 'field' is all wrong, because settings + 4 is a pointer to the 5th element (counting from 0) of an array of 'settings_row' structures. What you need to write is:
void read_settings_file(settings_row* settings)
{
char* field = (char *)settings + sizeof(void*);
int i = 0;
while(read_value_into(field[i]) && i++ < SETTINGS_PER_ROW)
;
}
The cast before addition is crucial!
C Standard (ISO/IEC 9899:1999):
6.3.2.3 Pointers
A pointer to void may be converted to or from a pointer to any incomplete or object
type. A pointer to any incomplete or object type may be converted to a pointer to void
and back again; the result shall compare equal to the original pointer.
[...]
A pointer to a function of one type may be converted to a pointer to a function of another
type and back again; the result shall compare equal to the original pointer. If a converted
pointer is used to call a function whose type is not compatible with the pointed-to type,
the behavior is undefined.
In many cases pointers are natural word sizes, so the compiler is unlikely to pad each member, but that doesn't make it a good idea. If you want to treat it like an array you should use an array.
I'm thinking out loud here so there's probably many mistakes but perhaps you could try this approach:
enum
{
kName = 0,
kTitle,
kDesc,
kBkFolder,
kSrcList,
kArcAll,
kIncFold,
kSettingsCount
};
typedef struct settings {
struct settings* next;
char *settingsdata[kSettingsCount];
} settings_row;
Set the data:
settings_row myRow;
myRow.settingsData[kName] = "Bob";
myRow.settingsData[kDescription] = "Hurrrrr";
...
Reading the data:
void read_settings_file(settings_row* settings){
char** field = settings->settingsData;
int i = 0;
while(read_value_into(field[i]) && i++ < SETTINGS_PER_ROW);
}
It's not guaranteed by the C standard. I've a sneaking suspicion, that I don't have time to check right now either way, that it guarantees no padding between the char* fields, i.e. that consecutive fields of the same type in a struct are guaranteed to be layout-compatible with an array of that type. But even if so, you're on your own between the settings* and the first char*, and also between the last char* and the end of the struct. But you could use offsetof to deal with the first issue, and I don't think the second affects your current code.
However, what you want is almost certainly guaranteed by your compiler, which somewhere in its documentation will set out its rules for struct layout, and will almost certainly say that all pointers to data are word sized, and that a struct can be the size of 8 words without additional padding. But if you want to write highly portable code, you have to use only the guarantees in the standard.
The order of fields is guaranteed. I also don't think you'll see intermittent failure - AFAIK the offset of each field in that struct will be consistent for a given implementation (meaning the combination of compiler and platform).
You could assert that sizeof(settings*) == sizeof(char*) and sizeof(settings_row) == sizeof(char*)*8. If both those hold, there is no room for any padding in the struct, since fields are not allowed to "overlap". If you ever hit a platform where they don't hold, you'll find out.
Even so, if you want an array, I'd be inclined to say use an array, with inline accessor functions or macros to get the individual fields. Whether your trick works or not, it's even easier not to think about it at all.
Although not a duplicate, this probably answers your question:
Why isn't sizeof for a struct equal to the sum of sizeof of each member?
It's not uncommon for applications to write an entire struct into a file and read it back out again. But this suffers from the possibility that one day the file will need to be read back on another platform, or by another version of the compiler that packs the struct differently. (Although this can be dealt with by specially-written code that understands the original packing format).
Technically, you can rely only on the order; the compiler could insert padding. If different pointers were of different size, or if the pointer size wasn't a natural word size, it might insert padding.
Practically speaking, you could get away with it. I wouldn't recommend it; it's a bad, dirty trick.
You could achieve your goal with another level of indirection (what doesn't that solve?), or by using a temporary array initialized to point to the various members of the structure.
It's not guaranteed, but it will work fine in most cases. It won't be intermittent, it will either work or not work on a particular platform with a particular build. Since you're using all pointers, most compilers won't mess with any padding.
Also, if you wanted to be safer, you could make it a union.
You can't do that the way you are trying. The compiler is allowed to pad any and all members of the struct. I do not believe it is allowed to reorder the fields.
Most compilers have an attribute that can be applied to the struct to pack it (ie to turn it into a collection of tightly packed storage with no padding), but the downside is that this generally affects performance. The packed flag will probably allow you to use the struct the way you want, but it may not be portable across various platforms.
Padding is designed to make field access as efficient as possible on the target architecture. It's best not to fight it unless you have to (ie, the struct goes to a disk or over a network.)
It seems to me that this approach creates more problems than it solves.
When you read this code six months from now, will you still be aware of all the subtleties of how the compiler pads a struct?
Would someone else, who didn't write the code?
If you must use the struct, use it in the canonical way and just write a function which
assigns values to each field separately.
You could also use an array and create macros to give field names to indices.
If you get too "clever" about optimizing your code, you will end up with slower code anyway, since the compiler won't be able to optimize it as well.