-1

私はYouTubeでRichard Buckland Lectureを見ていました.彼はポインタの仕組みを示していました.

void main()
{
    int x=20,y=30;
    task(x,y)
    getch();
}
void task(int x,int y)
{}

task() は元の変数を認識しないため、元の変数から完全に独立した変数 x と y の in task() の 2 つのコピーが作成され、ポインタを使用して task() が直接アクセスできるようになります。 main() 変数 x と y のアドレス。

&x と言うと、x の値が配置されているアドレスを参照しますが、*x と言うと、それは *20 という意味で、アドレス 20 に配置されている値のようなものですよね? そして、C のポインターの概念に従えば、それは正しいです。

しかし、アドレス20にある値を返すのではなく、「無効な間接化」エラーが表示されます。

そのエラーを認識していないわけではありませんが、C コンパイラがそのエラーを表示するのはそのためです。私はまだポインターの概念に従っていますが、方法は異なります。したがって、概念的には正しいと思いますが、それでも構文的には間違っています。なんで?

4

4 に答える 4

2

※20ってアドレス20にある数値みたいなもんでしょ?

そうです、多かれ少なかれ、しかし

  • それは実際にはaddress で始まる20値です。コンパイラが値のサイズと式の型を知るためには、意味する値の型を知る必要があるため、適切なポインター型にキャストする必要があります。 20たとえば、(int*)20は へのポインタなintので、*((int*)20)型はintです。
  • address を含むメモリブロックが実際に割り当てられている可能性は低いため、このようなアドレスを安全に「作成」することはできません20
于 2012-09-21T17:23:50.830 に答える
1

理解しなければならないのは、C には C++ のような「参照渡し」の概念がないということです。つまり、コンパイラは、変数の型に関係なく、渡された変数のコピーを作成します。コピーが保存される場所は、コンパイラ、アーキテクチャ、最適化レベルなどによって異なります。一般に、どこに保存されたかを気にする必要も、知る必要もありません。本当に重要なのは、関数内でコピーを取得することです。

理論的には、次のようなことを行うことで、メモリの任意のアドレスにアクセスできます。

int *var = ((int*) 0xdeadbeef);
*var = 3;

意味: 値0xdeadbeefを整数ポインターにキャストし、.csv に保存しvarます。次に、値 3 を から始まるブロック (x86 では 4 バイト長) に保存します0xdeadbeef。これは正しいCですが、今日のオペレーティングシステムはメモリコントローラーを使用して限られたアドレス/ブロックのセットにアクセスできるため、セグメンテーション違反が発生することは間違いありません.

これを行う場合:

int var = 10;

コンパイラは値 10 をどこかに格納します (レジスタに格納される場合もありますが、コンパイラが RAM に格納すると仮定します)。C プログラマーは、通常、それがどこにあるかは気にしません。リンカは正しいアドレスを処理します。

このコードを見てみましょう

void foo(int f)
{
   /* do something with f */
}

void bar(int g)
{
    int f1 = 2;
    f1 += g;
    foo(f);
}

コンパイラはそれをどのようにアセンブラに変換しますか? このように見えるかもしれません

foo:
0xa0000000: load in register 1 the 4-byte value @ 0x12abcd00
0xa0000004: /* do something with register 1 */
0xa0000008: /* do something else with register 1 */
...
bar:
0xa0000c00: load in register 3 the 4-byte value @ 0x12ad0004
0xa0000c04: load in register 4 the value 2
0xa0000c08: add register 3, register 4 (result saved in 'result register')
0xa0000c0b: save the content of 'result register' @ 0x12abcd00 (look at the first line)
0xa0000c10: jump 0xa0000000  (call foo)

同じことがポインターにも当てはまります。ポインターは多かれ少なかれ整数変数です。ポインタによって格納される値はアドレスです。整数との違いは、そのアドレスに格納されている値にアクセス (= 逆参照) できることです。*演算子を使用してこれを行います(最初の例を参照*var = 3)。&演算子は、変数のアドレスを返します。

だからあなたが持っているとき

void foo(int *x)
{
    *x = 3;
}

void bar(void)
{
    int i = 9;
    int *i_ptr = &i;
    foo(i_ptr);
    /* i is 3 */
}

C は のコピーを作成しますが、コピーi_ptrの格納された値がi_ptrのアドレスと同じである場合は、で定義されていなくてもi、それfooを逆参照して の値を変更できます。iifoo

では、C ではいつポインターを使用するのでしょうか。一般に、ポインターを使用する場合の最も一般的なケースは次のとおりです。

  • 関数で宣言していない変数を変更したい
    • たとえば、一度に複数の値を返す必要がある
  • 動的に割り当てられたメモリを使用します ( mallocrealloc、を参照calloc) 。
  • 私たちは文字列を扱います
于 2012-09-21T23:34:14.597 に答える
1

私はあなたの言いたいことがよくわかりません (あなたの英語はサポートできないほど貧弱です!) が、私がそれを正しく理解していれば、次のように整数として指定された定数アドレスを逆参照する必要があります。

int someVar = *20;

現在、 の型20は int であり、ポインター型ではないため、逆参照できません。そのためには、適切なタイプのポインターに変換する必要があります。

int someVar = *(int *)20;

はい、これはおそらくセグメンテーション違反になります。

于 2012-09-21T17:22:21.267 に答える
0

あなたが言おうとしているのは、次のことだと思います。

void task(int *px, int *py)
{
    /* get the addresses of two integers */

    *px += 10; /* change the value of the integer px is pointing to */
    *py += 20; /* change the value of the integer py is pointing to */
}

void main()
{
    /* make a space on the stack for two integers, assign values */
    int x=20;
    int y=30; 

    /* display those values */
    printf("x=%d, y=%d", x, y);

    /* pass the address of each value to a function */
    task(&x,&y);

    /* display the changed values*/
    printf("x=%d, y=%d", x, y);

    getch();
}

これは x と y のアドレスを渡すため、関数 task() はそれらの値を変更できます。

于 2012-09-21T17:23:59.037 に答える