How to access a structure member in a function that get it as void* type?
I want to have a function that accepts different type of structures as argument. So, since I don't have a specific type, I have to use void*. Now quest开发者_如何学Cion is: when I pass a structure to this function, how can I access a known member of this structure inside the function? Specifically, I know that all structures have str1 as a member and I want, for example, print it. Here is a sample code:
struct {
char* str1;
float tt1; } var1 = {"This is me", 12};
struct {
char* str1;
int tt2; } var2 = {"This is you", 18};
void printStruct(void* str)
{
printf("\n the structure string is %s", ??);
//can I put something in ?? to print the string?
}
main(....)
{
printStruct(&var1);
printStruct(&var2);
}
You can pass a function pointer to decouple the function from its users
void printStruct(void* data, char * (*namef)(void *data)) {
printf("\n the structure string is %s", namef(data));
}
Then suppose you pass a struct S1*
to the function, you can pass it a getter like this:
char * whois(void *data) {
return ((struct S1*)data)->str1;
}
int main(void) {
struct S1 s = {"This is me", 12};
printStruct(&s, whois);
}
You could use a more uglier alternative with offsetof
though
void printStruct(void* data, size_t named) {
printf("\n the structure string is %s", *(char**) ((char*)data + named));
}
int main(void) {
struct S1 s = {"This is me", 12};
printStruct(&s, offsetof(struct S1, str1));
}
offsetof
is a macro that gives you the offset of a member within a struct. In the function you position the pointer to the char*
member (so you have to cast to char**
to get the appropriate type) and get the member value.
However i would prefer the first way of using functions. That way, the print function doesn't even have to know that data
points to a struct. It can hand over it as a black box to the getter function and in it you can let the compiler handle the low-level offset calculations for you.
You can cast it to the type you want it to be and then access the member, for example, suppose you have your prototype, do this:
struct X
{
char* str1;
float tt1; } var1 = {"This is me", 12};
void printStruct(void* str)
{
((struct X *)str)->str1;
...
}
Notice the use of brackets; what happens is a cast of type void* to struct var2 * and this type, inside the outermost () brackets, can be accessed as a variable of that type.
You should give your structs names. For example,
struct foo {
char * str1;
float tt1;
};
Then you can declare your instances like this:
struct foo var1 = { "This is me", 12 };
Then you can cast the void *
to a struct foo *
in your function:
void printStruct(void * ptr) {
printf("str=%s\n", ((struct foo *)ptr)->str1);
}
And you can call it like this:
int main(...) {
struct foo var1 = { "This is me", 12 };
printStruct(&var1);
}
Update: Commenter Johannes was correct, my answer doesn't account for the fact that the original poster said he might not know the type of each struct. Updated solution is below:
Since you know that all your structs contain a char * str1
, you can take advantage of C's "flat memory model" by casting your structs directly to generic, tagged struct that only contains char * str1
. This will only work if char * str1
is the first element of the structs.
struct base { char * str1; };
struct { char * str1; float tt1; } var1 = { "This is me", 12 };
struct { char * str1, int tt2; } var2 = { "This is me", 18 };
void printStruct(void * ptr) {
printf("str is %s\n", ((struct base *)ptr)->str1);
}
This is a pretty dirty hack, though, IMO. If there are other members that you want to share, you won't be able to use this same trick.
Another option is to define a struct
that uses a union with an enumerator to keep track of which type is actually being used in the union:
enum { FLOAT_TYPE, INT_TYPE };
struct foo {
char * str1;
union {
float tt1;
int tt2;
} numericValue;
int unionType;
};
This would let you define variables like this:
struct foo var1 = { .str1 = "This is me", .numericValue.tt1 =12, .unionType = FLOAT_TYPE };
struct foo var2 = { .str1 = "This is me", .numericValue.tt2 =18, .unionType = INT_TYPE };
Then you don't need to handle for different types of structs, you can cast your void pointer to struct foo *
instead:
void printStruct(void * ptr) {
struct foo * p = (struct foo *)ptr;
printf("string is %s\n", p->str1);
}
This might be require a bit more work but it's much cleaner, IMO.
For most compilers it is just safe to cast and access the member that way, since it is the first member of both of your structures, odds are that the alignment will be the same.
The thing is though, since you will need to cast the pointers into a structure they need to have tags:
struct S1 { char* str1; float tt1; } var1 = {"This is me", 12};
struct S2 { char* str1; int tt2; } var2 = {"This is you", 18};
void printStruct(void* str) { printf("\n the structure string is %s", ((struct S1 *)str)->str1); }
Note however that for more complex examples with more variables, and different order, that this might not be safe, it all depends on the alignment the compiler chooses.
First, a simple example:
int booger(int type, void * st) {
switch(type) {
case foo:
struct foo * f = (struct foo *)st;
/// do stuff
break;
case bar:
/// ...
Now, for your struct: Define a new struct which only contains the first member -- the string: struct st { char * str; }; Now you can pass all of your other structs into your function by casting them to a virtual_st * and then do the same thing that is in the first example to get the actual members.
int baz(struct st * s) {
if (!strcmp(s->str, "foo") ) {
// ...
EDIT: By the way, this is similar to how c++ compilers that output c do it, except that the initial field is a pointer to a type structure which contains function pointers for member functions and static class members. I don't know exactly how multiple inheritance works, though.
If you're passing in a void*
I don't see how the method could know anything about what you're passing in, except that it's an address to something. What this means is that you have to cast it before you can use it, or else make sure that all of the struct
s that you would pass in have the char*
in exactly the same location each time.
精彩评论