我有许多处理一个结构的类似函数调用,但是每个调用都使用不同的结构域。
示例:
typedef struct {
int i1;
int i2;
int i3;
} S;
获取结构字段的函数(最好避免使用它们):
int getFieldI1 (S *s){ return s->i1; }
int getFieldI2 (S *s){ return s->i2; }
int getFieldI3 (S *s){ return s->i3; }
函数我必须多次调用:
void doJob (int (*get_field_func)(S *)){
//some code
S s;
int v = get_field_func(&s);
//some code
}
我这样调用doJob():
doJob(&getFieldI1);
doJob(&getFieldI2);
doJob(&getFieldI3);
我想这样做:
doJob(i1);
doJob(i2);
doJob(i3);
在C语言中有可能吗?
答案 0 :(得分:2)
您可以使用内存偏移量。
void doJob (int offset){
//some code
S s;
int v = *(&s+offset*sizeof(int));
//some code
}
您可以这样称呼它:
doJob(0);//i1
doJob(1);//i2
doJob(2);//i3
如注释中所指出,偏移量是不安全的。您可以为此创建支票:
if(offset>2||offset<0){
//some kind of error
}
此外,仅当结构仅包含整数(或相同类型的元素,您需要对其进行调整)时才可以使用它(请参见注释)。
如果s1,s2和s3之前有元素,则需要添加元素的大小(作为填充,只需添加);
另一个选项(没有提到的问题)是定义常量/宏:
您将像这样定义它们:
#define I1 &getFieldI1
#define I2 &getFieldI2
#define I3 &getFieldI3
并使用以下命令调用它:
doJob(I1);
doJob(I2);
doJob(I3);
答案 1 :(得分:0)
只需传递一个指向该字段的指针:
void doJob( int* fieldPointer )
{
assert( fieldPointer != NULL );
// Get the field value:
int v = *fieldPointer;
// Do something with the field value:
v += 10;
// Save the updated value back to the field:
*fieldPointer = v;
}
用法:
S structInstance = ...
doJob( &structInstance.i1 );
doJob( &structInstance.i2 );
doJob( &structInstance.i3 );
答案 2 :(得分:0)
如何将结构字段名称传递给函数?
通常,您不能。用C编码的典型库不会将内部struct
的字段显示到外部。换句话说,字段名称仅对于编译器是已知的,并且与当前translation unit相关,并且在运行时没有任何意义。
请考虑以下metaprogramming方法:编写元程序(使用C或某些脚本语言,例如Guile,awk,Python等)来生成C代码,并相应地建立您的构建。这可能意味着要编辑Makefile或配置build automation工具。
这是上个世纪以来的惯例。看看SWIG或RPCGEN是一个著名的例子。
您可能会使用preprocessor的技巧,例如X-macros。
答案 3 :(得分:0)
抱歉,我无法猜测i1 / i2 / i3的正确类型。所以我使用c ++中的auto关键字:
#include <stdio.h>
typedef struct {
int i1;
int i2;
int i3;
} S;
int getFieldI1 (S *s){ return s->i1; }
int getFieldI2 (S *s){ return s->i2; }
int getFieldI3 (S *s){ return s->i3; }
void doJob (int (*get_field_func)(S *)){
//some code
S s = {1,2,3};
//S s;
int v = get_field_func(&s);
//some code
printf("got: %d\n", v);
}
int main() {
S s = {1,2,3};
auto i1 = getFieldI1;
auto i2 = getFieldI2;
auto i3 = getFieldI3;
doJob(i1);
doJob(i2);
doJob(i3);
}
然后
g++ 59503102.cxx -o 59503102 && ./59503102
按预期产生
got: 1
got: 2
got: 3
普通c版本
#include <stdio.h>
typedef struct {
int i1;
int i2;
int i3;
} S;
int getFieldI1 (S *s){ return s->i1; }
int getFieldI2 (S *s){ return s->i2; }
int getFieldI3 (S *s){ return s->i3; }
void doJob (int (*get_field_func)(S *)){
//some code
S s = {1,2,3};
//S s;
int v = get_field_func(&s);
//some code
printf("got: %d\n", v);
}
int main() {
S s = {1,2,3};
int (*i1)(S *) = getFieldI1;
int (*i2)(S *) = getFieldI2;
int (*i3)(S *) = getFieldI3;
doJob(i1);
doJob(i2);
doJob(i3);
}
答案 4 :(得分:0)
不幸的是,C并不能完全满足您的需求。但是您可以通过一些代码更改来部分获胜。
我有一个解决方案。对于第一个,我提出了(简化!)实现,对于第二个,我仅提供了一个提示。请检查它们是否适合您。
您的示例结构:
typedef struct {
int i1;
int i2;
int i3;
} S;
我将定义一个代表特定字段的枚举:
typedef enum
{
FIELD_ID_I1,
FIELD_ID_I2,
FIELD_ID_I3,
FIELD_ID_MAX
} FieldId_e;
然后,我将在您的常规函数中添加一个字段参数,在内部管理要返回的正确字段。 必须在此处进行一些智能的错误管理,以防ID错误。为简便起见,我只返回-1。
int getField (S *s, FieldId id)
{
int ret = -1;
switch(id)
{
case FIELD_ID_I1:
ret = s->i1;
break;
case FIELD_ID_I2:
ret = s->i2;
break;
case FIELD_ID_I3:
ret = s->i3;
break;
}
return ret;
}
您的doJob将成为
void doJob (int (*get_field_func)(S *, FieldId), FieldId id){
//some code
S s;
int v = get_field_func(&s, id);
//some code
}
最后的通话将变成这个。但是可能(取决于您的情况)只有一个通用函数将可以省略函数指针,从而大大简化了界面。
doJob(&getField, FIELD_ID_I1);
doJob(&getField, FIELD_ID_I2);
doJob(&getField, FIELD_ID_I3);
只是对另一个需要使用指针的棘手解决方案的简短引用。
您知道offsetof
宏吗? (Wikipedia EN)
它计算给定成员在a中的偏移量(以字节为单位) struct或union类型,即size_t类型的表达式。 offsetof() 宏带有两个参数,第一个是结构名称,而 第二个是结构中成员的名称。
在这种情况下,您可能会遇到类似的情况
int getField (S *s, size_t offset);
doJob(&getField, offsetof(S, i1));