1

私はCを学び始めています。

さて、私が理解していることから、ポインタは別の変数のアドレスを指し、直接それに近づかないことによってその変数値を変更するために使用されます。

今、私は3つの質問があります:

  1. それも正しいですか?もしそうなら、なぜ私はそれが必要なのですか?

  2. 他に用途はありますか?

  3. 次のコード行に違いはありますか?

    // 1
    int x = 10;
    int *ptr = &x;
    // 2
    int x = 10;
    int ptr = &x;
    
4

4 に答える 4

2

ポインタの値は、メモリ内の別のオブジェクトの場所です。したがって、最初の一連の宣言が与えられます。

int x = 10;
int *ptr = &x;

あなたはメモリに次のようなものを持っているでしょう(アドレスは薄い空気から引き出されており、実際のプラットフォームを表すことを意図していません):

アイテムアドレス0x000x010x02 0x03
---- ------- ---- ---- ---- ----
   x 0x08001000 0x00 0x00 0x00 0x0A
 ptr 0x08001004 0x08 0x00 0x10 0x00

x値が含まれます10ptrのアドレスが含まれていますx。したがって、 x*ptrは同等です。読み取りまたは書き込み*ptrは、への読み取りまたは書き込みと同じxです。

FWIW、それとの違い

int x = 10;
int ptr = &x;

これはptr整数へのポインタではなく整数として扱われるため、次のようなことを試みるとコンパイラはヤクします*ptr(単項のオペランドはポインタ型で * なければなりません)。

では、なぜポインタなのか?

Cのポインターは、次の3つの目的を果たします。

まず、ポインターを使用すると、参照渡しのセマンティクスを模倣できます。Cでは、すべての関数の引数は値で渡されます。つまり、関数の仮引数は、実際の引数とはメモリ内の異なるオブジェクトです。たとえば、次のswap関数を使用します。

void swap(int a, int b)
{
  int t = a;
  a = b;
  b = t;
}

int main(void)
{
  int x = 1, y = 2;
  swap(x, y);
  ...
}

メモリを見ると、次のようなものがあります。

アイテムアドレス0x000x010x02 0x03
---- ------- ---- ---- ---- ----
   x 0x08001000 0x00 0x00 0x00 0x01
   y 0x08001004 0x00 0x00 0x00 0x02
   a 0x08001010 0x00 0x00 0x00 0x01
   b 0x08001014 0x00 0x00 0x00 0x02

これは、またはに加えられた変更が反映されないaことを意味します。の呼び出し後、変更されません。 bxyswapxy

ただし、次のように、とにポインタを渡すと、のようになります。xyswap

void swap(int *a, int *b)
{
  int t = *a;
  *a = *b;
  *b = t;
}

int main(void)
{
  int x = 1, y = 2;
  swap(&x, &y);
  ...
}

すると、私たちの記憶は次のようになります。

アイテムアドレス0x000x010x02 0x03
---- ------- ---- ---- ---- ----
   x 0x08001000 0x00 0x00 0x00 0x01
   y 0x08001004 0x00 0x00 0x00 0x02
   a 0x08001010 0x08 0x00 0x10 0x00
   b 0x08001014 0x08 0x00 0x10 0x04

aおよびにはとのアドレスb含まれているため、からの読み取りと書き込みは、とからの読み取りと書き込みに相当します。したがって、この呼び出しの後、との値が変更されます。xy *a*bxyswapxy

ポインタを使用する2つ目の理由は、動的に割り当てられたメモリを追跡するためです。実行時に新しいオブジェクトを作成する場合は、mallocorcalloc呼び出しを使用します。これにより、メモリが割り当てられ、メモリへのポインタが返されます。

int *p = malloc(sizeof *p * N); // dynamically allocates enough memory
                                // to hold N integer values and saves
                                // the location to p

ポインタは、Cで動的に割り当てられたメモリを追跡 する唯一の方法です。

最後に、ポインターを使用すると、リスト、ツリー、キューなどの動的データ構造を作成できます。構造内の各要素が次の要素を明示的に指すという考え方です。たとえば、整数の単純なリストを定義するタイプは次のとおりです。

struct elem
{
  int data;
  struct elem *next;
};

タイプの各オブジェクトは、タイプstruct elemの次のアイテムを明示的に指しますstruct elem。これにより、必要に応じて(配列とは異なり)リストにアイテムを追加したり、リストからアイテムを削除したりできます。また、リストを作成するときにリストを並べ替えることができます(例を投稿しますが、疲れています。おそらく意味がありません;とにかくすぐにそれらに到達します)。

于 2012-09-16T14:12:44.033 に答える
1

ポインタは、指定されたタイプのメモリのアドレスを格納します。は、現在値10を格納している&x変数のアドレスです。Anはこの場所を保持できます。10(別名)を変更するには、xint* ptrx

*ptr = 3;

コードの2番目のブロックはptr、実際の番号であるかのようにアドレスを格納しています。これは、印刷しようとするptrと、それが半乱数(おそらく非常に高い)であることに気付くであろうことを意味します。

これらの基本的なデータ型がどのようにメモリに格納されるかを調査することをお勧めします。

用途としては、自分でローカルコピーを作成するのではなく、スコープ外のメモリを変更できる関数を作成できます。あなたが学び続けて機能に到達するならば、それはよりよく説明されるべきです。Cを学ぶために何を使っていますか?私はあなたが持っているものを捨ててK&Rを取得することを強くお勧めします。

于 2012-09-16T13:27:03.317 に答える
1

ポインタは通常、変数のアドレスを格納するために使用されます。ここで最初のケースで&xは、アドレスxを指定し、変数*ptrには、アドレス値がポイントされた場所に値ストアがありますptr。2番目のケースでは、ptrは変数xのアドレスを格納します。

#include<stdio.h>
#include<inttypes.h>
int main()
{
    int x = 10;
    int *ptr = &x;
    uintptr_t p = (uintptr_t)&x;
    printf("x=%d ,*ptr=%d,ptr=%p,p=%p",x,*ptr,ptr,p);
    return 0;
}

私のマシンでは、このコードは以下を生成します。

  x=10 ,*ptr=10,ptr=0028FF14,p=0028FF14
于 2012-09-16T13:29:46.657 に答える
1

ポインタはメモリアドレスです。

ただし、必ずしも単一の変数を指しているわけではありません。
次のことを想像してみてください。

int * x = malloc( 2 * sizeof( int ) );

ここに、2つの整数を含むことができるメモリ領域へのポインタがあります。
それで:

  • x[ 0 ]最初のintです
  • x[ 1 ]2番目のintです

間接参照するときは、その例で最初のものを割り当てます。

*x = 0; // same as x[ 0 ] = 0

3番目の質問については、確かに違いがあります。

int x    = 10;
int *ptr = &x; // As expected, ptr is now a pointer to x
int x    = 10;
int ptr  = &x; // Certainly not what you expect as ptr is not a pointer

2番目のケースでは、メモリアドレスを整数に割り当てます。これはポインターではありません。ポインターの長さがintと同じでない場合(たとえば、64ビットプラットフォームの場合)に問題が発生します。

算術演算ができるので、ポインタは便利です。

int * x  = malloc( 2 * sizeof( int ) );
int * x2 = x; // So we don't touch x, as we'll need to free it.

x2++; // Now x2 points to the second integer.

x2[ 0 ] = 0; // same as x[ 1 ] = 0;

インクリメントは、ポインタのタイプに基づいて行われます。これは通常、魔法が始まるところです。

ショートのサイズが2のプラットフォームを使用している場合、次のことを想像できます。

short * x  = malloc( 2 * sizeof( short ) );
char  * x2 = ( char * )x;

aのサイズcharが1であるため、以前の短いポインターの各バイトにアクセスできます(2バイトを割り当てたため4バイト)。

それで:

x2[ 0 ] -> First short / First byte
x2[ 1 ] -> First short / Second byte
x2[ 2 ] -> Second short / First byte
x2[ 3 ] -> Second short / Second byte

そして最後に、たとえば構造体を扱う場合にもポインタは優れており、それらを引数として渡す必要があります。
このような場合、ポインタを使用すると構造体アドレスが渡されます。そうしないと、呼び出し時に構造体をコピーする必要があり、効率がまったく悪くなります。

void foo( struct x p );   // Structure data will be copied
void bar( struct x * p ); // Only the address will be passed, no data copied
于 2012-09-16T13:32:33.677 に答える