簡體   English   中英

reinterpret_cast <char *>(p)或static_cast <char *>((void *)p))對於字節指針的區別,哪個更好?

[英]reinterpret_cast<char*>(p) or static_cast<char*>((void*)p)) for bytewise pointer difference, which is better?

以下三個強制轉換用於提取用於指針算術的原始字節指針有什么區別嗎? (假設一個char為1個字節的平台。)

  1. static_cast<char*>((void*)ptr))
  2. reinterpret_cast<char*>(ptr)
  3. (更新)或: static_cast<char*>(static_cast<void*>(ptr))

我應該選擇哪個?

更詳細......

給定一個類中兩個成員對象的指針,我想計算從一個到另一個的偏移量,這樣我就可以重建給定偏移量的一個成員的地址和另一個成員的地址。

// assumed data layout:
struct C {
  // ...
  A a;
  // ...
  B b;
}

我目前使用的代碼是:

void approach1( A *pa, B *pb )
{
  // compute offset:
  std::ptrdiff_t offset = static_cast<char*>((void*)pa) - static_cast<char*>((void*)pb);
  // then in some other function...
  // given offset and ptr to b, compute ptr to a:
  A *a = static_cast<A*>( (void*)(static_cast<char*>((void*)pb) + offset) );
}

main()
{
  C c;
  approach1(&c.a, &c.b);
}

我想知道以下是否更好(或更糟):

void approach2( A *pa, B *pb )
{
  std::ptrdiff_t offset = reinterpret_cast<char*>(pa) - reinterpret_cast<char*>(pb);
  // ...
  A *a = reinterpret_cast<A*>( reinterpret_cast<char*>(pb) + offset );
}

這兩種方法完全相同嗎? 它們同樣便攜嗎?

我的印象是, approach1()更具可移植性,因為“ static_cast指向和來自void*的指針會保留地址 ”,而reinterpret_cast<>保證較少(請參閱鏈接中接受的答案)。

我想知道最干凈的方法是什么。

更新:目的說明

很多人都問過計算這些補償的目的是什么。 目的是構造實例偏移的元類表。 這由運行時反射機制用於自動GUI構建和持久性(偏移量不是序列化的,僅用於遍歷結構)。 該代碼已經生產超過15年。 出於這個問題的目的,我只想知道計算指針偏移的最便攜方式。 我無意對元類系統的工作方式進行大的改動。 另外,我一般也對最好的方法感興趣,因為我有其他用途(例如共享內存代碼的差異指針)。

注意:我不能使用offsetof()因為在我的實際代碼中我只有指向實例ab的指針,我不一定有包含對象的類型c或其他靜態信息來使用offsetof() 我只能假設ab是同一個對象的成員。

這兩個將導致相同的結果,因此差異主要是語義,並且reinterpret_cast具有您想要的操作的含義,以及只需要一個強制轉換而不是兩個強制轉換的事實(並且您的代碼中的投射較少)更好)。

reinterpret_cast的

5.2.10 / 7 :可以將對象指針顯式轉換為不同類型的對象指針。 當對象指針類型的prvalue v被轉換為對象指針類型“指向cv T的指針”時,結果是static_cast <cv T *>(static_cast <cv void *>(v))。

因此,除非在中年平台上出現一個exotique隨機低級別的不同行為,你絕對應該:

reinterpret_cast<char*>(ptr);

一般來說。

那就是說,你為什么不在你的情況下使用uintptr_t 它更加合適,你不需要指針:

void approach3( A *pa, B *pb )
{
  std::ptrdiff_t offset = reinterpret_cast<std::uintptr_t>(pa) - reinterpret_cast<std::uintptr_t>(pb);
  // ...
  A *a = reinterpret_cast<A*>( reinterpret_cast<std::uintptr_t>(pb) + offset );
}

有關其他信息,請參閱

http://en.cppreference.com/w/cpp/language/reinterpret_cast

我不建議計算班級成員地址之間的偏移距離。 編譯器可能會注入填充數據,或者即使它正在工作,它也只能在該特定主機上運行的特定編譯器上以相同的方式工作。 應用此實踐時存在大量錯誤來源。 例如,如果您必須處理多個虛擬繼承中的着名虛擬表和內存布局,該怎么辦? 這將完全使您的解決方案無法使用。

所以回到根源:你為什么要這樣做? 也許有更好的解決方案。

編輯/更新

謝謝你解釋我們原因。 這是一個我直到現在才看到的非常有趣的方法。 我今天學到了一些東西。

但是,我仍然堅持認為應該有一種更簡單的方法來處理這個問題。 就像一個證明的概念,我寫了一個小應用程序,只是為了看看你的哪些方法正常工作。 對我來說,他們都不工作。

該應用程序是一個稍微擴展的方法,在這里它是:

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

struct A
{
    A(const std::string& pa) : a(pa) {printf("CTR: A address: %p\n", this) ;}
    std::string a;
};

struct B
{
    B(const std::string& pb) : b(pb) {printf("CTR: B address: %p\n", this) ;}
    std::string b;
};

// assumed data layout:
struct C {

    C() : a("astring"), b("bstring") {}
  // ...
  A a;
  // ...
  B b;
};

void approach1( A *pa, B *pb )
{

    printf("approach1: A address: %p B address: %p\n", pa, pb); 
    // compute offset:
    std::ptrdiff_t offset = static_cast<char*>((void*)pb) - static_cast<char*>((void*)pa);
    // then in some other function...
    // given offset and ptr to b, compute ptr to a:
    A *a = static_cast<A*>( (void*)(static_cast<char*>((void*)pb) + offset) );
    printf("approach1: a address: %p \n", a); 

    std::cout << "approach1: A->a=" << a->a << std::endl;
}


void approach2( A *pa, B *pb )
{
    printf("approach2: A address: %p B address: %p\n", pa, pb); 

    std::ptrdiff_t offset = reinterpret_cast<char*>(pb) - reinterpret_cast<char*>(pa);

    A *a = reinterpret_cast<A*>( reinterpret_cast<char*>(pb) + offset );
    printf("approach2: a address: %p \n", a); 
    std::cout << "approach2: A->a=" << a->a << std::endl;
}

main()
{
  C c;
  std::cout << c.a.a << std::endl;

  approach1(&c.a, &c.b);
  approach2(&c.a, &c.b);
}

它的輸出在我的計算機上( uname -a Linux flood 3.13.0-33-generic #58-Ubuntu SMP Tue Jul 29 16:45:05 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux )和我的編譯器( g++ (Ubuntu 4.8.2-19ubuntu1) 4.8.2 )是:

CTR: A address: 0x7fff249f0900
CTR: B address: 0x7fff249f0908
astring
approach1: A address: 0x7fff249f0900 B address: 0x7fff249f0908
approach1: a address: 0x7fff249f0910 
approach1: A->a=<GARBAGE>
approach2: a address: 0x7fff249f0910 

其中<GARBAGE>按預期包含...垃圾。

請參閱: http//ideone.com/U8ahAL

暫無
暫無

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

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