簡體   English   中英

在 C 中可靠且可移植地存儲和檢索結構類型的對象

[英]Reliably and portably store and retrieve objects of structure type in C

@bdonlan,在Copying structure in C with assignment 而不是 memcpy()中,列出了使用memcpy復制結構類型對象的幾個原因。 我還有一個原因:我想使用 memory 的同一區域在不同時間存儲和檢索任意對象(可能具有不同的結構類型) (例如預分配堆上的存儲)。

我想知道:

  • 這如何可移植地完成(在標准定義的行為的意義上)和
  • 標准的哪些部分允許我合理地假設它可以便攜地完成。

這是一個MRE (排序:“M”[最小] 沒有那么多,我基本上是在詢問“R”[可重現]):

編輯:我希望在此之后放置一個更好的例子。 我將此留在這里,以便為迄今為止的答案和評論提供參考。

// FILE: memcpy_struct.c

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

// EDIT: @john-bollinger POINTS OUT THAT THE FOLLOWING LINE
//  IS NOT PORTABLE.
// typedef struct { } structure ;
// INSTEAD:
typedef struct { char dummy ; } structure ;

typedef struct {
    unsigned long long u ; unsigned long long v ;
} unsignedLongLong2; // TWICE AS MANY BITS AS long long

typedef struct
{
    unsigned long long u ; unsigned long long v ;
    unsigned long long w ; unsigned long long x ;
} unsignedLongLong4; // FOUR TIMES AS MANY BITS AS long long

typedef unsigned char byte ;

void store ( byte * target , const structure * source , size_t size ) {
    memcpy ( target , source , size ) ;
}

void fetch ( structure * target , const byte * source , size_t size ) {
    memcpy ( target , source , size ) ;
}

const size_t enough =
    sizeof ( unsignedLongLong2 ) < sizeof ( unsignedLongLong4 )
    ? sizeof ( unsignedLongLong4 ) : sizeof ( unsignedLongLong2 ) ;

int main ( void )
{
    byte * memory = malloc ( enough ) ;
    unsignedLongLong2 v0 = { 0xabacadabaabacada , 0xbaabacadabaabaca } ;
    unsignedLongLong4 w0= {
        0xabacadabaabacada , 0xbaabacadabaabaca ,
        0xdabaabacadabaaba , 0xcadabaabacadabaa } ;
    unsignedLongLong2 v1 ;
    unsignedLongLong4 w1 ;
    store ( memory ,   ( structure * ) & v0 ,   sizeof v0 ) ;
    fetch ( ( structure * ) & v1 ,   memory ,   sizeof v1 ) ;
    store ( memory ,   ( structure * ) & w0 ,   sizeof w0 ) ;
    fetch ( ( structure * ) & w1 ,   memory ,   sizeof w1 ) ;
    char s [ 1 + sizeof w0 * CHAR_BIT ] ; // ENOUGH FOR TERMINATING NULL CHAR-
    char t [ 1 + sizeof w0 * CHAR_BIT ] ; // ACTERS + BASE-2 REPRESENTATION.
    sprintf ( s, "%llx-%llx",  v0 . u,  v0 . v ) ;
    sprintf ( t, "%llx-%llx",  v1 . u,  v1 . v ) ;
    puts ( s ) ;   puts ( t ) ;
    puts ( strcmp ( s , t ) ? "UNEQUAL" : "EQUAL" ) ;
    sprintf ( s, "%llx-%llx-%llx-%llx",  w0 . u,  w0 . v,  w0 . w,  w0 . x ) ;
    sprintf ( t, "%llx-%llx-%llx-%llx",  w1 . u,  w1 . v,  w1 . w,  w1 . x ) ;
    puts ( s ) ;   puts ( t ) ;
    puts ( strcmp ( s , t ) ? "UNEQUAL" : "EQUAL" ) ;
    free ( memory ) ;
}

編譯為

gcc -std=c11 memcpy_struct.c # can do C99 or C17, too

對應可執行文件的Output

abacadabaabacada-baabacadabaabaca
abacadabaabacada-baabacadabaabaca
EQUAL
abacadabaabacada-baabacadabaabaca-dabaabacadabaaba-cadabaabacadabaa
abacadabaabacada-baabacadabaabaca-dabaabacadabaaba-cadabaabacadabaa
EQUAL

但是,如果遵守標准,什么能保證輸出對始終EQUAL呢? 我認為以下內容有幫助(N2176 類型 6.2.5-28):

所有指向結構類型的指針應具有相同的表示和 alignment 要求。

編輯:在考慮了答案和評論之后,我認為以下是更好的 MRE:

// FILE: memcpy_struct-1.c

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

typedef struct {
    size_t length ;
} array_header ;

typedef struct
{
    size_t capacity ;
    size_t length ;
} buffer_header ;

const size_t hsize_max =
    sizeof ( array_header ) < sizeof ( buffer_header )
    ? sizeof ( buffer_header ) : sizeof ( array_header ) ;

const size_t block = 512u ;

const size_t pageSize = block * ( 1 +
    ( hsize_max / block + ! ! hsize_max % block ) ) ;

int main ( void )
{
    void * memory = malloc ( pageSize ) ;
    array_header a0 = { 42u } ;
    buffer_header b0 = { 42u , 0u } ;
    array_header a1 ;
    buffer_header b1 ;
    memcpy ( memory ,     & a0 , sizeof a0 ) ;
    memcpy (   & a1 ,   memory , sizeof a1 ) ;
    memcpy ( memory ,     & b0 , sizeof b0 ) ;
    memcpy (   & b1 ,   memory , sizeof b1 ) ;
    fputs ( "array_header-s are " , stdout ) ;
    puts ( a0.length == a1.length ? "EQUAL" : "UNEQUAL" ) ;
    fputs ( "buffer_header-s are " , stdout ) ;
    puts ( b0.capacity == b1.capacity && b0.length == b1.length
        ? "EQUAL" : "UNEQUAL" ) ;
    free ( memory ) ;
}

既然你問的是可移植性和標准的規定,首先想到的是沒有任何成員的結構類型,比如這個......

 typedef struct { } structure;

... 是不可移植的擴展。 您的目標似乎是將structure *用作指向結構的通用指針類型,但是當您將void *用作指向任何類型的通用指針時,您不需要它。 使用void * ,您甚至可以自動獲得指針轉換,而無需顯式強制轉換。 另請注意,當您調用memcpy()時,您最終還是會轉換為void *

我想使用 memory 的相同區域在不同時間存儲和檢索任意對象(可能具有不同的結構類型)(例如預分配堆上的存儲)。

行。 這不是一個特別大的問題。

我想知道:

  • 這如何可移植地完成(在標准定義的行為的意義上)和

你的例子很好。 或者,如果您事先知道可能要存儲的所有不同結構類型,則可以使用union

  • 標准的哪些部分允許我合理地假設它可以便攜地完成。

在您的動態分配/ memcpy()示例中,有

  • C17 7.22.3.4/2:“malloc malloc為一個object分配空間,其大小由size指定”

  • C17 6.2.4/2:“一個 object 存在,有一個不變的地址,並在其整個生命周期中保留其最后存儲的值。”

  • C17 7.22.3/1:“分配的 object 的生命周期從分配一直延伸到解除分配。”

  • C17 7.24.2.1/3:“memcpy function 將memcpy指向的 object 中的n字符復制到s1指向的s2中。”

因此,在一個僅表現出已定義行為的程序中, memcpy()忠實地將所有指定字節從源對象的表示復制到目標對象的表示。 object 保留它們不變,直到並且除非它們被覆蓋或生命周期結束。 這使它們可用於第二個memcpy()以將它們從那里復制到其他一些 object。兩個memcpy都不會改變字節序列,並且分配的 object 忠實地將它們保留在兩者之間,所以最后,所有三個對象 - 原始的,分配的和最終的目的地必須包含相同的字節序列,最多為復制的字節數。

如果您正在詢問某種方法來“存儲”結構,然后將相同的結構恢復到相同類型的 object 中,那么僅復制字節就足夠了。 這可以通過memcpy完成,並且不需要使用由各種數量的unsigned long long元素定義的結構進行任何拼湊。 1這由 C 2018 6.2.6.1 第 2 至 4 段保證:

2 除位字段外,對象由一個或多個字節的連續序列組成,其數量、順序和編碼是明確指定的或實現定義的。

3 存儲在無符號位字段和unsigned char類型對象中的值應使用純二進制表示法表示。

4 存儲在任何其他 object 類型的非位域對象中的值由n × CHAR_BIT位組成,其中n是該類型的 object 的大小,以字節為單位。 該值可以復制到類型為unsigned char [n]的 object 中(例如,通過memcpy ); 生成的字節集稱為值的 object 表示……

因此,要存儲任何結構或除位字段以外的任何 object,請為其保留足夠的 memory 2並將對象的字節復制到該 memory。要恢復結構,請將字節復制回來。

關於:

我認為以下內容有幫助(N2176 類型 6.2.5-28):

所有指向結構類型的指針應具有相同的表示和 alignment 要求。

那是無關緊要的。 問題的代碼中沒有使用任何指針的表示形式,因此它們的表示形式(哪些字節構成了指針的記錄值)是無關緊要的。

腳注

1為什么要使用不同名稱的多個成員? unsigned long elements in it, all you need is struct { unsigned long long x[ ]; }要定義一個包含unsigned long元素的結構,您只需要struct { unsigned long long x[ ]; } ]; } . struct { unsigned long long x[ ]; }

2對於 object X ,可以使用void * Memory = malloc(sizeof X)或者,如果您的編譯器支持可變長度 arrays,則使用unsigned char Memory[sizeof X]; , 或者,如果你想把它放在一個結構中, struct { unsigned char x[sizeof X]; } Memory; struct { unsigned char x[sizeof X]; } Memory; .

暫無
暫無

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

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