繁体   English   中英

如何在C中动态访问struct的成员?

[英]How to access member of struct dynamically in C?

我有两个结构和const字符数组:

typedef struct {
    int skip_lines;
    int num; // count of files
    int i; // number of the file to define order; extremly important to set correctly and then not to change!
    char filename[70];
    char main_directory[16];
    char submain_directory[100];
} FILE_;

typedef struct {
    FILE_ radiation_insolation[7];
    FILE_ radiation_radiation[5];
    FILE_ winds[9];
    FILE_ pressure[1];
    FILE_ humidity[1];
    FILE_ temperature[4];
} FILES;

char *tables[] = {"radiation_insolation", "radiation_radiation", "winds", "pressure", "humidity", "temperature" };

我也有FILES files; 在main函数中,并启动从文件加载数据的函数。 因此,文件的每个成员都包含数据。

然后,我需要像这样访问数据:

files->radiation_insolation[0].skip_lines
files->radiation_radiation[0].skip_lines
files->radiation_winds[0].skip_lines
files->pressure[0].skip_lines
files->humidity[0].skip_lines
files->temperature[0].skip_lines

我的计划是创建循环以动态处理每个成员。

for(i = 0; i<6; i++) {
        // do some job
    }

我的问题是当我需要使用循环中的表[i]访问例如files-> radiation_insolation时该怎么做? 如何创建成员的名称,以便编译器知道要访问的成员?

在PHP语言中,可以使用$ files-> $ tables [i]之类的东西。 但是,如何在C中做到这一点?

答案是你做不到。 好吧,在真正的C编译器上,您也许可以将第二个结构别名为FILE_数组,但是我敢肯定它会调用未定义的行为。 我认为标准中没有什么可以说所有成员都是相同类型的结构中的填充必须与所有成员都是相同类型的数组中的填充相同。

如果能够在一个for语句中访问所有成员对您很重要,则最好使用实际的数组并定义一些常量:

enum {
   radiation_isolation = 0,
   radiation_radiation = 7,
   winds = 12,
   // etc
}

FILE_ files[total_files];

FILE_ *isolation_3 = &files[radiation_isolation + 3];

您可能会编写一些函数来使其看起来更好并提供一些边界检查。

在C语言中,实际上没有办法做到这一点。结构不是表,而是与硬件更接近的东西,即内存块。

您可以创建一个icky宏来访问该结构:

// bad idea
#define FILES_ITEM(var, field, index, member) var.field[index].member

但是这样的宏只是毫无意义且不好的做法,更清晰地键入所有内容:

int main (void)
{
  FILES files;

  for(size_t i=0; i<7; i++)
  {
    files.radiation_insolation[i].skip_lines = i;
    printf("%d ", files.radiation_insolation[i].skip_lines);
  }
}

除了上述样式,通常很难证明其他任何理由。


使用C11,您可以通过使用联合来改善这种情况,联合包含一个匿名结构和一个数组:

#define FILE_ITEMS_N (7 + 5 + 9 + 1 + 1 + 4)

typedef union {

  struct
  {
    FILE_ radiation_insolation[7];
    FILE_ radiation_radiation[5];
    FILE_ winds[9];
    FILE_ pressure[1];
    FILE_ humidity[1];
    FILE_ temperature[4];
  };

  FILE_ items [FILE_ITEMS_N];

} FILES;

然后,您可以分别访问成员:

files.radiation_insolation[0].skip_lines = 123;

或作为数组:

files.items[item].skip_lines = 123;

C11§6.7.2.1保证工会可以工作:

14结构或联合对象的每个非位字段成员都以适合于其类型的实现定义的方式对齐。

/-/

17在结构或联合的末尾可能存在未命名的填充。

这意味着内部结构的所有成员都保证正确对齐,如果需要,在结构的末尾添加尾随填充。

此外,根据C11 6.5 / 7,还保证数组可以毫无问题地别名单个成员:

一个对象只能通过具有以下类型之一的左值表达式访问其存储值:

/-/

—集合或联合类型,其成员中包括上述类型之一(递归地包括子集合或包含的联合的成员)

使unionFILES和相似的结构与一个FILE_所有27个元素的数组:

typedef union
{
    FILES f;
    FILE_ all[27];
}
FILES_U;

然后,您可以访问files->f.radiation_radiation[0]或与files->f.radiation_radiation[0] files->all[7] 并且tables数组将在all (0,7,12 ...)中包含基本索引,而不是字符串名称。

一种方法是通过(滥用)使用x-宏。 它们使您减少重复,却以同事的潜在愤怒为代价。 好处是您只需要在一个地方更新项目列表,并且预处理器将自动生成结构和所有必要的元数据。

即,您仅定义一个这样的条目列表,其中FILE_ENTRY尚未定义:

#define X_FILE_LIST(X_FILE_ENTRY) \
    X_FILE_ENTRY(radiation_insolation, 7) \
    X_FILE_ENTRY(radiation_radiation, 5) \
    X_FILE_ENTRY(winds, 9) \
    X_FILE_ENTRY(pressure, 1) \
    X_FILE_ENTRY(humidity, 1) \
    X_FILE_ENTRY(temperature, 4)

然后根据需要定义FILE_ENTRY(name, len)

// number of entries
#define X_EXPAND_AS_COUNT(name, len) 1 + 
const int FILES_count = X_FILE_LIST(X_EXPAND_AS_COUNT) 0;

// struct definition
#define X_EXPAND_AS_FIELD(name, len) FILE_ name[len];
typedef struct {
    X_FILE_LIST(X_EXPAND_AS_FIELD)
}
FILES;

// byte offsets of each field
#define X_EXPAND_AS_BYTEOFFSET(name, len) offsetof(FILES, name),
int FILES_byte_offsets[] = {
    X_FILE_LIST(X_EXPAND_AS_BYTEOFFSET)
};

// FILE_ offsets of each field
#define X_EXPAND_AS_FILEOFFSET(name, len) offsetof(FILES, name)/sizeof(FILE_),
int FILES_offsets[] = {
    X_FILE_LIST(X_EXPAND_AS_FILEOFFSET)
};

// sizes of each array
#define X_EXPAND_AS_LEN(name, len) len,
int FILES_sizes[] = {
    X_FILE_LIST(X_EXPAND_AS_LEN)
};

// names of each field
#define X_EXPAND_AS_NAME(name, len) #name,
const char * FILES_names[] = {
    X_FILE_LIST(X_EXPAND_AS_NAME)
};

这将扩展为:

const int FILES_count = 1 + 1 + 1 + 1 + 1 + 1 + 0;

typedef struct {
    FILE_ radiation_insolation[7];
    FILE_ radiation_radiation[5]; 
    FILE_ winds[9]; 
    FILE_ pressure[1]; 
    FILE_ humidity[1]; 
    FILE_ temperature[4];
}
FILES;

int FILES_byte_offsets[] = {
    ((size_t)&(((FILES*)0)->radiation_insolation)),
    ((size_t)&(((FILES*)0)->radiation_radiation)),
    ((size_t)&(((FILES*)0)->winds)),
    ((size_t)&(((FILES*)0)->pressure)),
    ((size_t)&(((FILES*)0)->humidity)),
    ((size_t)&(((FILES*)0)->temperature)),
};

int FILES_offsets[] = {
    ((size_t)&(((FILES*)0)->radiation_insolation))/sizeof(FILE_), 
    ((size_t)&(((FILES*)0)->radiation_radiation))/sizeof(FILE_),
    ((size_t)&(((FILES*)0)->winds))/sizeof(FILE_), 
    ((size_t)&(((FILES*)0)->pressure))/sizeof(FILE_),
    ((size_t)&(((FILES*)0)->humidity))/sizeof(FILE_),
    ((size_t)&(((FILES*)0)->temperature))/sizeof(FILE_),
};

int FILES_sizes[] = { 7, 5, 9, 1, 1, 4, };

const char * FILES_names[] = {
    "radiation_insolation", "radiation_radiation",
    "winds", "pressure", "humidity", "temperature",
};

然后,您可以使用类似的方法进行迭代:

for (int i = 0; i < FILES_count; i++)
{
    FILE_ * first_entry = (FILE_ *)&files + FILES_offsets[i];
    for (int j = 0; j < FILES_sizes[i]; j++)
    {
        FILE_ * file = first_entry + j;
        printf("%s[%d].skip_lines = %d \n",
            FILES_names[i],
            j,
            file->skip_lines);
    }
}

这将遍历FILES所有成员,并遍历每个字段的所有数组成员:

// output of the program above
radiation_insolation[0].skip_lines = 0
radiation_insolation[1].skip_lines = 0
radiation_insolation[2].skip_lines = 0
radiation_insolation[3].skip_lines = 0
radiation_insolation[4].skip_lines = 0
radiation_insolation[5].skip_lines = 0
radiation_insolation[6].skip_lines = 0
radiation_radiation[0].skip_lines = 0
radiation_radiation[1].skip_lines = 0
radiation_radiation[2].skip_lines = 0
radiation_radiation[3].skip_lines = 0
radiation_radiation[4].skip_lines = 0
winds[0].skip_lines = 0
winds[1].skip_lines = 0
winds[2].skip_lines = 0
winds[3].skip_lines = 0
winds[4].skip_lines = 0
winds[5].skip_lines = 0
winds[6].skip_lines = 0
winds[7].skip_lines = 0
winds[8].skip_lines = 0
pressure[0].skip_lines = 0
humidity[0].skip_lines = 0
temperature[0].skip_lines = 0
temperature[1].skip_lines = 0
temperature[2].skip_lines = 0
temperature[3].skip_lines = 0

这将带您进入实际的“反思”,使您可以按其名称查找成员:

FILE_ * get_entry_by_name_and_index(FILES * files, const char * name, int idx)
{
    // NOTE: no bounds checking/safe string function, etc

    for (int i = 0; i < FILES_count; i++)
    {
        if (strcmp(FILES_names[i], name) == 0)
        {
            int base_offset = FILES_offsets[i];
            return (FILE_ *)files + base_offset + idx;
        }
    }

    return NULL;
}

例如,这将获得指向files.winds[4]的指针:

FILE_ * item = get_entry_by_name_and_index(&files, "winds", 4);
assert((void*)item == (void*)&files.winds[4]);

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM