C言語:エッセイ5ポインター1

ポインタは重要な概念ですが、まったく複雑ではありません。しかし、彼は確かにC言語の本質です。

正しく柔軟に使用すると、複雑なデータ構造を効果的に表現したり、メモリを動的に割り当てたり、文字列を便利に使用したり、配列を効果的かつ便利に使用したりできます。

ポインタとは何かを明確にするために、データがメモリにどのように格納され、どのように読み取られるかを理解する必要があります。

美しい物語から始めましょう。

たとえば、シャオAが「私は美人だ」という本を借りに図書館に行ったとき、彼は偶然に道でハンサムな男に出くわし、シャオAは勇敢に駆け寄って携帯電話を取り出しました。ハンサムな男が電話番号と住所を追加すると、ハンサムな男は、小さなAは本当に自宅の電話番号を教えていないように見えると思います。彼は自宅の住所を言っただけです。彼は独身のアパートの102号室に住んでいるので、小さなAはリトルAはすでに彼の家の住所を知っているので、彼のところに行くことができます。これは私たちのメモリにリンクされています。メモリには2つの属性があることがわかっています。最初のメモリは、2つの線形ストレージスペースです。属性の1つはアドレスであり、もう1つはアドレスに格納されているデータです。OK、メモリは私たちの住所と同じようにデータを保存するために使用されます。すべての家の番号には家があり、家には同じようにハンサムな男がいます。住所からインデックスを付けたい特定のメモリにインデックスを付けることができます。住所と家の番号がわからない場合は、どのように見つける必要があり、ドアを1つずつノックすることはできません。したがって、固定アドレスに対応するデータがある限り、メモリにはプログラムされたアドレスがあります。このアドレスに基づいてデータを保存してインデックスを作成すると、自分の物がどこに保存されているかがわかり、どこで物を入手できるかもわかります。

メモリ領域の各バイトには、「アドレス」である番号があります。プログラムで変数が定義されている場合、システムはプログラムのコンパイル時にこの変数にメモリユニットを自動的に割り当てます。これはローカル変数と呼ばれます。

C言語では、変数にアクセスするには、直接アクセスと間接アクセスの2つの方法があります。

例は次のとおりです。

Aドロワーを開くには、次の2つの方法があります。

(1)1つの方法は、Aキーを携帯し、必要に応じて、引き出しを開いて必要なものを取り出すためのキーを見つけることです。-----直接インタビュー

次のような直接アクセス:a = 5;

システムのコンパイル時に、変数にアドレスが割り当てられています。たとえば、変数aの割り当てられたアドレスが2000の場合、このステートメントの機能は、定数5をアドレス2000のユニットに保存することです。(メモリには2つの属性があると言いました。1つはアドレスで、次にアドレスに対応するスペースに格納されたデータ、次に2000のアドレスに格納されたデータは5であり、この変数はaと呼ばれます。)

(2)別の方法は、安全のために、Aキーを別の引き出しBに入れてロックすることです。ドロワーAを開く必要がある場合は、Bキーを見つけ、ドロワーBを開き、Aキーを取り出してから、ドロワーAを開き、ドロワーAの内容を取り出す必要があります。-----間接アクセス

次のような間接アクセス:scanf( "%d"、&a); //この関数の関数は整数変数を読み取り、その値をに割り当てます。

この関数を呼び出すとき、変数aのアドレスを関数scanfに渡します。この関数は、最初にアドレスをユニットに保存し、次にキーボードから受信したデータを保存されたアドレスを介して変数aに保存します。

1.ポインタを知る

C言語では、ポインタは特殊な変数であり、アドレスを格納します(通常、変数は値を格納します。たとえば、ちょうど今a = 5、変数aは5を格納しますが、ポインタは特殊な変数です。ポインタ変数と呼ばれるアドレスを格納します)。ポインタ変数を定義するとします。int* i_pointerは、整数変数iのアドレスを格納するために使用されます。ステートメントi_pointer =&i;を使用できます(iのアドレスを取得し、それをポインター変数i_pointerに割り当てます)

上の図に示すように、変数i、変数j、および変数kのアドレスが2000、2002、2004であり、そこに格納されているデータがそれぞれ3、6、および9であり、定義したポインタ変数が格納されていると仮定します。アドレス、ここではこの文i_pointer =&iを使用して変数iのアドレスをBに格納するため、iのアドレスはBに格納されます。ポインタ変数も変数であり、配列変数も変数です。ただし、変数と同じですが、ポインタ変数はアドレスを格納し、通常の変数は値を格納します。私たちは彼の柔軟なアプリケーションを使用するだけで非常にNBになります。

i(2000)のアドレスをi_pointに格納します。このとき、i_pointerの値は(2000)であり、これは変数iが占めるユニットの開始アドレスです。(異なるタイプがメモリ内の異なるバイトを占有するため)

変数iの値にアクセスするには、間接的な方法を使用できます。最初に「iのアドレス」を格納する変数i_pointerを見つけ、そこからiのアドレスを取得し(2000)、次にi3の値を取得します。下の図を参照してください。直接の場合は、i = 3です。取り出してください。

*:値演算子と呼ばれます。

&:アドレス取得演算子と呼ばれます;(後で、これら2つの操作の使用方法を詳細に分析します。現在、これは基本的な使用方法にすぎません)

int i=2000;//i的值为2000
int *pointer;//我们声明一个指针pointer//此处的*号并不是取值操作符,这个是申明它为指针的一个特征
pointer=&i;//在这里取出i的地址,比如说他的地址是1000,就把他的地址给了变量pointer//这个是取址
printf("%d\n",*pointer)//那我通过这个*号,取出指向这个地址的里边的值,也就是打印出来的值应该是2000//这个才是取值操作

変数のアドレスを知っていると、このアドレスを介して変数にアクセスできるため、変数のアドレスは変数の「ポインター」と呼ばれます。ポインタはアドレスを指します。ポインタ変数に関しては、C言語で特殊なタイプの変数が定義されています。これらの変数は、変数のアドレスを格納するために使用され、ポインタ変数と呼ばれます。(したがって、アドレスと変数は完全に異なるものです。)

注:ポインター変数の値(つまり、ポインター変数に格納されている値はアドレス(つまり、ポインター))は、「ポインター(アドレス)」と「ポインター変数(変数)」の2つの概念と区別する必要があります。

//定义一个指针变量
float *pointer_3;//pointer_3是指向float型变量的指针变量//pomiter_3是变量名而不是*pointer_3//*号只是起到表明它是指针
//*表示定义,*可以表示声明定义一个指针,如果不在声明的情况下,它是取值操作符

//次の割り当てステートメントを使用して、ポインタ変数に別の変数のアドレスを取得させ、変数を指すことができるようにすることができます。といった:

まず、pointer_1はiのアドレスを格納するため、変数iを指します。pointer_2はjのアドレスを格納するため、変数jを指します。次に、次のステートメントがある場合:

Pointer_1 = pointer_2; // pointer_1のコンテンツよりも変数pointer_2のコンテンツを保存します。したがって、この時点で、pointer_1に格納されているのは、iのアドレスではなく、jのアドレスです。このとき、pointer_1はjを指しています。もちろん、pointer_2はまだjを指しています。つまり、iは破棄され、両方ともjに直接移動します。

ポインタ変数の前の*は、変更のタイプがポインタ変数であることを示します。つまり、タイプ指定子*変数名。

ポインタ変数を定義するときは、基本タイプを指定する必要があります。特別な注意は次のとおりです。整数変数を指すポインタ変数に配置できるのは、整数変数のアドレスのみです。

ポインタ変数に格納できるのはアドレス(ポインタ)のみであることに注意してください。ポインタ変数に整数(またはその他の非アドレスタイプのデータ)を割り当てないでください。割り当てない場合、コンパイラは値をアドレスとして扱います。

&变量名;//地址运算符&来表示变量的地址。

2.機能パラメータとしてのポインタ

aとbの2つの整数を入力し、大きさの順に出力します

#include<stdio.h>
//交换a,b的值
void swap(int *p1,int *p2);//行参定义的指向整数型的指针变量

void main()
{
   int a,b;
   int *pointer_1, *pointer_2;
   scanf("%d %d",&a,&b);
   pointer_1=&a;//pointer_1该指针变量指向的是a。
   pointer_2=&b;
   if(a<b)
   {
       swap(pointer_1,pointer_2);//所以实参需要传入的是????指针呢?还是指针变量。pointer_1叫做指针变量
   }
   printf("\n%d > %d\n",a,b);    
}
void swap(int *p1,int *p2)
{
   int temp;
   printf("I'm swap....\n");
   temp=*p1;
   *p1=*p2;
   *p2=temp;
}

3つの整数a、b、cを入力し、大きさの順に出力します

#include<stdio.h>
//交换a,b的值
void exchange(int *q1,int *q2,int q3);//使得a>b>c。

void main()
{
   int a,b,c;
   int *p1, *p2,*p3;
   scanf("%d %d %d",&a,&b,&c);
   p1=&a;//p1指针指向a的地址
   p2=&b;
   p3=&c;
   exchange(p1,p2,p3);//与上边定义相比,是做了int *q1=p1;这个操作。(行参和实参的一个交换就相当于一个赋值操作,他们是通过栈来完成的)
   printf("%d %d %d\n",a,b,c);    
}
void exchange(int *q1,int *q2,int *q3)
{
   void swap(int *pt1,int *pt2);//用于交换
   if(*q1<*q2)//p1和q1都是指向a变量的//即如果a<b的话交换
   { 
      swap(q1,q2);
   }
   if(*q1<*q3)//a如果小于c的话c,交换
   { 
      swap(q1,q3);
   }
   if(*q2<*q3)//b如果小于c的话再交换
   { 
      swap(q2,q3);
   }

}
void swap(int *pt1,int *pt2)
{
   int temp;
   temp=*pt1;
   *pt1=*pt2;
   *pt2=temp;
}

3.配列とポインター

(配列とポインターはアドレスを指しているため、配列は比較的安定しており、ポインターはいつでも可変です。配列は定義時にスペースを占有し、ポインターは可変であるため、ポインタースペースはいつでも削除できます。アレイはできません)

変数にはアドレスがあり、配列にはいくつかの要素が含まれ、各配列要素はメモリ内のストレージユニットを占有し(そしてそれらは連続しています)、それらはすべて対応するアドレスを持っています。

ポインタ変数(変数のアドレスは内部に格納されています)は変数を指すことができます。もちろん、配列要素を指すこともできます(要素のアドレスをポインタ変数に入れます)

いわゆる配列要素へのポインタは、配列要素のアドレスです。

配列要素を指すポインタ変数を定義する方法は、前に紹介した変数を指すポインタ変数と同じです。

int a[10];//定义a为包含10个整型数据的数组
int *p;//定义p为指向整型变量的指针变量 
//应当注意,如果数组为int型,则指针变量的基类型也应为int型
p=&a[2];//指针指向数组的第三个元素;把a[2]元素的地址赋给指针变量p,也就是说把p指向a数组的第2号元素。
p++;//表示

配列要素を参照してください。添え字メソッドまたはポインタメソッドを使用できます。

(1)添え字法:a [i]の形式。

(2)ポインタ方式:*(a + 1)または*(p + i); // aは最初の要素のアドレスであるため、iを追加すると、アドレスにiを加えたものではなく、i番目の要素を指すことになります。 、タイプによって異なりますので。

ここで、aは配列の名前、pは配列要素を指すポインター変数であり、その初期値p = aはp =&a [0]です。

注:(コンパイラーでアドレスにコンパイルされた)配列の名前は、「コンパイルされた配列の最初の要素のアドレス」です(これは上記の類似点です)。

関数パラメーターとして配列名を使用する--------

といった:

void f(int arr[],int n)
{
    ......
}
void main()
{
   int arr[10];
   ... ...
   f(arr,10);//数组名相当于该数组的首地址
}

上記では、f(int arr []、int n)、arrはコンパイル時にポインタ変数として処理されます。これは、関数fの最初の部分をf(int * arr、int n)として書き込むのと同じであるため、上記の2つの書き込み方法同等です。

なお、C言語で関数を呼び出すときに仮想と実を組み合わせる方法では、値の転送を使用します。変数名を関数パラメーターとして使用すると、変数の値が渡されます。配列名を関数パラメーターとして使用する場合は、配列名が原因です。配列の最初の要素のアドレスを表すため、渡される値はアドレスであるため、仮パラメーターはポインター変数である必要があります。

例:n個の整数を逆の順序で配列aに格納します。

#include<stdio.h>
void reverse(int x[],int n)//形参x是数组名
void main()
{
   int i, a[10]={3,5,7,8,1,0,2,6,9,4};
   printf(" The original array:\n");
   for(i=0,i<10,i++)
   {
       printf("%d",a[i]);
   }
   printf("\n");
   reverse(a,10);
   printf("The Array has been inverted:\n");
}
void reverse(int x[],int n)
{
   int temp,i,j,m;
   m=(n-1)/2;
   for(i=0;i<=m;i++)
   {
      j=n-1-i;
      temp=x[i];
      x[i]=x[j];
      x[j]=temp;
   }
}

関数のパラメーターをポインターで変更します。

#include<stdio.h>
void reverse(int *x,int n)//形参x为指针变量
void main()
{
   int i, a[10]={3,5,7,8,1,0,2,6,9,4};
   printf(" The original array:\n");
   for(i=0,i<10,i++)
   {
       printf("%d",a[i]);
   }
   printf("\n");
   reverse(a,10);
   printf("The Array has been inverted:\n");
}
void reverse(int *x,int n)//形参x为指针变量
{
   int *p,temp,*i,*j,m;
   m=(n-1)/2;//中间序号
   i=x;//前边的序号//i指向数组的第一个元素
   j=x-1+n;//后边的序号//j指向数组的最后一个元素
   p=x+m;//指向中间配对
   for(;i<=p;i++,j--)
   {
      
      temp=*i;
      *i=*j;
      *j=temp;
   }
}

パラメータとしてポインタを使用する------

例:10個の数値から最大値と最小値を見つけます。

#include<stdio.h>
int max,min;//全局变量
void max_min_values(int array[],int n);
void main()
{
   int i,number[10];
   printf("Enter 10 integer numbers:\n");
   for(i=0;i<10;i++)
   {
       scanf("%d",&number[i]);
   }
   max_min_values(number,10);
   printf("\nmax=%d,min=%d\n,"max,min);
}
void max_min_values(int array[],int n)
{
   int *p,*array_end;
   array_end=array+n;
   max=min=*array;
   for(p=array+1;p<array_end;p++)
   {
      if(*p>max)
      {
          max=*p;
      }
      else if(*p<min)
      {
          min=*p;
      }
   }

}

概要:配列があり、関数(サブルーチン)でこの配列の要素の値を変更する場合、正式なパラメーターに実際に関与する対応する関係には4つのタイプがあります。

(1)正式なパラメーターと実際のパラメーターの両方で配列名が使用されます。

void main()
{
   int a[10];
   f(a,10)
}
void f(int [],int n)
{
   ......
}

(2)実際のパラメーターは配列名を使用し、正式なパラメーターはポインター変数を使用します。といった:

void main()
{
   int a[10];
   f(a,10);
}
void f(int *a,int n)
{
   ......
}

(3)実際のパラメーターと行パラメーターの両方がポインター変数を使用します。といった:

void main()
{
   int a[10];
   int *p=a;//指针变量p指向数组的首地址
   f(p,10);//传指针相当于传a数组的地址
}
void f(int *x,int n)//int *x指针相当于指向a的地址,相当于传值的时侯x=p,p指向a那么x也指向a
{
   ......
}

(4)実際のパラメーターはポインター変数であり、行パラメーターは配列名です。といった:

void main()
{
   int a[10];
   int *p=a;
   f(p,10);//1这里把数组的首地址当作实参传过去,
}
void f(int x[],int n)//2定义数组来接收首地址,说明位置起始点一样指向了同一组数组。
{
   ......
}

例:要素を降順で配列に配置します。

#include<stdio.h>
int max,min;//全局变量
void sort(int array[],int n);
void main()
{
   int *p,i,a[10]={3,7,4,9,2,0,5,8,1,6};
   printf("The origial array:\n");
   for(i=0;i<10;i++)
   {  
         printf("%d",a[i]);
   }
   printf("\n");
   p=a;
   sort(p,10);
   printf("The result is:\n");
   for(p=a;i=0;i<10;i++)
   {
       printf("%d",*p);
       p++;
   }
   printf("\n");
}
void sort(int x[],int n)
{
   int i,j,k,t;
   for(i=0;i<n-1;i++)
   {
      k=i;//假设第一个是最大的,如果接着的那个比他大那就把他调过来
      for(j=i+1;j<n;j++)
      {
         if(x[j]>x[k])
            {
                t=x[j];
                x[j]=x[k];
                x[k]=t;
            }
      }
   }

}

 

おすすめ

転載: blog.csdn.net/m0_37957160/article/details/108512339