簡體   English   中英

Reinterpret_cast與C風格演員

[英]Reinterpret_cast vs. C-style cast

我聽說reinterpret_cast是實現定義的,但我不知道這究竟意味着什么。 你能提供一個如何出錯的例子,它出錯了,使用C-Style演員會更好嗎?

C風格的演員並不是更好。

它只是按順序嘗試各種C ++風格的轉換,直到找到一個有效的轉換。 這意味着,當它就像一個reinterpret_cast ,它具有完全相同的問題,作為一個reinterpret_cast 但另外,它有這些問題:

  • 它可以做很多不同的事情,並且通過閱讀代碼調用哪種類型的static_cast const_cast並不總是很清楚(它可能表現得像reinterpret_castconst_caststatic_cast ,而那些做非常不同的事情)
  • 因此,更改周圍的代碼可能會更改強制轉換的行為
  • 在閱讀或搜索代碼時很難找到 - reinterpret_cast很容易找到,這很好,因為演員表很難看,使用時應該注意。 相反,通過搜索可以更加可靠地找到C風格的強制轉換(如(int)42.0

要回答問題的其他部分,是的, reinterpret_cast是實現定義的。 這意味着當您使用它從int*轉換為float* ,您無法保證結果指針將指向同一地址。 那部分是實現定義的。 但是如果你把生成的float*reinterpret_cast放回到int* ,那么你將獲得原始指針。 這部分是有保證的。

但同樣,請記住,無論使用reinterpret_cast還是C風格的reinterpret_cast ,都是如此:

int i;
int* p0 = &i;

float* p1 = (float*)p0; // implementation-defined result
float* p2 = reinterpret_cast<float*>(p0); // implementation-defined result

int* p3 = (int*)p1; // guaranteed that p3 == p0
int* p4 = (int*)p2; // guaranteed that p4 == p0
int* p5 = reinterpret_cast<int*>(p1); // guaranteed that p5 == p0
int* p6 = reinterpret_cast<int*>(p2); // guaranteed that p6 == p0

它是在某種意義上定義的實現,標准不會(幾乎)規定不同類型值在位級別上應該是什么樣子,應該如何構造地址空間等等。 所以它真的是一個非常適合轉換的平台,例如:

double d;
int &i = reinterpret_cast<int&>(d);

但正如標准所說

對於那些了解底層機器的尋址結構的人來說,這並不奇怪。

因此,如果你知道你做了什么以及它在低級別上看起來如何,那么什么都不會出錯。

C風格的強制轉換在某種程度上類似於它可以執行reinterpret_cast,但它也首先“嘗試”static_cast並且它可以拋棄cv資格(而static_cast和reinterpret_cast不能)並執行轉換而忽略訪問控制(參見5.4) / 4在C ++ 11標准中)。 例如:

#include <iostream>

using namespace std;

class A { int x; };
class B { int y; };

class C : A, B { int z; };

int main()
{
  C c;

  // just type pun the pointer to c, pointer value will remain the same
  // only it's type is different.
  B *b1 = reinterpret_cast<B *>(&c);

  // perform the conversion with a semantic of static_cast<B*>(&c), disregarding
  // that B is an unaccessible base of C, resulting pointer will point
  // to the B sub-object in c.
  B *b2 = (B*)(&c);

  cout << "reinterpret_cast:\t" << b1 << "\n";
  cout << "C-style cast:\t\t" << b2 << "\n";
  cout << "no cast:\t\t" << &c << "\n";
}

這是ideone的輸出:

reinterpret_cast:  0xbfd84e78
C-style cast:      0xbfd84e7c
no cast:           0xbfd84e78

請注意,reinterpret_cast生成的值與'c'的地址完全相同,而C樣式轉換導致正確的偏移指針。

有正當理由使用reinterpret_cast ,由於這些原因,標准實際上定義了發生的事情。

第一種是使用不透明的指針類型,既可以用於庫API,也可以用於在單個數組中存儲各種指針(顯然還有它們的類型)。 您可以將指針轉換為適當大小的整數,然后返回指針,它將是完全相同的指針。 例如:

T b;
intptr_t a = reinterpret_cast<intptr_t>( &b );
T * c = reinterpret_cast<T*>(a);

在這個代碼中, c保證像你期望的那樣指向對象b 轉換回不同的指針類型當然是未定義的(有點)。

函數指針和成員函數指針允許類似的轉換,但在后一種情況下,您可以轉換為/從另一個成員函數指針轉換,只是為了擁有一個大的enouhg變量。

第二種情況是使用標准布局類型。 這是在C ++ 11之前支持的因素,現在已在標准中指定。 在這種情況下,標准將reinterpret_cast視為static_cast,先將void *視為void *,然后將static_cast視為desination類型。 在進行二進制協議時會使用這種方法,其中數據結構通常具有相同的頭信息,並允許您轉換具有相同布局但C ++類結構不同的類型。

在這兩種情況下,您都應該使用顯式reinterpret_cast運算符而不是C樣式。 雖然C風格通常會做同樣的事情,但它有遭受過載轉換運算符的危險。

C ++有類型,它們通常在彼此之間進行轉換的唯一方法是使用您編寫的定義良好的轉換運算符。 一般來說,這就是你們都需要並且應該用來編寫你的程序。

但是,有時您希望將表示類型的位重新解釋為其他內容。 這通常用於非常低級別的操作,而不是您通常應該使用的操作。 對於這些情況,您可以使用reinterpret_cast

它是實現定義的,因為C ++標准並沒有真正說明事情應該如何在內存中進行布局。 這是由您特定的C ++實現控制的。 因此, reinterpret_cast的行為取決於編譯器如何在內存中放置結構以及它如何實現reinterpret_cast

C風格的強制轉換非常類似於reinterpret_cast ,但它們的語法要少得多,不推薦使用。 我們的想法是,轉換本質上是一個丑陋的操作,它需要丑陋的語法來通知程序員發生了一些可疑的事情。

它可能出錯的一個簡單示例:

std::string a;
double* b;
b = reinterpret_cast<double*>(&a);
*b = 3.4;

該程序的行為未定義 - 編譯器可以做任何它喜歡的事情。 最有可能的是,當string的析構函數被調用時,你會遇到崩潰,但誰知道呢! 它可能只會損壞您的堆棧並導致無關函數崩潰。

reinterpret_cast和c-style強制轉換都是實現定義的,它們幾乎完全相同。 不同之處是:
1. reinterpret_cast無法刪除constness。 例如 :

const unsigned int d = 5;
int *g=reinterpret_cast< int* >( &d );

會發出錯誤:

error: reinterpret_cast from type 'const unsigned int*' to type 'int*' casts away qualifiers  

2.如果使用reinterpret_cast ,則很容易找到您執行此操作的位置。 使用c風格的演員陣容是不可能的

C風格的強制轉換有時會以未指定的方式對對象進行類型化處理,例如(unsigned int)-1 ,有時會將相同的值轉換為不同的格式,例如(double)42 ,有時也可以這樣做,如何(void*)0xDEADBEEF重新解釋位但是(void*)0保證是一個空指針常量,它不一定具有與(intptr_t)0相同的對象表示,並且很少告訴編譯器執行類似shoot_self_in_foot_with((char*)&const_object);

這通常都很好,但是當你想要將一個double uint64_t轉換為uint64_t ,有時你想要這個值,有時你想要這些位。 如果你知道C,你知道C風格的演員會做哪一個,但是在某些方面它更好,兩者都有不同的語法。

Bjarne Stroustrup在他的指導方針中,在另一個上下文中推薦了reinterpret_cast :如果你想以一種語言沒有用static_cast定義的方式來打字,他建議你用reinterpret_cast<double&>(uint64)類的東西reinterpret_cast<double&>(uint64)比其他方法。 它們都是未定義的行為,但這使得它非常明確地表達了您正在做的事情以及您是故意這樣做的。 讀一個聯盟中不同的成員而不是你上次寫的那個。

暫無
暫無

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

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