Side-notes:
I have to admit: I can't see the point in trying to write a function to update/retrieve a value from a struct in the way you describe. If you know the type of the struct, and you know what member you want to retrieve/update, what's wrong with str_var.member or str_ptr->member? It'll be more performant anyway...
The basic answer:
A basic example of how I might write a function that assigns to a member of a given struct would be this:
void update(void *str_ptr, size_t member, void * value, size_t v_size)
{
memcpy(
(void *) (((char *)str_ptr) + member),
value,
v_size);
}
How to call:
struct student your_struct;
const char *str = "string";
assign(
(void *) &your_struct,
offsetof(struct student, name),
(void *) str,
strlen(str)
);
The retrieve function works similarly, but should be even easier to implement:
void retrieve( void *str_ptr, size_t member, void *target, size_t size)
{
if (size == 0) size = sizeof(*target);//optional
memcpy(
target,
(((char *) str_ptr) + member),
size
)
}
Which can be called like so:
int my_am;
retrieve ( (void *) &your_struct, offsetof(struct student, AM), &my_am, 0);
//or
retrieve ( (void *) &your_struct, offsetof(struct student, AM), &my_am, sizeof(my_am));
When retrieving strings, you may want to consider using strncpy, and check the type of target. But implementing that sort of thing is your job.
More efficient alternatives
Quite apart from the regular int foo = struct_var.member, being the best option, as I explained at the top of my answer, it could well be you find yourself in a situation where that is not possible. My answer will work in those cases, but I've just provided 2 regular functions. There are better ways:
As you can see, though, these functions only add some syntactic sugar to your code, in the sense that they wrap a singular memcpy call that would look a tad messy if left in-line:
memcpy(
(void *) &my_am,
(void *) (((char *) &str_ptr) + offsetof(struct student, am)),
sizeof(my_am)
);
So you could turn these functions into macro's or (if you're writing C99>= code), use inline functions.
The choice is yours. Both the inline and macro's have their pro's and cons.
Now, how does this all work?
Basically, the magic happens here:
(void *) (((char *)str_ptr) + member)
What happens here:
(char *)str_ptr cast the void pointer to a char pointer. given that char is guaranteed to be 1 byte in size, we can access struct members using pointer arithmetic now.
+ member: this value was obtained through the offsetof member, its value is the offset of a given member of a struct in respect to its initial memory address
(void *): re-cast the lot to a void pointer for the memcpy function
That's it, really. If you can't predict the types you'll be working with, then simply use void *
The optional size = sizeof(*target); bit is actually a way to "guess" the type with which you're dealing. If sizeof(*target); is 1, there's a good chance you're dealing with a char type, but you shouldn't rely on this check, though...
More info
For more details on my usage of offsetof and struct-member pointer arithmetic, check this question
The source, for future reference of a working implementation of these functions:
#include <stddef.h>
#include <stdio.h>
struct foo
{
int bar;
int foobar;
};
void retrieve( void *str_ptr, size_t member, void *target, size_t size);
void update( void *str_ptr, size_t member, void *value, size_t size);
int main ( void )
{
struct foo test = {.bar = 123,.foobar = 345};
int target = 0;
printf("Initial values: %d\n%d\n", target, test.foobar);
retrieve(
(void *) &test,
offsetof(struct foo, bar),
(void *) &target,
sizeof(target)
);
update (
(void *) &test,
offsetof(struct foo, foobar),
(void *) &target,
sizeof(target)
);
printf("After calls: %d\n%d\n", target, test.foobar);
return 0;
}
void retrieve( void *str_ptr, size_t member, void *target, size_t size)
{
//optional
if (size == 0) size = sizeof *target;
memcpy(
target,
(void *) (((char *) str_ptr) + member),
size
);
}
void update( void *str_ptr, size_t member, void *value, size_t size)
{
//optional
if (size == 0) size = sizeof(*target);
memcpy(
(void *) (((char *) str_ptr) + member),
value,
size
);
}