旁注:
我必须承认:我看不出尝试编写函数以按照您描述的方式从结构中更新/检索值的意义。如果您知道结构的类型,并且您知道要检索/更新的成员,str_var.member 或str_ptr->member 有什么问题?反正性能会更好...
基本答案:
我可能编写一个分配给给定结构成员的函数的基本示例如下:
void update(void *str_ptr, size_t member, void * value, size_t v_size)
{
memcpy(
(void *) (((char *)str_ptr) + member),
value,
v_size);
}
如何调用:
struct student your_struct;
const char *str = "string";
assign(
(void *) &your_struct,
offsetof(struct student, name),
(void *) str,
strlen(str)
);
retrieve 函数的工作原理类似,但应该更容易实现:
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
)
}
可以这样称呼:
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));
在检索字符串时,您可能要考虑使用strncpy,并检查target 的类型。但实施这类事情是你的工作。
更有效的替代方案
正如我在答案顶部解释的那样,除了常规的int foo = struct_var.member 之外,这是最好的选择,很可能您发现自己处于不可能的情况。我的回答适用于这些情况,但我刚刚提供了 2 个常规功能。有更好的方法:
但是,正如您所看到的,这些函数只是为您的代码添加了一些语法糖,因为它们包装了一个单独的 memcpy 调用,如果留在行内看起来会有点混乱:
memcpy(
(void *) &my_am,
(void *) (((char *) &str_ptr) + offsetof(struct student, am)),
sizeof(my_am)
);
因此,您可以将这些函数转换为宏,或者(如果您正在编写 C99>= 代码),请使用 inline 函数。
这是你的选择。 inline 和宏都有各自的优缺点。
现在,这一切如何运作?
基本上,魔法发生在这里:
(void *) (((char *)str_ptr) + member)
这里发生了什么:
-
(char *)str_ptr 将 void 指针转换为 char 指针。鉴于char 的大小保证为 1 字节,我们现在可以使用指针算法访问结构成员。
-
+ member:这个值是通过offsetof成员获得的,它的值是结构的给定成员相对于它的初始内存地址的偏移量
-
(void *):将批次重新转换为 memcpy 函数的空指针
就是这样,真的。如果您无法预测您将使用的类型,那么只需使用void *
可选的size = sizeof(*target); 位实际上是一种“猜测”您正在处理的类型的方法。如果 sizeof(*target); 为 1,则很有可能您正在处理 char 类型,但您不应该依赖此检查...
更多信息
有关我使用 offsetof 和结构成员指针算法的更多详细信息,check this question
来源,供将来参考这些功能的工作实现:
#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
);
}