簡體   English   中英

你能不能假設類型轉換指針是安全的?

[英]Can you ever assume typecasting pointers is safe?

我從很多人那里聽說你不能保證類型轉換會無損。 只有在您不了解處理器時才會這樣,也就是說,您還沒有驗證用於數據類型的字節數? 讓我舉個例子吧:

如果執行以下操作:

typedef struct
{
    int i;
    char c;
    float f;
    double d;
} structure;

size_t voidPtrSz = sizeof(void *);
size_t charPtrSz = sizeof(char *);
size_t intPtrSz = sizeof(char *);
size_t floatPtrSz = sizeof(float *);
size_t doublePtrSz = sizeof(double *);
size_t structPtrSz = sizeof(structure *);
size_t funcPtrSz = sizeof(int (*)(float, char));

printf("%lu\n", voidPtrSz);
printf("%lu\n", charPtrSz);
printf("%lu\n", intPtrSz);
printf("%lu\n", floatPtrSz);
printf("%lu\n", doublePtrSz);
printf("%lu\n", structPtrSz);
printf("%lu\n", funcPtrSz);

......輸出如下......

4
4
4
4
4
4
4

您能否假設在所有情況下都可以安全地將特定數據類型指針類型轉換為另一個數據類型指針? 例如,如果您執行此操作:

int foo(float, char)
{
}

void *bar(void)
{
    return (void *)foo;
}

int (*pFunc)(float, char) = bar();

你能否確定pFunc的地址是foo

關於您的特定代碼示例,讓我們參考C99語言標准的第6.3.2.3節:

指向void的指針可以轉換為指向任何不完整或對象類型的指針。 指向任何不完整或對象類型的指針可能會轉換為指向void的指針並再次返回; 結果應該等於原始指針。

請注意,指向函數的指針與指向對象的指針不同。 唯一提到指針到函數的轉換是:

指向一種類型的函數的指針可以被轉換為指向另一種類型的函數的指針並且再次返回; 結果應該等於原始指針。 如果轉換的指針用於調用類型與指向類型不兼容的函數,則行為未定義。

所以你的代碼示例調用了未定義的行為。

如果我們避免使用函數指針轉換,以下段落將解釋所有內容:

指向對象或不完整類型的指針可以轉換為指向不同對象或不完整類型的指針。 如果生成的指針未針對指向類型正確對齊,則行為未定義。 否則,當再次轉換回來時,結果將等於原始指針。

注意:在指針類型之間進行轉換是轉換然后解除引用的一個單獨問題(通常,只有在轉換為char *然后解除引用時才有效)。

您能否假設在所有情況下都可以安全地將特定數據類型指針類型轉換為另一個數據類型指針?

任何數據指針都可以安全地轉換為char*void* 這樣創建的任何char*void*都可以轉換回其原始類型。 當對指針執行間接尋址時,任何其他數據指針強制轉換都會導致未定義的行為。

任何函數指針類型都可以轉換為任何其他函數指針類型,但不應該通過錯誤的類型調用函數。 將函數指針強制轉換為void*或任何其他數據指針類型會導致未定義的行為。

只有在您不了解處理器時才會這樣,也就是說,您還沒有驗證用於數據類型的字節數?

即便如此,你也不安全。 當C標准表示構造具有未定義的行為時,編譯器編寫者可以根據需要自由地處理構造。 結果是,即使您認為您知道具有UB的構造將被處理,因為您知道目標CPU,優化編譯器可能會削減角落並生成與您預期的完全不同的代碼。

@Oli Charlesworth給你一個很好的答案。

我希望我能對指針是什么有所了解,這樣你就可以更好地理解指針機制:

指針是一個地址。 該地址是數據第一個字節的地址。 指針的類型指定從第一個字節開始的字節數是數據的一部分以及這些字節如何編碼數據。

例如,在gcc x86上,如果你有一個int * pint * p的值告訴數據的起始地址,p( int * )的類型告訴他在那個地址他將解釋4個字節(在小端字節順序)以二進制補碼有符號數表示。

void *指針是“通用指針”。 指針仍然保存一個地址,但指針類型沒有指定你在那里找到什么樣的數據,甚至沒有指定數據的字節數,所以你永遠不能通過void *指針訪問數據,但是如前所述,你可以安全地在指向void的指針和指向任何不完整或對象類型的指針之間進行轉換。

指向函數的指針保存函數的地址,指針的類型告訴如何調用該函數(什么參數和什么類型)以及函數返回的內容。

暫無
暫無

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

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