実際、あなたは正しいと同時に間違っています。
Cには、任意の左辺値を任意の左辺値に安全に型キャストする機能があります。ただし、構文は単純なアプローチとは少し異なります。
左辺値ポインターは、Cで次のように異なるタイプの左辺値ポインターにキャストできます。
char *ptr;
ptr = malloc(20);
assert(ptr);
*(*(int **)&ptr)++ = 5;
すべての位置合わせ要件を満たすために必要なため、これmalloc()
も許容できる使用法です。ただし、以下は移植性がなく、特定のマシンでの位置合わせが間違っているために例外が発生する可能性があります。
char *ptr;
ptr = malloc(20);
assert(ptr);
*ptr++ = 0;
*(*(int **)&ptr)++ = 5; /* can throw an exception due to misalignment */
要約すると:
- ポインタをキャストすると、右辺値になります。
- ポインタで使用
*
すると、左辺値になります(*ptr
に割り当てることができます)。
++
(のように*(arg)++
)を操作するには左辺値が必要です(左辺値arg
である必要があります)
したがって、は左辺値であるため((int *)ptr)++
失敗しますが、そうではありません。は、と書き直すことができますが、キャストが原因で失敗し、純粋な右辺値になります。ptr
(int *)ptr
++
((int *)ptr += 1, ptr-1)
(int *)ptr += 1
これは言語の欠点ではないことに注意してください。キャストは左辺値を生成してはなりません。以下を見てください:
(double *)1 = 0;
(double)ptr = 0;
(double)1 = 0;
(double *)ptr = 0;
最初の3つはコンパイルされません。なぜ誰かが4行目がコンパイルされることを期待するのでしょうか?プログラミング言語は、そのような驚くべき振る舞いを決して公開してはなりません。さらに、これはプログラムの動作が不明確になる可能性があります。検討:
#ifndef DATA
#define DATA double
#endif
#define DATA_CAST(X) ((DATA)(X))
DATA_CAST(ptr) = 3;
これはコンパイルできませんよね?しかし、あなたの期待が成り立つならば、これは突然cc -DDATA='double *'
!でコンパイルされます。安定性の観点から、特定のキャストにそのようなコンテキスト左辺値を導入しないことが重要です。
Cの正しいことは、左辺値があるかどうかということです。これは、意外かもしれない任意のコンテキストに依存してはなりません。
Jensが指摘したように、左辺値を作成する演算子はすでに1つあります。これは、ポインターの間接参照演算子である「単項*
」です(のように*ptr
)。
として書くことができ、*ptr
として書くことができることに注意してください。配列の添え字は左辺値であるため、左辺値も同様です。0[ptr]
*ptr++
0[ptr++]
*ptr
待って、何? 0[ptr]
エラーに違いないですよね?
実は違う。それを試してみてください!これは有効なCです。次のCプログラムは、すべての点でIntel 32/64ビットで有効であるため、コンパイルして正常に実行されます。
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
int
main()
{
char *ptr;
ptr = malloc(20);
assert(ptr);
0[(*(int **)&ptr)++] = 5;
assert(ptr[-1]==0 && ptr[-2]==0 && ptr[-3]==0 && ptr[-4]==5);
return 0;
}
Cでは、両方を持つことができます。左辺値を作成しないキャスト。そして、ある意味でキャストを使用する機能。これにより、左辺値プロパティを存続させることができます。
ただし、キャストから左辺値を取得するには、さらに2つの手順が必要です。
- キャストする前に、元の左辺値のアドレスを取得します。左辺値なので、いつでもこのアドレスを取得できます。
- 目的のタイプのポインターにキャストします(通常、目的のタイプはポインターでもあるため、そのポインターへのポインターがあります)。
- キャスト後、この追加のポインターを逆参照します。これにより、左辺値が再び得られます。
したがって、間違っている代わりに、*((int *)ptr)++
を書くことができます*(*(int **)&ptr)++
。これはまたptr
、この式ですでに左辺値でなければならないことを確認します。または、Cプリプロセッサを使用してこれを作成するには:
#define LVALUE_CAST(TYPE,PTR) (*((TYPE *)&(PTR)))
したがって、渡されたvoid *ptr
もの(に偽装する可能性がありますchar *ptr
)については、次のように書くことができます。
*LVALUE_CAST(int *,ptr)++ = 5;
通常のポインタ演算の警告(プログラムの異常終了または互換性のないタイプでの未定義の動作。これは主にアラインメントの問題に起因します)を除いて、これは適切なCです。