簡體   English   中英

C / C ++ Struct內存布局等效

[英]C/C++ Struct memory layout equivalency

考慮以下C結構和C ++結構聲明:

extern "C" { // if this matters
typedef struct Rect1 {
  int x, y;
  int w, h;
} Rect1;
}

struct Vector {
  int x;
  int y;
}

struct Rect2 {
  Vector pos;
  Vector size;
}
  • Rect1Rect2對象的內存布局是否始終相同?

  • 具體來說,我可以放心reinterpret_castRect2*Rect1* ,並假設所有四個int中的值Rect2對象匹配一對一的四個int以秒Rect1

  • 如果我將Rect2更改為非POD類型(例如通過添加構造函數),會有所不同嗎?

  • 我會這樣認為,但我也認為Rect2::posRect2::size之間可能(合法地)填充。 因此,為確保確定,我將添加特定於編譯器的屬性以“打包”字段,從而確保所有int都是相鄰且緊湊的。 這與C與C ++的關系不那么多,而更多的事實是,當使用兩種語言進行編譯時,即使這些編譯器來自同一家供應商,也可能會使用兩個“不同的”編譯器。
  • 使用reinterpret_cast將一種類型的指針轉​​換為另一種類型的指針,您可能會違反“嚴格別名”規則。 假設您之后確實取消了指針的引用,在這種情況下,您將取消引用。
  • 添加構造函數不會更改布局(盡管它將使類變為非POD),但是在兩個字段之間添加諸如private訪問說明符可能會更改布局(實際上,不僅在理論上)。

Rect1和Rect2對象的內存布局是否始終相同?

是。 只要滿足某些明顯的要求,就可以保證它們是相同的。 這些明顯的要求是關於目標平台/體系結構在對齊方式和字長方面都相同。 換句話說,如果您愚蠢到為不同的目標平台(例如32位和64位)編譯C和C ++代碼並嘗試將它們混合使用,那么您將遇到麻煩,否則,您不必令人擔心的是,基本上要求C ++編譯器產生與在C中相同的內存布局,並且對於給定的字長和對齊方式,ABI在C中固定。

具體來說,我是否可以安全地將Rect2 *從Rect2 *重新解釋為Rect1 *,並假設Rect2對象中的所有四個int值都與Rect1中的四個int一對一匹配?

是。 從第一個答案開始。

如果我將Rect2更改為非POD類型(例如通過添加構造函數),會有所不同嗎?

不,或者至少不再。 唯一重要的是,該類仍然是標准布局類,不受構造函數或任何其他非虛擬成員的影響。 從C ++ 11(2011)標准開始有效。 在此之前,該語言是關於“ POD類型”的,正如我剛才在標准布局中給出的鏈接中所述。 如果您使用的是C ++ 11之前的編譯器,那么很有可能仍按與C ++ 11標准相同的規則進行工作(C ++ 11標准規則(對於標准布局和平凡類型)基本上是編寫以匹配所有編譯器供應商已經做的事情。

對於像您這樣的標准布局類,您可以輕松地從結構開始檢查結構成員的位置。

#include <cstddef>

int x_offset = offsetof(struct Rect1,x); // probably 0
int y_offset = offsetof(struct Rect1,y); // probably 4
....
pos_offset = offsetof(struct Rect2,pos); // probably 0
....

http://www.cplusplus.com/reference/cstddef/offsetof/

是的,它們將始終相同。 您可以嘗試在此處運行以下示例cpp.sh它按預期運行。

  // Example program
#include <iostream>
#include <string>

typedef struct Rect1 {
  int x, y;
  int w, h;
} Rect1;

struct Vector {
  int x;
  int y;
};

struct Rect2 {
  Vector pos;
  Vector size;
};


struct Rect3 {
  Rect3():
   pos(),
   size()
  {}
  Vector pos;
  Vector size;
};


int main()
{

  Rect1 r1;
  r1.x = 1;
  r1.y = 2;
  r1.w = 3;
  r1.h = 4;
  Rect2* r2 = reinterpret_cast<Rect2*>(&r1);
  std::cout << r2->pos.x << std::endl;
  std::cout << r2->pos.y << std::endl;
  std::cout << r2->size.x << std::endl;
  std::cout << r2->size.y << std::endl;


  Rect3* r3 = reinterpret_cast<Rect3*>(&r1);
  std::cout << r3->pos.x << std::endl;
  std::cout << r3->pos.y << std::endl;
  std::cout << r3->size.x << std::endl;
  std::cout << r3->size.y << std::endl;
}

暫無
暫無

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

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