2017-09-02 107 views
1

基本上我試圖通過使用枚舉和一個新的函數來幫助刪除我的代碼中的重複項。C使用枚舉縮短代碼

說我有一個結構如下:

typedef struct user { 
    bool empty; 
    int lineNumber; 
    char *errMessage; 
    char *username; 
    char *password; 
    char *uid; 
    char *gid; 
    char *gecos; 
    char *dir; 
    char *shell; 
} user; 

,我作出這樣一個枚舉如下:

typedef enum {username, password, uid, gid, gecos, dir, shell} userValue; 

我試圖做的是在一個函數傳遞一個參數userValue enumParam並由此選擇我想訪問的結構中的哪個成員。輔助函數的例子:

void parseHelper(userValue enumParam) { 
    user *newStruct; 
    newStruct -> enumParam = "hello"; 
} 

使用示例:

parseHelper(password) 

Expected: newStruct->password should point to "hello" 

但是,這並沒有幫助,因爲我收到以下錯誤:

'user {aka struct user}' has no member named 'val'

+0

你可以使用開關st爲每個枚舉值做一些不同的事情。這個功能可能很長很醜,但它可能會讓你需要稱之爲更清潔的地方。這不像你在寫完它後總要看看它。 –

+0

奶牛42,檢查我的答案,以防萬一。 – pablo1977

回答

3

沒有我的朋友。

枚舉只是整數,它們是沒有字符串替換:)

+0

Ahh ok:/我想我可以以某種方式縮短我的代碼。哦,經驗教訓。 – Cows42

0

- >運算符是用於訪問指針的成員變量的結構。重要的是這些成員變量的名稱不能改變。你應該在宣言中以你給他們的名字給他們打電話。

這裏你試圖訪問name'val'的成員變量,但是沒有這樣的成員變量。因此錯誤。

在這個函數中,你必須將enum的可能值置於條件下以匹配可能的結構名稱。

0

因爲枚舉常量是整數的名稱,而不是結構成員的名稱,所以不能直接像顯示的那樣去做。您不能使用任何類型的變量直接編寫對結構成員的引用來標識成員名稱 - 您必須明確指定成員(因此它是結構中的硬連接偏移量)。然而,假設你的編譯器對匿名聯合和結構成員有C11支持,並且假設你可以重新定義結構類型(它不是完全固定和不可移動的,而是由某種外部力量預先規定的,例如導師或標準),你可以選擇你想要像這樣的代碼是什麼讓相當接近:

#include <stdio.h> 
#include <stdbool.h> 

typedef struct user 
{ 
    bool empty; 
    int lineNumber; 
    char *errMessage; 
    union 
    { 
     struct 
     { 
      char *username; 
      char *password; 
      char *uid; 
      char *gid; 
      char *gecos; 
      char *dir; 
      char *shell; 
     }; 
     char *field[7]; 
    }; 
} user; 

typedef enum {username, password, uid, gid, gecos, dir, shell} userValue; 

static void parseHelper(user *u, userValue enumParam, char *value) 
{ 
    u->field[enumParam] = value; 
} 

int main(void) 
{ 
    user u; 
    u.empty = false; 
    u.lineNumber = 1; 
    u.errMessage = "no error"; 
    parseHelper(&u, password, "secret"); 
    parseHelper(&u, username, "me"); 
    parseHelper(&u, uid, "0"); 
    parseHelper(&u, gid, "1"); 
    parseHelper(&u, gecos, "Yours Truly"); 
    parseHelper(&u, dir, "/home/me"); 
    parseHelper(&u, shell, "/bin/sea"); 

    printf("%s:%s:%s:%s:%s:%s:%s\n", u.username, 
      u.password, u.uid, u.gid, u.gecos, u.dir, u.shell); 
    return 0; 
} 

運行時,它產生的輸出:

me:secret:0:1:Yours Truly:/home/me:/bin/sea 

有幾個因素使這個工作。

  1. 結構成員名稱位於單獨的名稱空間中,因此枚舉元素名稱不會影響結構成員名稱。
  2. 您嘗試處理的所有字段都是相同類型(char *)。
  3. 在結構中有匿名聯合時,可以按名稱訪問聯合的元素。
  4. 在聯合中有一個匿名結構時,可以按名稱訪問結構中的元素。

如果你堅持C90或C99,則可以達到類似的效果,但它是更冗長:

#include <stdio.h> 
#include <stdbool.h> 

typedef struct user 
{ 
    bool empty; 
    int lineNumber; 
    char *errMessage; 
    union 
    { 
     struct 
     { 
      char *username; 
      char *password; 
      char *uid; 
      char *gid; 
      char *gecos; 
      char *dir; 
      char *shell; 
     } f; 
     char *field[7]; 
    } u; 
} user; 

typedef enum {username, password, uid, gid, gecos, dir, shell} userValue; 

static void parseHelper(user *u, userValue enumParam, char *value) 
{ 
    u->u.field[enumParam] = value; 
} 

int main(void) 
{ 
    user u; 
    u.empty = false; 
    u.lineNumber = 1; 
    u.errMessage = "no error"; 
    parseHelper(&u, password, "secret"); 
    parseHelper(&u, username, "me"); 
    parseHelper(&u, uid, "0"); 
    parseHelper(&u, gid, "1"); 
    parseHelper(&u, gecos, "Yours Truly"); 
    parseHelper(&u, dir, "/home/me"); 
    parseHelper(&u, shell, "/bin/sea"); 

    printf("%s:%s:%s:%s:%s:%s:%s\n", u.u.f.username, u.u.f.password, 
      u.u.f.uid, u.u.f.gid, u.u.f.gecos, u.u.f.dir, u.u.f.shell); 
    return 0; 
} 

這將產生相同的輸出,當然。

1

我會這樣做的方式是使用函數指針。它不是一個非常乾淨的解決方案,但它應該工作。

基本上在這個解決方案中,枚舉必須與char指針在結構中定義的順序相同。 (我寧願像這樣進行手動設置到索引在struct:

typedef enum { 
    username = 0, 
    password = 1, 
    uid = 2, 
    gid = 3, 
    gecos = 4, 
    dir = 5, 
    shell = 6 
} userValue; 

現在枚舉可以從第一字符*通過在結構(用戶名的枚舉表示被用作一個索引)這個可以通過獲取的指針char *與使用指針運算遞增它做

完整的代碼示例(使用的調試,確認無誤執行):

#include <string.h> 

typedef struct user 
{ 
    bool empty; 
    int lineNumber; 
    char *errMessage; 
    char *username; 
    char *password; 
    char *uid; 
    char *gid; 
    char *gecos; 
    char *dir; 
    char *shell; 
} user; 

typedef enum { 
    username = 0, 
    password = 1, 
    uid = 2, 
    gid = 3, 
    gecos = 4, 
    dir = 5, 
    shell = 6 
} userValue; 

void parseHelper(userValue enumParam) 
{ 
    user newStruct; 
    memset(&newStruct, 0, sizeof(user)); 

    char** selected = &newStruct.username; 
    selected = (char**)(selected + ((int)enumParam)); 
    *selected = "hello"; 
} 

int main() 
{ 
    parseHelper(username); 
    parseHelper(password); 
    parseHelper(uid); 
    parseHelper(gid); 
    parseHelper(gecos); 
    parseHelper(dir); 
    parseHelper(shell); 
    return 0; 
} 

- 編輯 也許實現相同的結果的更可讀的方式是投第一char*(在這種情況下,用戶名),以字符*的陣列,並使用標準陣列語法來移動到

void parseHelper(userValue enumParam) 
{ 
    user newStruct; 
    memset(&newStruct, 0, sizeof(user)); 

    // Other fields is an array of char * which starts at username... 
    char** otherFields = &newStruct.username; 
    otherFields[(int)enumParam] = "hello"; 
} 
期望偏移

- 選項2

如果你能夠修改該結構的定義,以及在其他地方你想通過他們的名字特別訪問字符串(例如user->username),你可以聲明字符指針的數組你的結構像這樣:

typedef struct user 
{ 
    bool empty; 
    int lineNumber; 
    char *otherFields[7]; 
    // otherFields[0] - username 
    // otherFields[1] - password 
    // otherFields[2] - uid 
    // otherFields[3] - gid 
    // otherFields[4] - gecos 
    // otherFields[5] - dir 
    // otherFields[6] - shell 
} user; 

,然後在根據需要使用的索引陣列填充:

void parseHelper(userValue enumParam) 
{ 
    user newStruct; 
    memset(&newStruct, 0, sizeof(user)); 

    newStruct.otherFields[enumParam] = "hello"; 
} 

引擎蓋下,這種解決方案是相同的第一個。然而,這還有一個好處,就是簡化枚舉的使用來訪問想要的char*,同時放棄使用字符串名稱訪問其餘代碼中特定字符串的好處。

2

我有一個建議給你。

首先,最後一個項目添加到您的enum列表(userValue_max):

typedef enum {username, password, uid, gid, gecos, dir, shell, userValue_max} userValue; 

現在,在一個稍微不同的方式界定struct,包含字符串數組:

typedef struct user 
{ 
    bool empty; 
    int lineNumber; 
    char *errMessage; 
    char *field[userValue_max]; 
} user; 

現在用這種方式編寫你的代碼:

void parseHelper(userValue enumParam) { 
    user *newStruct = malloc(sizeof(user)); // Allocating memory here!! 
    newStruct->field[enumParam] = "hello"; 
}