私は C# と Java のバックグラウンドを持っていますが、C++ でポインターを使用したキャストが何を意味するのか理解できないようです。
例えば:
int x = 1;
char c = *((char*)&x);
それは何をするためのものか?それは何に役立ちますか?
どちらの例でも、コードがコンパイルされないという間違いを犯しています。したがって、次のことをしようとしていると仮定します。
int x = 1;
char c = *((char*)&x);
アーキテクチャに応じて、c
の最下位バイトまたは最上位バイトの値のいずれかになりますx
。この例では、これは 0 または 1 のいずれかになります (これは実際にバイト順序を検出するために使用できます)。
2 番目の例は機能しません。const
無効な操作や不正なキャストを無視しようとしているためです (これは "const correctness" とも呼ばれます)。
編集:「それはどういう意味ですか?」に関するあなたのコメントについて:
式:
&somevariable
のアドレスを返しますsomevariable
。
の内容が実際の値のアドレスである*somevariable
と想定し、実際の値が返されます。somevariable
宣言内:
datatype
通常の変数/オブジェクトです。これは「値渡し」で渡されます。
datatype&
参考です。これは、Java/C# の通常の変数とまったく同じように機能し、参照によって渡されます。
datatype*
ポインタです。これには、実際の値が配置されているアドレス (上記を参照) が含まれているだけであり、基本的に参照によっても渡されます。
実際のキャストは Java/C# とほとんど同じように機能しますが、ポインターはまさにそれであり、実際の値の場所を指します。混乱するかもしれませんが、C/C++ のポインターは、Java/C# で使用される標準の変数/参照とほとんど同じように機能します。
これを見てください:
MyClass x; // object of MyClass
MyClass *x; // pointer to an object of MyClass - the actual value is undefined and trying to access it will most likely result in an access violation (due to reading somewhere random).
MyClass *x = 0; // same as above, but now the default value is defined and you're able to detect whether it's been set (accessing it would essentially be a "null reference exception"; but it's actually a null pointer).
MyClass &x = MyClass(); // creating a new reference pointing to an existing object. This would be Java's "MyClass x = new MyClass();"
C++ でのキャストは、Java でのキャストと同じように機能し、ポインターは関係ありません。
int x = 1;
char c = (char) x; // Lose precision
ただし、ここで何をしているのか:
int x = 1;
char *c = (char *)x;
x
の値が文字のアドレスであることをコンパイラに伝えています。と同等です
char *c;
c = 1; // Set the address of c to 0x0000000000000001
これを行う必要がある場合はほとんどありません。
C++ には 2 つの根本的に異なる概念があり、どちらも「キャスト」と呼ばれることがあります。1 つは変換で、もう 1 つは再解釈です。
変換により、既存のオブジェクトと「同じ値」を持つ新しいオブジェクトが作成されますが、型は異なります。ここではいくつかの例を示します。
例 1: タイプの昇格
// 1a: promote int to double to get the correct type of division
int numerator = rand(), denominator = rand();
double d = double(numerator) / double(denominator);
// 1b: convert int to double to achieve a particular argument deduction
int n;
template <typename T> void do_numeric_stuff(T x) { /* ... */ }
do_numeric_stuff(double(n));
例 2: 派生から基底への変換
struct B { }; struct D : B { };
D x;
D * p = &x; // pointer to x
B * q = p; // implicit conversion; may change the value!
一方、再解釈では、ある変数を別の変数であるかのように扱うことができます。これに対する唯一の適切で有用なアプリケーションは、何らかの形でのシリアライゼーションです。
例 3: シリアル化
std::ofstream file("output.bin"); // output file
char large_buffer[HUGE]; // in-memory buffer
unsigned int n = get_data();
char const * p = reinterpret_cast<char const *>(&n);
file.write(p, p + sizeof n); // write the bytes of `n`
std::copy(p, p + sizeof n, large_buffer); // ditto
std::copy(large_buffer + 17, large_buffer + 17 + sizeof n,
reinterpret_cast<char *>(&n)); // repopulate `n` from buffer
標準では、正しい型ではないポインターを介してオブジェクトにアクセスすることは未定義の動作であると述べています (「型パニング」とも呼ばれます)。オブジェクト ポインターを a などに格納し、void*
それを変換して使用することは問題ありませんが、float を整数であるかのように扱うことは問題があります。つまり、型のオブジェクトをT
配列であるかのように扱います。つまりchar[sizeof(T)]
、すべてのオブジェクトの基になるバイナリ表現にアクセスできます。
(char*) のような c 型のキャストは絶対に避けるべきです。本当に型キャストを行う必要がある場合はdynamic_cast
、static_cast
とを参照してくださいreinterpret_cast
。
しかし、すでに述べたように、キャストが必要になることはめったにありません。
詳細については、こちらをご覧ください: http://www.cplusplus.com/doc/tutorial/typecasting/
http://www.parashift.com/c++-faq/static-typing-and-cpp.html
http://www.parashift.com/c++-faq-lite/print-char-or-ptr-as-number.html
カスタム IO ボード上の指定されたアドレスで HW にアクセスするために、私はずっと前にそのイディオムを使用しました。たとえば、PIC (プログラマブル割り込みコントローラー) に書き込み、いくつかのフラグ (架空のコード) をリセットするには:
#define PIC_LOC 0x1000
#define PIC_ENABLE_PORT *((char*)(PIC_LOC+0x10))
#define BIT_ENABLE (1 << 3)
...
PIC_ENABLE_PORT |= BIT_ENABLE;
...