C言語ポインタの詳しい解説(1)超詳しい~

ここに画像の説明を挿入します


https://img-blog.csdnimg.cn/6a78491801a74bbd8d932659b06e11db.gif#pic_center

1. メモリとアドレス

1.1 メモリ

メモリとアドレスについて話す前に、実際のケースを考えてみましょう。

寮の建物があり、あなたはその建物に入れられたとします。2 階には 100 の部屋がありますが、部屋には番号が付いていません。友達の 1 人があなたと遊びに来ます。彼があなたを見つけたければ、それぞれの部屋に行かなければなりません。これは非常に非効率ですが、フロアとそのフロアにある部屋に従って各部屋に番号を付けるとします。

一楼:101102103...
二楼:201202203....
...

部屋番号を使用すると、友人が部屋番号を取得すると、すぐに部屋を見つけてあなたを見つけることができます。

生活では、各部屋に部屋番号が付いているため、効率が向上し、すぐに部屋を見つけることができます。

上記の例を計算に置き換えるとどうなるでしょうか?

CPU (中央処理装置) がデータを処理するとき、必要なデータがメモリから読み取られ、処理されたデータもメモリに戻されることがわかっています。コンピュータを購入するとき、コンピュータのメモリは 8GB です。 16GB/32GB など。これらのメモリ空間を効率的に管理するにはどうすればよいでしょうか?

実際には、メモリはメモリ ユニットに分割されており、各メモリ ユニットのサイズは 1 バイトです。

コンピュータの一般的な単位(補足):

ビットには 2 進数のビット 1 または 0 を格納できます。

1 bit - 比特位
2 byte - 字节
3 KB
4 MB
5 GB
6 TB
7 PB
1byte = 8bit
1KB = 1024byte
1MB = 1024KB
1GB = 1024MB
1TB = 1024GB
1PB = 1024TB

このうち、各メモリ単位は学生寮に相当し、1 人がバイトに 、クラスメイトが住む 8 人部屋のように、一人一人がビットです。

また、各メモリユニットには番号が付いており(この番号は寮の部屋の番地に相当します)、このメモリユニットの番号を基に、CPU はすぐにメモリスペースを見つけることができます。

日常生活では、家の番号をアドレスとも呼びます。コンピュータでは、メモリ ユニットの番号をアドレスとも呼びます。 C 言語では、アドレスポインタ という新しい名前が付けられました。

したがって、次のように理解できます。

メモリユニット番号住所ポインタ

1.2 アドレッシングの理解方法

CPU がメモリ内の特定のバイト空間にアクセスするとき、CPU はそのバイト空間がメモリ内のどこにあるかを知る必要があります。メモリ内には多くのバイトがあるため、メモリをアドレス指定する必要があります (多数のドミトリーがあり、ドミトリーが必要とするのと同じです)。番号が付けられます)。

コンピュータのアドレス指定は各バイトのアドレスを記録するのではなく、ハードウェア設計によって行われます。

ピアノやギターには「ドラミがソラセドを送る」というメッセージは書かれていませんが、演奏者は各弦のすべての位置を正確に見つけることができます。それはメーカーが楽器のハードウェアレベルで設計しており、すべてのプレイヤーがそれを知っているからです。本質は合意された合意です!

ハードウェアアドレス指定についても同様です

詳細については、以下の図を参照してください。

まず、コンピュータには多数のハードウェア ユニットがあり、それらのハードウェア ユニットが相互に連携して動作することを理解する必要があります。いわゆるコラボレーションでは、少なくとも相互にデータを転送できなければなりません。

しかし、ハードウェアとハ​​ードウェアは互いに独立しているため、どのように通信するのでしょうか?答えは簡単、「線」で結ぶだけです。

また、CPU とメモリの間では大量のデータのやり取りが行われるため、両者を線で接続する必要があります。

しかし、今日私たちが懸念しているのは、アドレス バスと呼ばれる一連の回線です。

32 ビット マシンには 32 のアドレス バスがあり、各ラインには 0 または 1、[電気パルスの有無] を示す 2 つの状態しかなく、1 つのラインで 2 つの意味を表すことができ、2 つのラインでmeans 4 つの意味を表現できます。 32 のアドレス行は 2^32 の意味を表すことができ、それぞれの意味がアドレスを表します。

アドレス情報はメモリに送られ、メモリ内でそのアドレスに対応するデータが検索され、対応するデータを経由してデータバスを介してCPU内部レジスタに転送されます。

2. ポインタ変数とアドレス

2.1 アドレス取得演算子 (&)

メモリとアドレスの関係を理解し​​た後、C 言語に戻りましょう。C 言語で変数を作成すると、実際には次のようなメモリ領域が適用されます。

#include <stdio.h>
int main()
{
    
    
    int a = 10;
    return 0;
}

たとえば、上記のコードは整数変数 a を作成し、整数 10 を格納するためにメモリに 4 バイトを割り当てます。各バイトにはアドレスがあります。上の図の 4 バイトのアドレスは次のとおりです。

1.0x006FFD6C  
2.0x006FFD6D 
3.0x006FFD6E  
4.0x006FFD6F 

では、a のアドレスを取得するにはどうすればよいでしょうか?

ここでは、演算子 (&) アドレス演算子を学習する必要があります。

#include <stdio.h>
int main()
{
    
    
    int a = 10;
    &a;//取出a的地址
    printf("%p\n", &a);
    return 0;
}

この描画例に従って、次のように出力されます: 006FFD6C

&a は、a が占める 4 バイトのうち小さい方のバイトのアドレスを取り出します。

整数変数は 4 バイトを占めますが、最初のバイト アドレスがわかっていれば、手がかりをたどって 4 バイトのデータにアクセスすることも可能です。の

2.2 ポインタ変数逆参照演算子(*)

2.2.1 ポインタ変数

アドレス演算子 (&) を通じて取得するアドレスは、0x006FFD6C のような数値です。場合によっては、この値も後で使用するために保存する必要があるため、そのようなアドレス値を保存します。それはどこですか?答えは、ポインタ変数です。

例えば:

#include <stdio.h>
int main()
{
    
    
    int a = 10;
    int* pa = &a;//取出a的地址并存储到指针变量pa中
    return 0;
}

ポインタ変数も変数の一種で、アドレスを格納するために使用され、ポインタ変数に格納された値がアドレスとして認識されます。

2.2.2 ポインタ型を逆アセンブルする方法

pa の型は int* であることがわかりましたが、ポインタの型をどのように理解すればよいでしょうか?

int a = 10;
int * pa = &a;

ここで、pa の左側に書かれている **int*,* は pa がポインタ変数であることを示し、その前の int は pa が整数型のオブジェクトを指していることを示しています。

詳細な説明については、下の図を参照してください。:

では、char型の変数chがあった場合、chのアドレスはどのような型のポインタ変数に入れればよいのでしょうか?

char ch = 'w';
pc = &ch;//pc 的类型怎么写呢?

よく考えてみると、次のようなコードも書くことができます。

char ch='w';
char *pc=&ch

先頭の * は、pc がポインタであることを意味します。、先頭の char は、pc が char 型である ch 変数を指すことを意味します。< a i=2> 。

同様に、他のタイプの変数のアドレスをポインタ変数に入れたい場合は、上記の操作に従うだけです

ポインタ変数はアドレスの値を格納するために使用され、ポインタ変数に格納された値がアドレスとして使用されることがわかります。

2.2.3 逆参照演算子

アドレスを保存して将来使用するので、どのように使用しますか?

実生活では、私たちは住所を使用して、物品を取り出したり保管したりできる部屋を見つけます。

これはC言語でも実は同じで、アドレス(ポインタ)さえ取得できれば、そのアドレス(ポインタ)が指すオブジェクトを渡すことができるので、ここで逆参照演算子 (*)
ここに画像の説明を挿入します

1 #include<stdio.h>
2 int main()
3 {
    
    
4     int a = 100;
5     int* pa = &a;
6     *pa = 0;
7     return 0;
8 }

上記のコードの 6 行目では、逆参照演算子を使用しています。*pa は、pa に格納されているアドレスを検索することを意味します。スペースが指定されています。 to、pa、は実際には変数 a です。 **したがって、pa=0、この演算子は変数 a を 0 に変更します。

ここでの目的が a を 0 に変更することであるなら、a=0 と記述するだけで十分ではないかと考えている人もいるはずです。 ?なぜポインタを使用する必要があるのでしょうか?

実際には、a の変更を pa 操作に引き継ぐことで、a を変更する方法が 1 つ増え、コードの柔軟性が向上します。

2.3 ポインタ変数のサイズ

前回のコンテンツでは、32 ビット マシンは 32 個のアドレス バスを想定していることを学びました。各アドレス ラインから出力される電気信号は、1 または 0 のデジタル信号に変換されます。次に、32 個のアドレス バスによって生成されるバイナリ シーケンスについて考えます。アドレス行をアドレスとして使用する場合、アドレスは 32 ビットであり、格納するのに 4 バイトが必要です。

ポインター変数を使用してアドレスを格納する場合、ポインターのサイズには 4 バイトのスペースが必要です。

同様に、64 ビット マシンの場合、アドレス ラインが 64 あると仮定すると、アドレスは 64 バイナリ ビットで構成されるバイナリ シーケンスであり、格納するには 8 バイトのスペースが必要で、ポインタのサイズは 8 バイトです。

詳細については、次のコードをご覧ください:

#include <stdio.h>
//指针变量的大小取决于地址的大小
//32位平台下地址是32个bit位(即4个字节)
//64位平台下地址是64个bit位(即8个字节)
int main()
{
    
    
    printf("%zd\n", sizeof(char *));
    printf("%zd\n", sizeof(short *));
    printf("%zd\n", sizeof(int *));
    printf("%zd\n", sizeof(double *));
    return 0;
}

32 ビットと 64 ビットのポインター変数サイズそれぞれの実行結果を見てみましょう。

结论

  • 32 ビット プラットフォームでは、アドレスは 32 ビットで、ポインタ変数のサイズは 4 バイトです。
  • 64 ビット プラットフォームでは、アドレスは 64 ビットで、ポインター変数のサイズは 8 バイトです。
  • ポインタ型の変数が同じプラットフォーム上で同じサイズである限り、ポインタ変数のサイズと型は相互に関係がないことに注意してください。

3. ポインタ変数の種類と意味

事前にわかっているポインタ変数のサイズはその型とは関係がありません。ポインタ変数である限り、同じプラットフォーム上ではサイズは同じです。さまざまな種類のポインターがあるのはなぜですか? 型についてはどうですか?

実際、ポインタ型には特別な意味があります。次に学習を続けます。

3.1 ポインタの逆参照

これに関して、次の 2 つのコードでは、主にデバッグ中のメモリの変更を観察します。

//代码1
#include <stdio.h>
int main()
{
    
    
    int n = 0x11223344;
    int *pi = &n;
    *pi = 0;
    return 0;
}

ここに画像の説明を挿入します

//代码2
#include <stdio.h>
int main()
{
    
    
    int n = 0x11223344;
    char *pc = (char *)&n;
    *pc = 0;
    return 0;
}

ここに画像の説明を挿入します
デバッグを通じて、コード 1 は n の 4 バイトすべてを 0 に変更しますが、コード 2 は n の最初の 2 バイトのみを 0 に変更することがわかります< i=2>。

したがって、次のような結論を導き出すことができます。
ポインタの型によって、ポインタを参照解除するときにどの程度の権限があるかが決まります(1 回の操作)数バイト)。
例: char スターのポインタ逆参照は 1 バイトのみにアクセスでき、short スターのポインタ逆参照は最初の 2 バイトのみにアクセスできます。 int のポインタの逆参照は 4 バイトにアクセスできます。
![](https://img-blog.csdnimg.cn/img_convert/e20d86e2fbdf471b0b21bd6f657c3ed8.png

3.2 ポインタ±整数

まずコードの一部を見て、デバッグしてアドレスの変更を観察しましょう~

#include <stdio.h>
int main()
{
    
    
	int n = 10;
	char* pc = (char*)&n;
	int* pi = &n;
	printf("%p\n", &n);
	printf("%p\n", pc);
	printf("%p\n", pc + 1);
	printf("%p\n", pi);
	printf("%p\n", pi + 1);
	return 0;
}

コードを実行した結果は次のようになります。

char star 型のポインタ変数 +1 は 1 バイトをスキップし、int* 型のポインタ変数 +1 は 4 バイトをスキップすることがわかります。

これはポインタ変数の型によってもたらされる変化であることがわかります。

結論: ポインターの種類によって、ポインターが前後に移動する距離 (距離) が決まります。ポインタのステップ サイズに相当します。

4.const 変更されたポインタ

4.1 const 変更変数

変数が変更できることは誰もが知っています。変数のアドレスがポインタ変数に与えられている場合、その変数はポインタ変数を通じて変更することもできます。ただし、変数にいくつかの制限を設け、変更することはできません。どうすればよいですか?このとき、C 言語キーワード const を使用する必要があります!

まず、次のコード行を見てみましょう。

#include <stdio.h>
int main()
{
    
    
	const int m = 100;
	m = 20;//m是可以修改的吗?
	printf("%d\n", m);
	return 0;
}

それで、このコードは実行できるでしょうか? vs~で実行しましょう

画像

この図から、VS はすでに 413 行目でエラー メッセージを報告していることがわかります。そのため、上記のコードの m は変更できないと考えられます。実際、m は本質的には変数ですが、const によって変更された後です。構文の制限が追加され、コード内で m を変更する限り、文法規則に準拠せずにエラーが報告され、m を直接変更することはできなくなります。

しかし、m をバイパスして m のアドレスを使用する場合は、m を変更することで実行できますが、そうすることは文法規則に違反します。

たとえば、次のコード行を見てみましょう。

#include <stdio.h>
int main()
{
    
    
    const int m = 0;
    printf("m = %d\n", m);
    int*p = &m;
    *p = 20;
    printf("m = %d\n", m);
    return 0;
}

出力結果:

ここから、これは確かに変更されていることがわかりますが、よく考えてみると、なぜ m を const で変更する必要があるのでしょうか。 m を変更できないようにするためですが、p が m のアドレスを介して m を変更できるとすると、const の制限が破られてしまうため、p は m のアドレスを取得しても m を変更できないはずですが、次にどうやって行うか?

4.2 const 変更後のポインタ変数

次のコードを見て分析してみましょう~

#include <stdio.h>
//代码1
void test1()
{
    
    
	int n = 10;
	int m = 20;
    int *p = &n;
    *p = 20;//ok?
    p = &m; //ok?
}
void test2()
{
    
    
    //代码2
    int n = 10;
    int m = 20;
    const int* p = &n;
    *p = 20;//ok?
    p = &m; //ok?
}
void test3()
{
    
    
    int n = 10;
    int m = 20;
    int *const p = &n;
    *p = 20; //ok?
    p = &m; //ok?
}
void test4()
{
    
    
    int n = 10;
    int m = 20;
    int const * const p = &n;
    *p = 20; //ok?
    p = &m; //ok?
}
int main()
{
    
    
    //测试无const修饰的情况
    test1();
    //测试const放在*的左边情况
    test2();
    //测试const放在*的右边情况
    test3();
    //测试*的左右两边都有const
    test4();
    return 0;
}

まず * の左側に const を置いた場合、どのようなエラーが発生するでしょうか。

vs の 425 行目のエラー メッセージから、const が int の左側に配置された場合、変更は *p であり、p を使用して逆参照操作を実行できないという制限があることがわかります。 は n 自体の内容を変更し、p 自体も変更できます

また、 * の右側に const を置くと、どのようなエラーが発生しますか?

vs の 426 行目のエラーメッセージから、const を * の右側に置くとポインタ変数自体が変更されることがわかります。つまり、ポインタ変数 p の内容は変更できませんが、その内容は変更されます。 to by p は変更できます。

では、 * の左右に const を置いた場合、どのようなエラーが発生するのでしょうか?

vs の 425 行目と 426 行目のエラー メッセージから、* の左側と右側を const で変更すると、ポインター変数 p 自体と p が指す内容は変更できないことがわかります。これは次と同等です。死に追いやられている、死んでいる。

したがって、次のような結論を導き出すことができます。

const がポインタ変数を変更する場合

  • Const を * の左側に配置すると、ポインターが指す内容を変更し、ポインターが指す内容をポインターを介して変更できないようにします。ただし、ポインタ自体の内容は変更可能です。
  • Const を * の右側に配置すると、ポインター変数自体が変更され、ポインター変数の内容は変更できなくなりますが、ポインターが指す内容はポインターを介して変更できます。

5. ポインタ演算

ポインタには次の 3 つの基本的な操作があります。

  • ポインタ±整数
  • ポインタ - ポインタ
  • ポインタの関係演算

5.1 ポインタ±整数

通常、配列内の要素を出力する場合、次の図に示すように、その添え字にアクセスすることで出力できることは誰もが知っています。

では、ポインターを使用して配列内の要素を出力したい場合は、どうすればよいでしょうか?

このように考えることができるのは、配列はメモリ内に継続的に格納されており、最初の要素のアドレスがわかっていれば、後続のすべての要素を簡単に見つけることができるからです。

1 int arr[10]={
    
    1,2,3,4,5,6,7,8,9,10}

具体的なコードの実装は次のとおりです。

#include <stdio.h>
//指针+- 整数
int main()
{
    
    
    int arr[10] = {
    
    1,2,3,4,5,6,7,8,9,10};
    int *p = &arr[0];
    int i = 0;
    int sz = sizeof(arr)/sizeof(arr[0]);
    for(i=0; i<sz; i++)
    {
    
    
    	printf("%d ", *(p+i));//p+i 这里就是指针+整数
    }
    return 0;
}

上記のコード行を分析してみましょう。まず、このコード行int *p=&arr[0]、まず、配列の最初の要素のアドレスを整数ポインタ p に入れます。

そして、for ループの *(p+i) は何を意味するのでしょうか?

デバッグを通じて、配列内の各要素が整数であり、連続的に格納され、4 バイトを占有することがわかります。

たとえば、ポインタ変数 int *p が最初の要素アドレス 0x008FFAF4 を取得するとします。 前に述べたように、&arr[0] は arr[0 を取り出します] ] 占有される 4 バイトのうち小さい方のバイトのアドレス。

そのアドレスに 1 を追加するとどうなるでしょうか?解析すると、この P は int* 型のポインタなので 1 を加算すると 4 バイトに直接ジャンプし、整数ポインタなので 1 を加算すると 4 バイトに直接ジャンプすることがわかります。整数要素は 4 バイトをスキップすることを意味します。 上の図から、ここで p+i が &arr[i] 要素のアドレスも取得していることがわかります。

次の式を推測できます: p+n=n*sizeof(int)

p は整数ポインタであるため、p+n は n*sizeof( int) バイトをスキップします。 。

しかし、このアドレスから探したい要素が見つかったらどうなるでしょうか。 (p+i) に対して前の dereference* 操作を実行すると、アドレスが指す対応する要素の値を取得できます。

実行結果は次のとおりです。

もちろん、この for ループでは、i を 10 回ループし、*p を逆参照してから p++ を実行することもできます。p++ の本質は p=p+1 です。
実際には、上記のコードと同じです。P と整数要素を p に代入し、逆参照操作を通じて、アドレスが指す要素の値を見つけます。このようにして 10 回ループし、配列の要素を出力することもできます。

具体的なコードと実行例を次の図に示します。
画像の説明を追加してください

同様に、文字ポインターの別の例を示します。具体的には、次のコードを見てください。

#include <stdio.h>
int main(){
    
    

	char str[]="abcdef";
	char *pc=&str[0];
	while(*pc!='\0'){
    
    
		printf("%c",*pc);
		pc++;
	}

	return 0;
}

たとえば、ここでは str の文字配列を定義しました。str 配列には、abcdef と \0 が格納されます。同様に、最初に str[0] を置きます。 \0 が文字列の終了マークであるため、要素のアドレスがスター PC に与えられます。

次に、ループ条件を *pc として記述できます。 ='0' の場合、\0 の前にあるすべての abcdef 文字を出力できます。各サイクルがそれを逆参照し、対応する要素を見つけて出力し、その後、pc++ で char 型を PC に追加します。ポインタは PC に割り当てられます。文字 f が出力され、その後、pc++ が文字 \0 を指しますが、\0 が ='\0' であり、明らかにループ条件を満たさない場合は、while ループから抜け出します。

5.2 ポインタ-ポインタ

まず次のコード行を見て、結果を見てみましょう。

#include <stdio.h>
int main(){
    
    

	int arr[10]={
    
    0};
	int ret=&arr[9]-&arr[0];
	printf("%d\n",ret);
	return 0;
}

結果を見てみましょう~
ここに画像の説明を挿入します

次に461 行のコードを int ret=&arr[0]-&arr[9]; に書き換えると、何が起こるでしょうか?実行して試してみましょう。

画像

結果は -9 なので、次の結論を導き出すことができます。

ポインタ-ポインタ(アドレス-アドレス)で得られる値の絶対値が、ポインタとポインタの間の要素数となります。

前提条件があります。両方のポインタが同じ空間を指している

なぜそんなことを言うのですか?

見てみましょう。 char 型の配列 ch を定義し、&arr[9]-&ch[5] の値を割り当てたとします。 ] を ret に変換し、2 つの配列間の要素の数を計算させます。

次のコード行を見てみましょう。

#include <stdio.h>
int main(){
    
    

	int arr[10]={
    
    0};
	char ch[7]={
    
    0};
	int ret=&arr[9]-&ch[5];
	printf("%d\n",ret);
	return 0;
}

まず、arr[9] と ch[5] のアドレスがどこにあるのかを図で描いてみましょう。

この 2 つの配列のアドレスはわかりますが、その間の要素数が不明です。次に、2 つの配列の要素数は int 型で計算されるのか、char 型で計算されるのか、数えてみてください。これは単なるナンセンスではないでしょうか?したがって、これら 2 つの計算は無意味です。

この概念を理解した後、ポインターはどのように使用されるのでしょうか?では、別の例を挙げてみましょう?

まず、次のコード行を見てみましょう~

#include <stdio.h>
#include <string.h>

int main(){
    
    
	char arr[]="abcdef";
	int lens=strlen(arr);
	printf("%d\n",lens);
	return 0;
}

上記のコード行から次のことがわかります。
strlen は文字列の長さを計算します。ここで、 strlen は arr 配列の長さを計算します。 arr 配列には文字列が格納されており、 \0 は文字列の終了マークであるため、 strlen は \0 より前の文字をカウントし、計算結果は 6 になります。

公式関数 Web サイトで strlen 関数の紹介もご覧いただけます~

ご興味がございましたら、Web サイトをクリックして使い方を確認してください。関数:
C/C++ 関数公式 Web サイト

では、ポインターを使用してこの strlen 関数をシミュレートおよび実装したい場合は、どうすればよいでしょうか?
このコードは次のように記述できます:

#include <stdio.h>
#include <string.h>

int my_strlen(char* str) {
    
    
	int count = 0;
	while (*str != '\0') {
    
    
		count++;
		str++;
	}
	return count;
}

int main(){
    
    
	char arr[] = "abcdef";
	int lens =my_strlen(arr);
	printf("%d\n", lens);
	return 0;
}

まず、次のコード行を見てみましょう

int lens =my_strlen(arr);

このコードの本質は、文字 a を my_strlen 関数に渡すことです。なぜそう言えるのでしょうか?下の図を見てみましょう:
ここに画像の説明を挿入します
この図から、次のことがわかります: 配列名は配列の最初の要素のアドレスです。したがって、my_strlen 関数のパラメーター部分では、char*str 形式のポインターを使用してそれを受け取ることができるため、最初の要素 a のアドレスを渡すことができます。

a のアドレスを str に渡すとき、変数 count を定義し、while ループに条件 *str!='0’ を追加できます。ただし、スター str は常に「a」のアドレスを指すとは限りません。
したがって、str が '\0' を指すまで、つまり '\0' に等しくなるまで、ループ本体にステートメント str++ を追加する必要があります。その後、ループから抜け出します。 while ループ、then to When return count は count の数を返すことと同じです。

このコードを実行した結果は次のとおりです。
画像の説明を追加してください
もちろん、このコードはまだ修正する必要があります。たとえば、次のように変更する必要があります。

#include <stdio.h>
#include <string.h>

int my_strlen(char* str) {
    
    
	char* pc =str;
	while (*str != 0)
		str++;
	return str - pc;
}

int main() {
    
    
	char arr[] = "abcdef";
	int lens =my_strlen(arr);
	printf("%d\n", lens);
	return 0;
}

上記の関数 my_strlen 部分では、まず char*str を使用して a の要素を受け取り、次に関数本体で char star pc を使用して str 要素 a を開始点マークとして受け取ります。 while ループでは、str!='\0' である限り、str が '\0' を指すまで 1 を加算し続け、ループ本体から抜け出します。
また、2 つのポインタ変数 pc と str は char star 型なので、それらを減算することで中区間要素の数が得られます。

以下はアニメーションのデモです。ご覧ください~
ここに画像の説明を挿入します
コードを実行した結果は次のとおりです。
ここに画像の説明を挿入します

ここに画像の説明を挿入します

5.3 ポインタの関係演算

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

//指针的关系运算
#include <stdio.h>
int main()
{
    
    
    int arr[10] = {
    
    1,2,3,4,5,6,7,8,9,10};
    int *p = &arr[0];
    int i = 0;
    int sz = sizeof(arr)/sizeof(arr[0]);
    while(p<arr+sz) //指针的大小比较
    {
    
    
        printf("%d ", *p);
        p++;
    }
    return 0;
}

このコードから、最初に *p を使用して配列の最初の要素 &arr[0] のアドレスを取得し、次にこのコード行を使用していることがわかります。 sizeof(arr)/sizof( arr[0] ) 配列内の要素の数を計算し、その要素の数を変数 sz に割り当てます。次に、while ループ条件で、 条件を (p
なぜこんなことを書くのかと疑問に思う人もいるかもしれません。 ) として記述します。 arr+sz) 。

実際に、以下の図を見てみましょう。

この図から、arr は配列名であり、配列の最初の要素のアドレスであるため、ポインタ変数 p と arr 配列の両方がそのアドレスを指していることがわかります。配列の最初の要素のインデックスは 0、配列の最後の要素のインデックスは 9 であるため、配列の最初の要素のインデックスは sz、つまり 10 になります。
したがって、そのアドレスは開始位置のアドレス + 10 に相当し、10 要素をスキップすることでそのアドレスをポイントできます。 配列名は配列の最初の要素のアドレスであるため。したがって、ループ条件を while(p<arr+sz) として記述し、ループ本体で、まず p のアドレスを逆参照し、そのアドレスが指すオブジェクトを見つけてから、p++ を実行します。なぜなら、p 自体はは整数ポインタであるため、p+1 が整数を 1 つスキップするたびに。 p のアドレスが arr+sz のアドレス以上になるまで、ループから抜け出します。

それでは、このコードの結果を見てみましょう~

実際、コードの 91 行目を次のように変更できます。

int *p=arr;

なぜなら配列名は配列の最初の要素のアドレスであるので、実際にはこれら 2 つのコードの実行結果は次のようになります。同様に、見てみるのもいいかもしれません~



さて、今日の内容はここで共有するだけです


ブロガーの言うことが良いと思うなら、


ワンクリックと 3 つのリンクでサポートへようこそ


ありがとう! ! ! ! ! !

ここに画像の説明を挿入します

おすすめ

転載: blog.csdn.net/m0_63564767/article/details/133561683