簡體   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