2011-07-27 7 views
14

だから、私たちは、私は配列を持っているとしましょう:私は私のマシン上で、「A」の種類を確認した場合Tの配列がTへのポインタに崩壊したら、再びTの配列にすることはできますか?

int a[3] = { 1, 2, 3 }; 

は今、私が取得:

cout<<typeid(a).name(); // prints 'A3_i' 

私はアドレスを取る場合「A」の(私の心の中で「アドレスを取っ」とは逆の動作である「デリファレンス」ので、私は本当に好きなもの)、そのアドレスを逆参照、タイプは変更されません:

cout<<typeid(*&a).name(); // also prints 'A3_i' 

しかし、私derefere場合まず最初にnce 'a'を入力し、そのアドレスを取ってください。タイプに変更されます(これは私が嫌いではないことを認めています。 )がintが、私はへのポインタint型を取得する必要があり、それは私が判明:アレイ型がに減衰した後は

1):だからここ

cout<<typeid(&*a).name(); // prints 'Pi' 

は私の二つの質問ですポインタ型の場合、配列型に戻すにはどうしてですか?

私は鋳造様お-ジャスト・ドント・ケアの明確な戦略を試してみました:

cout<<typeid((int[3]) &*a).name(); // does not compile 
// "error: ISO C++ forbids casting to an array type `int [3]'" 

が働くだろうが、他のキャストですか?それとも、このタイプの変換は厳密に制限されていませんか?

2)アレイ型に戻ることができるかどうかにかかわらず、正確にどんな情報がスライスされ、崩壊からポインタへの処理で失われますか?

私は、ポインタ型と配列型が同等ではないことを理解しています。私は、配列型がポインタ型に格納されている情報の厳密なスーパーセットであると仮定します。これは正しいと思いますか?

私は他の質問では、配列型の余分な情報は次のとおりです:配列がスタック上にあるかどうか、またそのサイズについての知識(何とか配列のサイズを知っていなければならないタイプの一部です、そうですか?)配列型に他の情報が隠されていますか?

+0

int []は型ですが、int [3]は型ではありません。 –

+4

@Daniel: "3つのintの配列"は "int []"とは明確に区別され、 "int [3]"は前者のための合理的な省略表現のようです。 –

+0

あなたの言うことは何でも。 –

答えて

10

これはあなたが探しているものなのかどうか分かりませんが、元の配列と同じ型のオブジェクトを返すために型キャスティングを使うことができます。アイデアは、ほとんど知られていないタイプのポインタ配列と参照配列を使用して情報を復元することです。たとえば:

char arr[137]; 
cout << sizeof(arr) << endl; // Prints 137 
cout << sizeof(arr[0]) << endl; // Prints 1 
cout << sizeof(&arr[0]) << endl; // Prints 4 (on my system) 
cout << sizeof(*&arr[0]) << endl; // Prints 1 
cout << sizeof((char (&) [137]) *&arr[0]) << endl; // Prints 137 

アイデアは、私たちがchar (&)[137]、137文字の配列への参照を入力し*&arr[0]を使用して作成された参照型キャストということです。 13737文字の配列のサイズは137なので、sizeofオペレータは137を印刷する必要があることを認識しています。

ただし、これは正しい型に型変換する場合にのみ有効です。たとえば、これは完全に合法です:

char arr[137]; 
cout << sizeof((char (&) [42]) *&arr[0]) << endl; // Prints 42 

ですから、情報を回復することができますが、あなたは簡単にその情報が間違って取得し、あなたが間違った情報を回復してきた場合につながることができます。

また、これがあなたが探していたものかどうかはわかりませんが、実際にキャストを使用して配列サイズ情報を取得できることがわかります。

+1

しかし、ちょうどあなたが聞きたいものをキャストに伝えるのではないですか? また、&* a(配列型からポインタ型に型を変更する)の合成を行い、配列型は変更しません。 – Jimmy

+0

@ Jimmy-あなたは絶対に正しいです - これは配列にサイズが何であるかを伝えます。したがって、あなたが探しているものはわかりません。 '&* a'の場合は、キャスト'(T(*)[size])&* a'を使って配列に元のサイズBTWを伝えることができます。私はかなり一般的にこれを行うことはできませんし、一度配列のサイズがなくなったら、それはなくなりましたが、あなたはサイズが何であるべきかについて特別な知識がある場合、私が持っているものは正しいと思います。 – templatetypedef

+0

それはきれいです。私が試してみると:cout << typeid((int(*)[3])&* a).name()<< endl; // PA3_i ...を出力します。これは、私が望むタイプへのポインタです...だから私は試しました:cout << typeid(*(int(*)[3])&* a).name()<< endl ; // A3_iを印刷します。これは私が後にしたタイプです。私は解析の順序を理解していないので(タイプが実際に失われたかどうかはわかりませんが)、私は最初の質問に肯定的に答えると思います。 2番目の質問に対する考え? p.s.私は実際にint(*)をこのコメントに入力したときにint()がなぜint()を示すのかわかりません。 – Jimmy

3

これは、あなたの質問に完全な答えではありませんが、ここで私が提供しているものだ -

私はそれがポインタに減衰した後、アレイ型に戻って取得する方法はないと思います。

元の配列のタイプは、アレイ内の要素の数であるNT [N]、です。ポインタに壊れた後は、サイズ情報が失われてしまいます。 @ templatetypdefのanswerが示しているように、ポインタを配列型にキャストすることができますが、次にキャストすることによってコンパイラにサイズが何であるかを伝えるだけで、実際にその情報を推測する方法はありませんポインタから。

元の型情報を保持するには、ポインタではなく参照によって配列を渡す必要があります。

#include <iostream> 
#include <typeinfo> 

template<size_t N> 
void ByRef(int(&array)[N]) 
{ 
    std::cout << typeid(array).name() << std::endl; 
} 

void ByPointer(int *array) 
{ 
    std::cout << typeid(array).name() << std::endl; 
    std::cout << typeid((int(&)[4])array).name() << std::endl; 
    std::cout << typeid((int(&)[42])array).name() << std::endl; 
} 

int main() 
{ 
    int a[4] = {1,2,3,4}; 

    ByRef(a); 
    ByPointer(a); 

    return 0; 
} 

上記の出力は、あなたがに取得しているかわから

A4_i 
Pi 
A4_i 
A42_i 
2

ではありませんが、あなたが何かの&を取る一度あなたが持っているすべてのアドレスです。単純な非オブジェクトデータ構造には記述子が含まれていないため、アドレスを指定すると、それが処理するものについて何らかの情報を得る方法はありません。たとえそれがcharに対処しているとしても、ポインタタイプによって伝えられます。void*にキャストすると、その情報は失われてしまいます。

私は、ポインタ型と配列型が同等ではないことを理解しています。私は、配列型がポインタ型に格納されている情報の厳密なスーパーセットであると仮定します。これは正しいと思いますか?

情報がタイプに「格納」されていません。情報(実行時に存在する場合)は変数に格納されます。 C/C++では、非オブジェクトの動的な型定義がないため、型に情報が格納されているだけでなく、型情報がどこにも格納されません。型情報は純粋にコンパイル時の概念であり、コンパイラによる静的解析の結果として、純粋にプログラム内のある点から別の点へと「流れる」。つまり、それは便利なフィクションです。

+1

私はあなたの説明を理解すると信じています。私が「タイプ情報」と呼んだのは、コンパイラが適切に動作するように指示する仕様です。したがって、配列型の '余分な情報'は、コンパイル時に追加チェックが行われるだけです(つまり、ポインタ型が必ずしも対象ではないコンパイル時検証)。 – Jimmy

+0

多態性クラスに対して有効にしない限り、または最初に多型性を実装するために使用されたものを数えない限り、実行時の型情報はありません。しかし、コンパイル時の型情報は「余分なチェック」以上のものをコントロールします。配列はポインタではありません。 'int x [3]'は概念的には、3つの整数のブロックのためにスタック上にメモリを割り当てます。アドレスは要求に応じて計算できます(崩壊プロセス)。 'int * x'は、**のアドレス**を指定せずに、アドレスのスタック上に概念的にメモリを割り当てます。 –

+0

ジミー、私はあなたがそれを持っていると思う。ほとんどのマシンでは、ビットのグループがポインタか否か、整数か、文字のグループか、その他のものかを判断する方法さえありません。 「機能ベース」であり、他のデータとのポインターを区別する方法がある(さらに、タイプに関していくつかのランタイムの手がかりを持っているいくつかのマシン(IBM iSeriesがまだ作成している唯一のマシンだと思います) )、しかしこれらは明確な例外です。 –

関連する問題