6

最近、いくつかのポインターの値をコンソールに出力して、小さなプログラムのデバッグを試みました。1 つ目は構造体のメモリ アドレスで、残りはそのフィールドのメモリ アドレスでした。コードの簡素化されたバージョンは次のとおりです。

#include <iostream>

struct testingPointers
{
    int i;
    float f;
    double d;
} test;

int main()
{
   std::cout << &test << '\n' << &(test.i) << '\n' << 
            &(test.f) << '\n' << &(test.d);
}

出力は次のとおりです。

0x681110
0x681110
0x681114
0x681118

(明らかに、正確な値は実行ごとに異なりますが、互いに相対的な位置は常に同じです)。

最初のポインタの値 ( のメモリ位置) がtest2 番目のポインタ ( の最初のフィールド) の値と同じであるため、混乱していますtest。これは、オブジェクトには真の一意のメモリ アドレスがなく、構造体またはクラスへのポインターが単に最初のフィールドを指していることを意味するのでしょうか? もしそうなら、どのようにステートメントは

a.b
a->b
a.b()

aが実際には最初のフィールドにすぎず、フィールドやメソッドがない場合は意味がありますか?

4

4 に答える 4

7

オブジェクトのアドレスは常に、そのオブジェクト内の最初の非静的メンバーのアドレスでなければなりません。標準からの引用 (C++11-9.2-20):

reinterpret_cast を使用して適切に変換された標準レイアウトの構造体オブジェクトへのポインターは、その最初のメンバー (または、そのメンバーがビットフィールドの場合は、それが存在するユニット) を指し、その逆も同様です。[ 注: したがって、標準レイアウトの構造体オブジェクト内に名前のないパディングがある場合がありますが、適切な配置を実現するために必要なため、先頭にはありません。

standard-layoutの要件は次のとおりです: StandardLayoutType

これは確かにネストを介して適用できます。標準では、ビット フィールドを除いて、最初のメンバーの型に例外はありません。すなわち:

class X
{
public:
    int x;
};

class Y
{
public:
    X x;
    int y;
};

Y yobj;

標準では、&yobj == &yobj.x == &yobj.x.x.

于 2012-11-20T23:58:13.367 に答える
5

クラスまたは構造体は、メモリ内に一緒に保持する必要があるフィールドのコレクションを記述し、それらとそれらに対して動作するいくつかの操作の間に意味的な関係を持っています。単純なケースでは、メモリ内のクラス型オブジェクトのコンテンツには、それを構成するメンバー (およびいくつかのパディング) 以外に何もありません。メモリ内にオブジェクトがあるtestingPointers場合、それは実際にはint、 、floatおよびdoubleです。クラスの概念は、正しい実行可能コードを生成するためだけに使用されました。実行時には存在しません (少なくともこの目的では存在しません)。

オブジェクトがメモリ アドレスを共有できるかどうかに関する標準の重要な部分は、§1.8/6 です。

オブジェクトがビットフィールドまたはサイズがゼロの基本クラスのサブオブジェクトでない限り、そのオブジェクトのアドレスは、それが占有する最初のバイトのアドレスです。ビット フィールドではない 2 つのオブジェクトは、一方が他方のサブオブジェクトである場合、または少なくとも 1 つがサイズ 0 の基本クラス サブオブジェクトであり、それらが異なる型である場合、同じアドレスを持つことができます。それ以外の場合、それらは別個のアドレスを持つものとします。

このことから、 membertest.iは のサブオブジェクトであるためtest、同じアドレスを持つ可能性が高いと推測できます。

プログラムのすべてのオブジェクトを可能な限り詳しく調べると、実際には、スカラー値と隣接するビット フィールドの膨大なコレクションが得られます。これらは、標準ではメモリ位置として知られています。これらは実際にスペースを占有するものです。残りのオブジェクトはすべて何らかの形でこれらで構成されています。

メモリ位置は、スカラー型のオブジェクト、またはすべてがゼロ以外の幅を持つ隣接するビット フィールドの最大シーケンスのいずれかです。[注:参照や仮想関数など、言語のさまざまな機能には、プログラムからはアクセスできないが実装によって管理される追加のメモリ位置が含まれる場合があります。—エンドノート]

于 2012-11-20T23:47:54.127 に答える
3

混乱を明確にするために:

1). 最初のポインターの値 (test のメモリー位置) は、2 番目のポインター (test の最初のフィールド) の値と同じです。
構造体とその最初のフィールドのアドレスは、構造体が連続してそのフィールドの両方のコレクションではないため、同じになります。

ここに画像の説明を入力

理解をさらに簡単にするために、配列の場合を考慮することもできます。配列のアドレスは、明らかに配列の最初の要素 (フィールド) に等しくなります。

2).これは、オブジェクトに実際の一意のメモリ アドレスがなく、構造体またはクラスへのポインターが単に最初のフィールドを指していることを意味しますか?
オブジェクトが常にヒープに割り当てられるJavaと混同していると思います。C++ では、オブジェクトがヒープに割り当てられ、(スタック内の) ポインター変数がヒープ内のそのオブジェクトを指す動的メモリ割り当て ('new' 演算子を使用) でない限り、構造体/クラスは常にスタックに割り当てられます。したがって、前者の場合、構造体変数は常に最初の要素 (フィールド) と同じアドレスになります。

それが役立つことを願っています。

于 2012-11-21T00:38:57.090 に答える
1

メモリ内の構造体は、フィールドをつなぎ合わせただけで構成されます。アラインメントの必要性に基づいて、構造体の前および/またはフィールド間にパディングがある場合がありますが、通常、最初のフィールドの前に余分な「もの」はありません。したがって、最初のフィールドと構造体自体のアドレスは同じです。

C では、型はコンパイル時にのみ存在することに注意してください。

于 2012-11-20T23:48:41.013 に答える