5

ポインターについて読んでいたとき、突然、ポインターが変数のメモリアドレスを格納する変数に過ぎない場合、すべての整数がポインターとして機能するはずだと思いました。次に、小さなプログラムを作成しました。警告が表示されましたが、何とか機能しました。

int main()
{
    int i,j;
    i=3;
    j=&i;
    printf("%d\n%d\n%d",i,j,&i);
    return 0;
}

出力は

3
1606416600
1606416600

では、通常の int が機能するのに、なぜ * を追加するのでしょうか?

別の質問は、次のプログラムへの出力に関するものです

int main()
{
    int a[] = {1,2,3,4,5,6,7};
    int *i,*j;
    i=&a[1];
    j=&a[5];
    printf("%d\n%d\n%d",j,i,j-i);
    return 0;
}

出力:

1606416580
1606416564
4

ji = 4 で 16 ではないのはなぜですか?

4

7 に答える 7

16

なぜポインターに * を付ける必要があるのですか

言語仕様がそう言っているからです。

では、通常の int が機能するのに、なぜ * を追加するのでしょうか?

「普通」intではうまくいかないからです。「異常」もありませんint

ポインターは別の型です。人間の脳がそれらを「メモリ」と呼ばれる巨大なバイト配列へのインデックスとして簡単に想像できるのも不思議ではありませんが、それは必ずしもコンピューターやコンパイラーが行うことではありません。C 標準では、ポインターとの間の変換intは実装定義の操作であると規定されています。

ただし、組み込みの int 型またはを使用すると、データを失うことなくポインタを格納できます。intptr_tuintptr_tintunsigned int


2 番目の質問については、ポインタ演算がそのように定義されているためです。そのように定義されているのは、論理的で直感的だからです。の場合p2 = p1 + 4p2 - p116 ではなく 4 です。

ポインター演算の詳細については、この質問を参照してください。


ああ、技術的には、ポインターの出力は変換指定子を使用して行われるため、最初のプログラムの動作は未定義ですが、 which is forを使用しました。最初のプログラムは次のようになります。%p%dint

printf("%d\n%d\n%p", i, j, (void *)&i);

(キャスト to にも注意してください。これは、キャスト toが必要なvoid *数少ないケースの 1 つです。そうしないと、再び UB になります。)void *

于 2013-09-22T09:35:14.677 に答える
7

それは型安全性にかかっています。つまり、あるものを他の何かに使用してはならないときに使用することです。

http://en.wikipedia.org/wiki/Type_safetyを参照

于 2013-09-22T09:34:53.357 に答える
3

(@H2CO3 と @EdHeal の既に良い回答に追加します。)

アセンブリ レベルでは、アドレスを int として扱い、あらゆる種類の汚いトリックを実行できますが、C はアセンブリよりもはるかに高レベルの言語です。プログラミング言語のコンテキストで「高レベル」とはどういう意味ですか? 「ハイレベル オブ アブストラクション」の略で、人間の書き方や考え方により近い言語という意味です。

ある意味で、それはすべて「抽象化」に関するものです。たとえば、車を考えてみてください。安全に運転するためだけに、面倒なエンジニアリングの詳細をすべて知る必要はありません。車は、機械エンジニアが必要とするものと比較して、「はるかに高いレベルの抽象化」と見なします。なぜこれが役立つのですか?あなたの脳は、例えば、エンジンの各歯車が毎分何回転しなければならないかを考えることを余儀なくされるのではなく、自動車事故に巻き込まれることなくあなたを家まで運転することに集中する自由を持っているからです.

この比喩はプログラミング言語にも当てはまります。抽象化は、基礎となる実装のあらゆる詳細を考える労力を節約できるので便利です。ポインター抽象化です (ただし、より現代的な言語で見られるものと比較すると、非常に高いレベルの抽象化ではありません)。これは、何かへの間接参照の典型的なモデルです。内部的には、アドレス、ハンドル、またはまったく別のものとして実装されている可能性がありますが、そのセマンティクスは標準によって記述 (および義務付け) されています。したがって、特にプラットフォームやアーキテクチャを切り替えるときに、アセンブリ プログラマの悪夢である多くの問題から解放されます。ポインタは、移植可能なプログラムの作成にも役立ちます。

于 2013-09-22T09:52:30.400 に答える
0

ポインターと整数は、多くのアーキテクチャーでポインターが整数として実装されているにもかかわらず、2 つの異なるものであるため、異なる型を持ちます。ただし、たとえば x86_64 アーキテクチャを考えてみましょう。整数が 64 ビット幅でポインタが 32 ビット幅の実装があります。

于 2013-09-22T10:53:31.250 に答える
0

アドレス表現と型の「安全性」の問題とは別に、ポインター演算と代入には、(単一の一般的なポインター型ではなく) 特定のポインター型が必要です。(これらはあなたの例では使用されていません。)

ポインター演算:

int intArr[2] = {1, 2};
int* pInt0 = &intArr[0];     // points to intArr[0]
int* pInt1 = pInt0 + 1;      // points to intArr[1]

char* pChar0 = pInt0;       // points to the first  byte of intArr[0]
char* pChar1 = pChar0 + 1;  // points to the second byte of intArr[0]

(6.3.2.3/7 参照)

ポインターによる代入:

int obj = 42;
unsigned char buf[sizeof(obj)];
for(unsigned i = 0; i < sizeof(obj); ++i) {  // like memcpy
    unsigned char* source = i + (unsigned char*)&obj;
    unsigned char* dest = i + buf;
    *dest = *source;    // copies one byte
}

int obj2 = 0;
int* pObj2 = &obj2;

*pObj2 = obj;           // copies sizeof(int) bytes

(6.2.6.1/4 参照)

于 2013-09-22T12:08:46.930 に答える