2014-09-23 360 views
5

我想寫點什麼,在C(無particolar味,可以說C11)結構的通用,全球常量數組,如下面的僞代碼:陣列的結構和sizeof

void * generic[] ={&(struct a va),&(struct b vb)}; 

現在我想創建一個函數,給定所需結構的位置id(我打算爲每個id使用const,硬編碼),將複製它。

因爲copy()也是通用的(接受一個void *作爲目的地),它需要知道嚴格的大小,女巫可能由調用者指定(很多錯誤傾向,他已經需要提供目標結構和權利ID)或者我還會爲每個結構維護一個正確的sizeof平行陣列。

有沒有辦法在編譯時自動化sizeof數組的初始化?或者甚至實現一些技巧,讓用戶指定只傳遞指針結構沒有它的ID,而不必寫一個專門的get_struct_x()

不允許動態分配(alloc(),局部變量很好),所有結構和通用數組內容在編譯時已知。

對不起,我的不好解釋,隨時改善/糾正它。

編輯澄清:我需要深入複製一個知道結構類型從一個數組存儲很多結構類型,但相同的類型不會重複自己。而且我需要從一個通用的get函數中完成,這個函數將從不同的線程中調用,所以在複製之前和之後都會鎖定一個互斥量,並且我希望將所有的鎖定和轉換代碼保留在一個點上,從而最大限度地減少調試並創建更有效的測試。

+0

我覺得你的問題是,「我可以寫一個函數,它需要一個'struct'和一個索引和'struct'目標地址作爲輸入 - 這樣特定的'struct'被綁定'別的地方'?」 – 2014-09-23 22:06:35

+1

「有沒有辦法在編譯時自動化sizeof數組的初始化?」唯一可用的機制是預處理器。這是抽象這種功能最常用的工具。 – 2014-09-23 22:07:13

+0

是否可以將每個結構的大小存儲在它自己內部?即,在每個結構中添加一個成員int mySize作爲第一個成員,並將它(在運行時)設置爲'x.mySize = sizeof(struct x)'。 – usr2564301 2014-09-23 22:09:36

回答

1

礦[S]沒有計算器的帳戶[/ S](@Francesco)的朋友已經重新闡述了@WhozCraig答案(由蘭迪·邁爾斯感謝great article)將(很老)X macro打造通用結構數組和枚舉女巫有助於訪問數組的結構,讓用戶只編輯一個點;

回到工作我告訴過他,我想嘗試使特定的結構getter(因爲在數組中沒有REPEATED STRUCT TYPE,如果你弄錯了,編譯時錯誤,所以在當你在開發的時候你會彎下腰,但是一旦編譯它就會更加健壯),所以我們將對所請求的結構的類型進行編譯檢查; 回到家他已經實施了。獎勵!

然後我又失去了一些時間,使開發人員只需鍵入一次結構,以防止在那一側的錯位; 這已經是優化螺旋的開始;

  • 因爲專門get和put,不再需要大小排列的,所以刪除了通用的結構
  • 沒有初始化的結構需要,我們使用「匿名」結構初始化
  • 因爲「匿名」初始化,它是很難直接訪問結構
  • 直接訪問需要的知識體系,有導致意識
  • 代碼的可讀性更強
  • 容易導出GENERIC_TABLE和外部獨立的文件結構定義
  • #ifdef的GENERIC_TABLE正常工作,將其移動到外部頭以增加可讀性
  • 外局部變量不需要動態初始化(無垃圾,嵌入型)
  • 使用極爲方便

成本

  • 也許有點臃腫預處理器?
  • 看來很多OOP的xD
  • 可以撥打得到與外行指針

TL放; DR:這裏完整的編譯代碼和基本的例子:

#include <memory.h> 
#include <stdio.h> 
#include <stdlib.h> 

/* BEGIN: just some struct to test, fell free to move them on external header file */ 
struct A 
{ 
    int a1; 
    long a2; 
}; 

struct B 
{ 
    float b1; 
    char b2[6]; 
}; 

struct C 
{ 
    unsigned int c1; 
    double c2[5]; 
}; 
/* END: just some struct to test, fell free to move them on external header file */ 

/* this macro will create the right X macro element, and also initiliaze the "anonymous" struct */ 
#define ADD_STRUCT_TO_ARRAY(xu) X(xu, &(struct xu){})SEP 

/* BEGIN: add or remove your own struct here! 
* fell free to move them on external header file 
* just need to pass struct name without "struct" to ADD_STRUCT_TO_ARRAY macro */ 
#define GENERIC_TABLE \ 
    ADD_STRUCT_TO_ARRAY(A) \ 
    ADD_STRUCT_TO_ARRAY(B) \ 
    ADD_STRUCT_TO_ARRAY(C) 
/* END: add or remove your own struct here! */ 

/* here we initialize the enum, where the type of the struct is the key, and its position in the array the value */ 
#define SEP , 
#define X(a,b) a 
enum STRUCT { 
    GENERIC_TABLE 
}; 
#undef X 

/* here we initalize the array of pointer to the structure */ 
#define X(a,b) b 
void * const generic[] = 
{ 
    GENERIC_TABLE 
}; 
#undef X 
#undef SEP 

/* here we create all the getter function. add here your array locking code */ 
#define SEP ; 
#define X(a,b) void get_##a(struct a * dest){memcpy(dest, generic[a], sizeof(struct a));} 
GENERIC_TABLE 
#undef X 
#undef SEP 

/* here we create all the putter function. add here your array locking code */ 
#define SEP ; 
#define X(a,b) void put_##a(struct a * source){memcpy(generic[a], source, sizeof(struct a));} 
GENERIC_TABLE 
#undef X 
#undef SEP 

/*run, code, run!*/ 
int main() 
{ 
    struct A a_copy; 
    struct B b_copy; 
    struct C c_copy; 

    get_A(&a_copy); 
    get_B(&b_copy); 
    get_C(&c_copy); 

    printf("STRUCTURE IN ARRAY BEFORE INITIALIZATION\n"); 
    printf("A = %d\n", a_copy.a1); 
    printf("B = %f\n", b_copy.b1); 
    printf("C = %x\n", c_copy.c1); 

    a_copy.a1 = -1; 
    b_copy.b1 = 2.3; 
    c_copy.c1 = 3; 

    printf("STRUCTURE CHANGED TO\n"); 
    printf("A = %d\n", a_copy.a1); 
    printf("B = %f\n", b_copy.b1); 
    printf("C = %x\n", c_copy.c1); 

    printf("STRUCTURE SAVED\n"); 
    put_A(&a_copy); 
    put_B(&b_copy); 
    put_C(&c_copy); 

    get_A(&a_copy); 
    get_B(&b_copy); 
    get_C(&c_copy); 

    printf("STRUCTURE LOADED\n"); 
    printf("A = %d\n", a_copy.a1); 
    printf("B = %f\n", b_copy.b1); 
    printf("C = %x\n", c_copy.c1); 

    a_copy.a1 = 1000; 
    b_copy.b1 = -50.576; 
    c_copy.c1 = 700; 

    printf("STRUCTURE CHANGED TO\n"); 
    printf("A = %d\n", a_copy.a1); 
    printf("B = %f\n", b_copy.b1); 
    printf("C = %x\n", c_copy.c1); 

    get_A(&a_copy); 
    get_B(&b_copy); 
    get_C(&c_copy); 

    printf("STRUCTURE RELOADED WITHOUT SAVING\n"); 
    printf("A = %d\n", a_copy.a1); 
    printf("B = %f\n", b_copy.b1); 
    printf("C = %x\n", c_copy.c1); 


    return 0; 
} 
0

我會使結構的大小成爲每個結構中的第一個元素。所以你有

struct a 
{ 
    size_t size; 
    int a1; 
    double a2; 
    // ... 
}; 

struct b 
{ 
    size_t size; 
    char b1[20]; 
    float b2; 
    // ... 
}; 

任何時候你創建一個結構,一定要初始化大小,例如,

struct a *aptr = malloc(sizeof(struct a)); 
aptr->size = sizeof(struct a); 
aptr->a1 = 3; 
aptr->a2 = 100.5; 

struct b someB = { sizeof(struct b), "hello", 1.0 }; 

然後,在拷貝過程中,您可以施放void *size_t *得到結構的大小,因爲你知道,每一個結構會具有尺寸爲第一要素。

+0

我一般喜歡這種方法比其他兩個答案更好,但不幸的是,OP明確指出,'的malloc()'是不允許的解。因此,沒有+1 :-( – cmaster 2014-09-24 19:24:49

3

無需修改底層的數據結構,這是我可以看到這將是相當易於管理的唯一方法。陣列中的每個條目是一個void*,而是包含一個void*(向元件)的結構體和一個size_t(該元素的sizeof數據):

#include <stdio.h> 
#include <stdlib.h> 

typedef struct Entry 
{ 
    size_t len; 
    void const *elem; 
} Entry; 

#define ELEM_ENTRY(x) { sizeof(x), &(x) } 

struct A 
{ 
    int a1; 
    long a2; 
} a; 

struct B 
{ 
    float b1; 
    char b2[6]; 
} b; 

struct C 
{ 
    unsigned int c1; 
    double c2[5]; 
} c; 

Entry generic[] = 
{ 
    ELEM_ENTRY(a), 
    ELEM_ENTRY(b), 
    ELEM_ENTRY(c) 
}; 
size_t generic_n = sizeof(generic)/sizeof(*generic); 


int main() 
{ 
    for (size_t i=0; i<generic_n; ++i) 
     printf("[%zu] = %zu\n", i, generic[i].len); 
} 

輸出

[0] = 8 
[1] = 12 
[2] = 44 

認爲這就是你想要做的。如果沒有,我會放棄這個答案。請注意,這可能會而不是如何工作,如果元素是指針類型(但它工作如何,如果它是本機陣列類型)。所以要小心。

+0

遺憾地刪除您accepeted的答案,但看到我自己的答案使用X宏做一些更多的技巧 – Lesto 2014-09-24 19:15:52