簡體   English   中英

結構數組和sizeof

[英]Array of struct and sizeof

我想用C語言編寫(沒有特殊的味覺,比如說c11)一個通用的,全局const結構數組,例如以下偽代碼:

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

現在,我想創建一個函數,給定所需結構的位置的id(我計划對每個id(使用硬編碼)使用const),然后將其復制。

因為那個copy()也是通用的(接受一個void *作為目標),所以它需要知道嚴格的大小,調用者可能會指定女巫(很多錯誤容易發生,他已經需要提供目標結構和正確的ID ),否則我還將為每個結構維護一個具有正確sizeof的並行數組。

有沒有一種方法可以在編譯時自動執行sizeof數組的初始化? 甚至實施某種技巧,讓用戶指定只需將指針傳遞給不帶其id的struct,而不必編寫專門的get_struct_x()

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

對不起,我的解釋不好,請隨時進行改進/糾正。

編輯以澄清問題:我需要從存儲許多結構類型的數組中深度復制一個已知的結構類型,但是同一類型永遠不會重復。 而且我需要從一個通用的get函數中進行操作,而該女巫將從不同的線程中調用,因此在復制之前和之后將鎖定一個互斥鎖,並且我希望將所有鎖定和強制轉換代碼集中在一個點上,以最大程度地減少調試和創建更有效的測試。

無需修改基礎數據結構,這是我可以看到的唯一可完全管理的方法。 數組中的每個條目都不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

認為這就是您想要做的。 如果沒有,我將放棄這個答案。 請注意,這可能不會工作,你怎么想它如果元素是一個指針類型(但它工作,你如何想它,如果它是一個本地數組類型)。 所以要小心

我的一個朋友,沒有stackoverflow帳戶(/ Frances)(@Francesco)重新闡述了@WhozCraig答案,並添加了(很舊的) X宏 (感謝Randy Meyers的精彩文章 )來構建泛型結構數組AND枚舉女巫幫助訪問數組的結構,而用戶只能編輯一個點;

回到工作上,我告訴他我想嘗試制作特定的結構getter(因為數組中將沒有重復的STRUCT TYPE。如果輸入錯誤,則編譯時錯誤,因此最終您在開發時會費勁,但是一旦編譯就會更健壯),因此我們將對所請求結構的類型進行編譯檢查; 回到家,他已經實施了。 KUDOS!

再說一次,我已經花了一些時間,使開發人員只需要鍵入一次結構即可,以防止在那一側出現錯位。 這是優化螺旋的開始;

  • 由於專門的獲取和放置,不再需要size數組,因此刪除了通用結構
  • 不需要初始化的結構,我們使用“匿名”結構初始化
  • 因為“匿名”初始化,所以很難直接訪問結構
  • 直接訪問需要系統知識,並導致意識
  • 使其更具可讀性
  • 易於在外部獨立文件上導出GENERIC_TABLE和結構定義
  • GENERIC_TABLE #ifdef正常工作,將其移至外部標頭以提高可讀性
  • 無需在局部變量外部進行動態初始化(無垃圾,嵌入式友好)
  • 極易使用

成本

  • 也許有點預處理器腫?
  • 似乎很多OOP xD
  • 您可以使用未初始化的指針調用get和put

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

我將結構的大小作為每個結構中的第一個元素。 所以你有了

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 *轉換為size_t *以獲取結構的大小,因為您知道每個結構都將以大小為第一個元素。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM