C 如何将结构字段名传递给函数?

C 如何将结构字段名传递给函数?,c,struct,C,Struct,我有许多类似的函数调用处理一个结构,但每个调用使用不同的结构字段 例如: typedef struct { int i1; int i2; int i3; } S; 获取结构字段的函数(最好避免): 函数我必须多次调用: void doJob (int (*get_field_func)(S *)){ //some code S s; int v = get_field_func(&s); //some code } 我这样调用doJ

我有许多类似的函数调用处理一个结构,但每个调用使用不同的结构字段

例如:

 typedef struct {
     int i1;
     int i2;
     int i3;
    } S;
获取结构字段的函数(最好避免):

函数我必须多次调用:

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);
doJob(0);//i1
doJob(1);//i2
doJob(2);//i3
#define I1 &getFieldI1
#define I2 &getFieldI2
#define I3 &getFieldI3

在C中是否可能?

只需传入指向该字段的指针:

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 );
如何将结构字段名传递给函数

一般来说,你不能。用C编码的典型库不向外部显示内部
struct
字段。换句话说,字段名只有编译器知道,并且与当前相关,在运行时没有意义

考虑以下方法:编写生成C代码的元程序(用C或某些脚本语言,如、、等),并相应地设置构建。这可能意味着编辑或配置您的工具

这是上个世纪以来的惯例。研究或作为一个著名的例子

您可能会使用技巧,例如…

选项1-偏移 您可以使用内存偏移

void doJob (int offset){
    //some code
    S s;
    int v = *(&s+offset*sizeof(int));
    //some code
}
你可以这样称呼它:

doJob(i1);
doJob(i2);
doJob(i3);
doJob(0);//i1
doJob(1);//i2
doJob(2);//i3
#define I1 &getFieldI1
#define I2 &getFieldI2
#define I3 &getFieldI3
正如评论中指出的,偏移是不安全的。您可以为此创建检查:

if(offset>2||offset<0){
    //some kind of error
}
只需使用以下命令调用它:

doJob(I1);
doJob(I2);
doJob(I3);

对不起,我猜不出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);
}
如期生产

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);
}
#包括
类型定义结构{
int i1;
int i2;
int i3;
}S;
int getFieldI1(S*S){返回S->i1;}
int getFieldI2(S*S){返回S->i2;}
int getFieldI3(S*S){返回S->i3;}
void doJob(int(*get_field_func)(S*)){
//一些代码
S={1,2,3};
//S S;
int v=获取字段函数(&s);
//一些代码
printf(“获取:%d\n”,v);
}
int main(){
S={1,2,3};
int(*i1)(S*)=getFieldI1;
int(*i2)(S*)=getFieldI2;
int(*i3)(S*)=getFieldI3;
doJob(i1);
doJob(i2);
doJob(i3);
}

不幸的是,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;
}
你的工作会变成

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);

只是一个简短的参考另一个棘手的解决方案,将需要发挥指针

您知道宏的偏移量吗

它的计算结果为给定成员在 结构或联合类型,类型为size\u t的表达式。抵消 宏接受两个参数,第一个是结构名称,第二个是 第二个是结构中成员的名称

在这种情况下,你可以有类似

int getField (S *s, size_t offset);

doJob(&getField, offsetof(S, i1));

你可以用一个宏而不是一个函数来实现这一点。你确定:
int-getFieldI2(S*S){return S->i1;}int-getFieldI3(S*S){return S->i1;}
或者这两个
i1
是打字错误吗?隐藏了一些东西,但想要引用它似乎有冲突。确定要公开的内容,以确定字段。也许是场的int索引?也许是enum?也许是字符串id?答案取决于调用者被允许知道什么。这真的是你正在使用的结构,还是仅仅是一个人为的例子?如果这是您的真实代码,那么整数数组将比结构简单。@chux-恢复Monica:首先我在K&R(RUS)中检查了这一时刻-他们说成员、元素或字段;在你的留言之后,我在K&R(英语)登记了——他们说只有会员。决定删除K&R(RUS)。结构是在doJob()中定义的,所以我不能使用指针。似乎他想做一些抽象隐藏,所以这不是正确的解决方案。这是一个不安全的解决方案,只有当结构如问题中所述且不只是一个示例时,它才会起作用。此外,如果这是一个解决方案,那么整数数组将比结构更好。偏移量?安全吗?@Nicholas:只是没有添加填充-只有当成员是
int
时才“可能”,但不能保证。此外,它只在所有成员都属于同一类型时才起作用。谢谢。我已经在我的答案中添加了这一点。问题是关于C,而不是我注意到的C++:)我仍然相信“int(*i1)(S*)=getFieldI1;”比“auto i1=getFieldI1;”更难看