Reputation: 7418
I want to read different data structure from file based on some value. In some interpreted languages I could do something similar to code below. In C++ I would extend WindowsOptions
class. However for some reason I chose C. I can write two separate code blocks for each case. Do I have any other options? This question may be stupid, but what would you expect from that HTML guy.
if(options.signature == 0x20b) {
PEOptionsHeaderWindowsPlus windowsOptions;
} else {
PEOptionsHeaderWindows windowsOptions;
}
fread(&windowsOptions, sizeof(windowsOptions), 1, file);
printf("%hu", windowsOptions.MajorOsVersion);
Upvotes: 2
Views: 1727
Reputation: 16540
the following suggested code handles either strut size and any error conditions
struct shortStruct
{
int field1;
};
struct longStruct
{
int field1;
int field2;
};
char buffer[sizeof(longStruct)+1];
memset( buffer, '\0', sizeof(longStruct) );
if ( 0x020b == options.signature )
{
byteCount = read( fd, buffer, sizeof( struct shortStruct ) );
}
else
{
byteCount = read( fd, buffer, sizeof( struct longStruct );
}
switch( byteCount )
{
case sizeof(longStruct):
// process long struct fields
break;
case sizeof(shortStruct):
// process short struct fields
break;
default:
// process the error
break;
} // end switch
Upvotes: 0
Reputation: 42139
Since there can be no padding at the head of a struct
, two struct
s sharing the same (or compatible) first fields are compatible for that part. This trick is used, e.g., by the various sockaddr
types (the first field being the address type and the rest depending on the value of that field).
Here is an example of how to use this property in the scenario you describe:
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#define common_fields \
int value; \
bool is_plus
struct base_type {
common_fields;
};
struct plus_type {
common_fields;
int plus_value;
};
static void print_struct (const struct base_type * const p) {
(void) printf("value = %d\n", p->value);
if (p->is_plus) {
const struct plus_type * const plus = (struct plus_type *)p;
(void) printf("plus_value = %d\n", plus->plus_value);
}
}
int main (int argc, char *argv[]) {
struct base_type *p;
if (argc > 1) {
p = malloc(sizeof(struct plus_type));
p->is_plus = true;
} else {
p = malloc(sizeof(struct base_type));
p->is_plus = false;
}
p->value = 6;
if (p->is_plus) {
((struct plus_type *)p)->plus_value = atoi(argv[1]);
}
print_struct(p);
free(p);
return EXIT_SUCCESS;
}
The common_fields
macro is used to avoid typing the common fields twice. Many popular compilers have an extension that would allow doing the following instead:
struct base_type {
int value;
bool is_plus;
}
struct plus_type {
struct base_type;
int plus_value;
};
With clang
and gcc
this extension is enabled by -fms-extensions
.
Note that you must take care to allocate the proper size of struct
when using this trick. Alternatively you could add extra fields (e.g., an array of char
) to the end of struct base_type
to make it at least as large as struct plus_type
(and any other struct
s that share the same base type), but in that case you may be better off with the union
solution.
Upvotes: 2
Reputation: 6005
How about wrapping it with another one?
typedef struct structA{
int x;
int y;
}A;
typedef struct structB{
A *a;
int z;
}B;
Note: remember initializing the A pointer inside structB
.
UPDATE: Another option would be to use a union of the two types:
typedef struct structA{
int x;
int y;
}A;
typedef struct structB{
int z;
}B;
union AB {
A* a;
B* b;
} ABUnion;
Upvotes: 2