簡體   English   中英

C99中的別名,類型修剪,並集,結構和位字段

[英]Aliasing, Type-punning, Unions, Structs and Bit Fields in C99

在收到以下對這個問題的回答的陳述后:

...您正在嘗試覆蓋valuebits ,並將數據填充到並集的一種替代方法中,然后將其從另一種方法中移除不確定的

我對在C99中允許進行類型修飾的內容(以及什么是審慎的內容)更加好奇。 環顧四周后,我發現在文章《通過C99中未指定的聯合體進行類型操作》中有很多有用的信息。

此處發布的評論和答案都需要很多幫助。 為了清楚起見(以及進行完整性檢查),我想基於對C99標准的理解來創建一個示例。 下面是我創建的示例代碼,盡管它按預期運行,但我想確定我的斷言是正確的。

以下代碼在注釋中包含了我的斷言。 這是我對C99中的類型修飾的理解。 我的評論正確嗎? 如果沒有,請您解釋一下原因?

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

#define NUM_BYTES   sizeof(uint32_t)
typedef union
{
    uint32_t fourByteValue;
    uint8_t  byteValue[NUM_BYTES];
    struct
    {
        unsigned int firstBitSpecified  :   1;
        unsigned int secondBitSpecified :   1;
        unsigned int thirdBitSpecified  :   1;
        unsigned int fourthBitSpecified :   1;
        unsigned int paddingBits        :   4;
        uint8_t  oneByteStructValue;
        uint16_t twoByteStructValue;
    };
} U;

int main (void)
{
    const char border[] = "==============================\n";
    U myUnion;
    uint8_t byte;
    uint32_t fourBytes;
    uint8_t i;

    myUnion.fourByteValue = 0x00FFFFFF;
    fourBytes = myUnion.fourByteValue;  /* 1. This is not type-punning. */
    printf("No type-punning fourByteValue:\n%s"
           "fourBytes\t= 0x%.4x\n\n", border, fourBytes);


    printf("Type-punning byteValue:\n%s", border);
    for (i = 0; i < NUM_BYTES; i++)
    {
        byte = myUnion.byteValue[i];   /* 2. Type-punning allowed by C99, 
                                             no unspecified values. */
        printf ("byte[%d]\t\t= 0x%.2x\n", i, byte);
    }
    printf("\n");

    myUnion.byteValue[3] = 0xff;
    fourBytes = myUnion.fourByteValue; /* 3. Type-punning allowed by C99 
                                             but all other 'byteValue's
                                             are now unspecified values. */
    printf("Type-punning fourByteValue:\n%s"
           "fourBytes\t= 0x%.4x\n\n", border, fourBytes);

    myUnion.firstBitSpecified = 0;
    myUnion.thirdBitSpecified = 0;
    fourBytes = myUnion.fourByteValue; /* 4. Again, this would be allowed, but 
                                             the bit that was just assigned
                                             a value of 0 is implementation
                                             defined AND all other bits are
                                             unspecified values. */
    printf("Type-punning firstBitSpecified:\n%s"
           "fourBytes\t= 0x%.4x\n\n", border, fourBytes);

    myUnion.fourByteValue = 0x00000001;
    fourBytes = myUnion.firstBitSpecified; /* 5. Type-punning allowed, although
                                                 which bit you get is implementation
                                                 specific. */
    printf("No type-punning, firstBitSpecified:\n%s"
           "fourBytes\t= 0x%.4x\n\n", border, fourBytes);
    fourBytes = myUnion.secondBitSpecified;
    printf("No type-punning, secondBitSpecified:\n%s"
           "fourBytes\t= 0x%.4x\n\n", border, fourBytes);

    return (EXIT_SUCCESS);
}

上面的代碼是在64位Windows 7計算機上使用mingw32-gcc.exe -Wall -g -std=c99編譯的。 運行代碼后,我收到以下輸出:

No type-punning fourByteValue:
==============================
fourBytes       = 0xffffff

Type-punning byteValue:
==============================
byte[0]         = 0xff
byte[1]         = 0xff
byte[2]         = 0xff
byte[3]         = 0x00

Type-punning fourByteValue:
==============================
fourBytes       = 0xffffffff

Type-punning firstBitSpecified:
==============================
fourBytes       = 0xfffffffa

No type-punning, firstBitSpecified:
==============================
fourBytes       = 0x0001

No type-punning, secondBitSpecified:
==============================
fourBytes       = 0x0000

我對那篇文章中鏈接的腳注的閱讀是, 從未指定通過聯合進行類型拼寫。 從去 ,標准說:

除了一個例外,如果在將值存儲到對象的其他成員之后訪問並集對象的成員,則該行為是實現定義的。

腳注不會改變這一點。 出現這種情況的原因是,C不能保證(a)數字類型的字節順序或(b) struct成員的內存順序,除非第一個成員必須是字節對齊的到了“開始” struct (這樣你可以做的排序鑄造,他們在做GTK實現多態)。

有問題的腳注針對這一行:

當值存儲在聯合類型的對象的成員中時,與該成員不對應但與其他成員相對應的對象表示形式的字節采用未指定的值,但聯合對象的值不應因此成為陷阱表示

它說:

78a如果用於訪問並集對象內容的成員與上次用於在對象中存儲值的成員不同,則該值的對象表示的適當部分將重新解釋為新類型的對象表示如6.2.6(有時稱為“類型校正”的過程)所述。 這可能是陷阱表示。

“重新解釋為新類型中的對象表示形式”是由實現定義的(因為在逐字節級別上對所有類型的解釋始終是實現的定義,同時考慮了字節序等)。 腳注只是增加了更多細節,以指出當您通過聯合對類型系統進行弄亂時,可能會發生令人驚訝的事情,包括引起陷阱表示。 在這里尋找“陷阱表示形式”的定義:

陷阱表示是一組位,當將其解釋為特定類型的值時,將導致未定義的行為。 陷阱表示形式最常見於浮點數和指針值,但從理論上講,幾乎任何類型都可以使用陷阱表示形式。 未初始化的對象可能包含陷阱表示。 這具有與舊規則相同的行為:對未初始化對象的訪問將產生未定義行為。

該標准提供的有關訪問未初始化數據的唯一保證是, 無符號字符類型沒有陷阱表示,並且填充沒有陷阱表示。

因此,通過在帖子中用unsigned char替換uint_8 ,可以避免未定義的行為,並最終實現特定於行為。 但是,如現在所寫,該標准並不禁止UB。

在您鏈接的帖子中的引號中明確指出了這一點:

最后,從C90到C99的更改之一是,消除了在最后一個存儲庫移到另一個存儲庫時訪問工會的一個成員的任何限制。 理由是行為將取決於值的表示形式。

根據定義,底層表示從未由標准定義。

暫無
暫無

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

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