ポインタの最も詳細な説明

ポインタ

1. ポインタ変数の定義

C言語の変数には、通常変数(コンテンツ変数)とアドレス変数(ポインタ変数)の2種類があります。通常変数(コンテンツ変数)にはコンテンツが格納され、アドレス変数(ポインタ変数)にはアドレスが格納されます。

  1. 定義されたフォーマット

格式: type * ポインタ変数名

//普通变量
int a;
//地址变量
int *a;

ノート:

① 定義変数(通常変数、ポインタ変数)は必ず型名を先頭に付けてください。

② ポインタ変数を定義する場合、ポインタ変数名の前にある記号は、"*"現在ポインタ型の変数が定義されていることを示します。“*”いいえポインター変数名の一部は単なるフラグです。

③ ポインタ変数はアドレスを格納するために特別に使用される変数であり、ポインタ変数に直接整数を代入することは禁止されています。

  1. ポインタ変数への参照

“&”アドレス演算子を例に挙げると、&通常の変数のアドレスは演算子を介して取り出すことができます

*ポインタ演算子は、*ポインタ変数が指す通常の変数の値を取り出すことができます
(通常の変数を間接参照)

例:

#include<stdio.h>
int main(){
    
    
	int a=4,b=0;
	int *p,*q,*t;
	p=&a;    //取出变量a的地址
	b=*p;
	 
	printf("a=%d\nb=%d\n*p=%d\n",a,b,*p);
	printf("\n\n\n");
	printf("a的地址为:%p\nb的地址为:%p\np的地址为:%p",&a,&b,p);
	return 0;	
}
//运行结果:
a=4
b=4
*p=4
a的地址为:000000000062FE14
b的地址为:000000000062FE10
p的地址为:000000000062FE14
--------------------------------
Process exited after 0.0017 seconds with return value 0

動作図:

ここに画像の説明を挿入

ノート:

①共通変数への「ポイント」にポインタ変数を代入することができます(ポインタ変数=&共通変数)

②C 言語の正しいアプローチは、まずポインタ変数で特定の記憶装置を指し、次にポインタ変数を使用して、そのポインタ変数が指す記憶装置を参照することです。

③ 変数名(通常変数、ポインタ変数)はすべてその記憶単位の値を表します。

④ ポインタ変数 p が変数 a を指している場合、ポインタ変数 p に変数 a のアドレスが代入されます。

⑤ すべてのポインタ変数は同じバイト数をメモリに割り当てます。

//关于第五条的解释
#include<stdio.h>
int main(){
    
    
	int *p;
	double *q;
	float *t;
	char *r;
	printf("\t int *p = %d\n",sizeof(p));
	printf("\t double *q = %d\n",sizeof(q));
	printf("\t float *t = %d\n",sizeof(t));
	printf("\t char *r = %d\n",sizeof(r));
	return 0;	
}

操作結果:

ここに画像の説明を挿入

2. 配列ポインタ

配列要素は通常の変数と同じであるため、配列要素を指すポインタ変数を定義することは、通常の変数を指すポインタ変数を定義することとまったく同じです。

int *p;
int a[10];
//a は配列の最初のアドレス、つまり a <=> &a[0];
p=a; 同等の p=&a[0];
p=a+1同等の p= &(a+1)

配列名は配列の最初のアドレスです

//上述等价的实例
#include<stdio.h>
int main(){
    
    
	int a[10];
	int *p,*q;
	p=&a[1];
	q=a+1; 
	printf("%p\n",p);
	printf("%p\n",q);
}

操作結果:

ここに画像の説明を挿入

ノート:

① C言語で規定されており、配列名は配列の先頭アドレスを表し、アドレス定数となります。

②ポインタ変数が配列の要素を指している場合、1増やすとポインタ変数は配列の次の要素を指し、1減らすと配列の前の要素を指します。

③ ポインタ変数が配列を指している場合、添字演算([])は配列の後またはポインタ変数の後に使用できます。

④ 2 つのポインタ変数が同じ配列を指している場合、2 つのポインタ変数の大きさは次のように比較できます。

⑤仮引数の配列は、「配列名」の値は変更できますが、実配列名の値は変更できないため、実際には実配列ではなくポインタ変数です。

⑥ 仮引数が配列またはポインタ変数の場合、関数内で仮引数を介して実引数の値を変更できます。

例 1:

ここに画像の説明を挿入

答えと分析:

ここに画像の説明を挿入

例 2:

//运行结果是什么?
#include<stdio.h>
int main() {
    
    
	int *p,a[3];
	p=a;  //将数组a的首地址,赋值给指针p 
	for(int i=0; i<3; i++) {
    
    
		scanf("%d",p++); //因为数组名就是地址,所以不用加取地址符 
	}
	printf("\n\n");
	
	for(p=a;p<a+3;){
    
      //将数组a的首地址,赋值给指针p 
		printf("%d ",*p++); //没输出一次,就向后移动一位 
	} 
	return 0;
}

操作結果:

ここに画像の説明を挿入

3. 多次元配列を指すポインタ変数

Ⅰ. ポインタとポインタの違い

例:以下の図に示すように、 では、追加されるのは要素です。 、追加されるのはint a[3]a+1ですint a[3][4]a+1
ここに画像の説明を挿入

覚えて:

① 列ポインタのみが「実際に」要素を指します。つまり、要素を指す記憶単位です。
② 1 次元配列名は列ポインタを表し、2 次元配列名は行ポインタを表します。

注: a が 2 次元配列の場合、次のものが存在します。

a+iは行ポインタ、つまり行全体を指します。それに 1 を加えると、次の行を指します。

*(a+i)と同様a[i]、要素を指すカラムポインタです。

*(a+i)+jは とa[i]+j同じで、どちらもa[i][j]要素のアドレスを表します。つまり、 と&a[i][j]同等です。*(a+i)+j= a[i]+j= &a[i][j]

*(*(a+i)+j)*(a[i]+j)(*(a+i))[j]同様a[i][j]、すべて要素を表しますa[i][j]

例 1:

次の定義があるとすると、int w[2][3]w への不正な参照は次のようになります。

A、*(w[0]+2)B、*(w+1)[2]C、w[0][0]D、*(w[1]+2)E、w[1]+2

分析: A はw[0][2]Yes の要素を表し、C は要素を表しますw[0][0]。Dはw[1][2]Yes の要素を表し、E は Yes の要素を表します。&w[1][2]つまり、E は要素ではなくアドレスです。

w[0][0]B では、wはアドレスを指すため、w+1 は 1 行下に移動すること、*(w+1)[2]つまり 2 行下に移動することを意味するため、表現された要素になりますが、 B が範囲外であるw[3][0]ため、int w[2][3]

例 2:

int a[3][4]; int *p=&a[0][3];

しかし:

p+1 は要素を指しますか?

p+4 は要素を指しますか?

p-2 は要素を指しますか?

二次元配列の要素のアドレスを取得するためによく使用されますか?

以下のように分析します。

ここに画像の説明を挿入

例 3:

2 次元配列を出力しますa[4][4](for ループは 1 つだけ使用できます)。

#include<stdio.h>
int main() {
    
    
	int a[4][4] = {
    
    
		{
    
    1, 2, 3, 4},
		{
    
    5, 6, 7, 8},
		{
    
    9, 10, 11, 12},
		{
    
    13,14,15,16}
	};
	int *p;
	for(p=a[0]; p<a[0]+12; p++) {
    
    
		printf("%d ",*p);
	}
	return 0;
}

操作結果:

ここに画像の説明を挿入

m 要素の 1 次元配列へのポインタ変数

Ⅱ . m個の要素からなる1次元配列を指すポインタ変数

m 要素の 1 次元配列を指すポインター変数の形式を定義します。

型 (*ポインタ変数名) [m]

ポインタ変数の列数

好き:

int a[5][7];
int (*p)[7];
p=a;

char b[ 10][80];
char (*r)[80];
r=b+5;

上記の擬似コード解析コードは次のとおりです。

/*
int a[5][7];
int (*p)[7];
p=a;
 */

#include<stdio.h>
int main() {
    
    
	int a[4][4];  //定义一个二维数组
	int (*p)[4];  //定义一个一维数组的指针变量,也就是说该数组是指向某一行第四列的一维数组
	int *c;       //普通的指针变量
	p=a;          //将二维数组的首地址赋值给p,也就是说此时的p是指向的a的首行,且(*p)[4]等价于p[0][4]
	printf("%p\n%p\n",a,p);   //打印数组a的首地址和p的首地址
	printf("%d ",sizeof(a));  //打印数组a所占的空间,也就是二维数组a[4][4]所占的空间
	printf("%d ",sizeof(*p)); //打印数组p所占的空间,也就是一维数组p[0][4]所占的空间
	printf("%d ",sizeof(c));  //打印指针变量(地址变量)c所占的空间,
	printf("\n\n");
 	return 0;
}

印刷結果は次のとおりです。

ここに画像の説明を挿入

/*
char b[ 10][80];
char (*r)[80];
r=b+5;
*/

#include<stdio.h>
#include<string.h>
int main() {
    
    
	char b[10][80];  //定义一个二维数组
	char (*r)[80];   //定义一个一维数组的指针变量,也就是说该数组是指向某一行第八十列的一维数组
	r=b+5;			//将b[0][0]向下移动五行再赋值给r,也就是将a[5][0]的地址赋值给r。
	printf("%p\n%p\n",r,b[5]); //打印地址
	printf("%d ",sizeof(*r));  //查看空间
	printf("%d ",sizeof(b[5]));//查看空间
	return 0;
}

印刷結果は次のとおりです。
ここに画像の説明を挿入

例 1:

次のコードを実行し、for ループの別の出力メソッドを記述し、p=a+1; のコードを記述した場合、どのような結果が出力されますか?

#include<stdio.h>
int main() {
    
    
	int a[3][4]= {
    
    1,2,3,4,5,6,7,8,9,10,11,12};
	int (*p)[4];
	p=a;
	int *q;
	for(int i=0; i<3; i++) {
    
    
		for(int j=0; j<4; j++) {
    
    
			printf("%d\t",p[i][j]);
		}
		printf("\n");
	}
	return 0;
}

答えと分析:

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

	int a[3][4]= {
    
    1,2,3,4,5,6,7,8,9,10,11,12};
	int (*p)[4];
	p=a;
	int *q;
	for(int i=0; i<3; i++) {
    
    
		for(int j=0; j<4; j++) {
    
    
			printf("%d\t",p[i][j]);
		}
		printf("\n");
	}
	printf("\n\n");
	
	//一层for循环打印二维数组 
	for(q=a[0];q<a[0]+12;q++){
    
    
		printf("%d  ",*q);
	}
	printf("\n\n");
	
	
	//打印p=a+1,回打印出什么 
	p=a+1;
	for(int i=0; i<2; i++) {
    
    
		for(int j=0; j<4; j++) {
    
    
			printf("%d\t",p[i][j]);
		}
		printf("\n");
	}
	return 0;
}

実行結果 (自己分析のために上記の知識ポイントと組み合わせたもの):

ここに画像の説明を挿入

例 2:

次のコードを実行すると何が出力されるでしょうか?

#include<stdio.h>
main() {
    
    
	int a[2][3]= {
    
    {
    
    1,2,3},{
    
    4,5,0} },(*pa)[3];
	int i;
	pa=a;
	for(int i=0;i<3;i++){
    
    
		if(i<2){
    
    	
			pa[1][i]=pa[1][i]-1;
		}
		else {
    
    
			pa[1][i]=1;
		}
	}
	printf("%d\n",a[0][1]+a[1][1]+a[1][2]);
}

答えと分析:

ここに画像の説明を挿入

4 番目、文字列を指すポインタ変数

文字列定数: C 言語は最初のアドレスに従って文字列定数を処理します。

ポインタなのでポインタ変数の原則に従う必要がありますが、文字列定数は定数であるため、代入番号の左側に文字列定数を書くことはできないことがわかっています。

文字列 char *p="abc" の場合

①最初の住所

②「abc」アドレス定数

③pには先頭文字のアドレスが格納される

例:

次のコードのうち、どれが正しく、どれが間違っているかを指摘し、その理由を説明してください。

char str[]="China";

char *p="China";     ✔   因为字符串是地址常量,所以正确

str = "Chinese";     ❌    str也是地址常量,不能放在赋值号的左边    

p="Chinese";         ✔    p是地址变量,字符串是地址常量,也是"C"的地址

char *q ={
    
    "China"};  ❌    加大括号是初始化,此时就不是地址常量了,所以错误

char s[]={
    
    "China"};  ✔    初始化,大括号可加可不加。所以✔

char *p;*p="china";*p是内容值,但是字符串是地址值,所以❌

5. 関数を指すポインタ変数

関数名は配列名と同じであり、開始アドレスであり、アドレス定数です。

関数ポインタ変数を定義する方法:

typename(*ポインタ変数名)();

例:

次のコードを実行すると結果はどうなるでしょうか?

#include<stdio.h>
int min(int a,int b) {
    
    
	return a>b?b:a;
}
int max(int a,int b) {
    
    
	return a>b?a:b;
}
int main() {
    
    
	int x= 6,y=10;
	int (*p)(int a,int b);  //定义指向函数的指针变量
	p=max; 				   //将max函数的地址赋值给指向函数的指针变量
	printf("%d\n",max(x,y));
	printf("%d\n",p(x,y));
	p=min;				   //将min函数的地址赋值给指向函数的指针变量
	printf("%d\n",min(x,y));
	printf( "%d\n",p(x,y)); 
}

操作結果:
ここに画像の説明を挿入

ノート:

(1) 関数を指すポインタ変数を定義する場合は、括弧が 2 つ必要であり、仮パラメータを定義する必要がないことに注意してください。ただし、devc++ などの一部のコンパイラでは、仮パラメータを定義する必要がある場合があります。

(2) 単一の関数名は関数の先頭アドレス(関数のエントリアドレス)を表します。

(3) 関数のポインタ変数は、関数のエントリポイント (関数の先頭アドレス) のみを指すことができ、関数内の特定の命令を指すことはできません。(関数を指すポインタ変数に 1 を加算することも無意味です)。

(4) 関数を指すポインタ変数に値を代入する場合は、関数名のみを記述し、パラメータを記述する必要はありません。

6. ポインタを返す関数

ポインタを返す関数は次のように定義されます。

型名 * 関数名 (パラメータリスト) {

}

例:

#include<stdio.h>
int *fun(int *x,int *y) {
    
    
	if(*x<*y)
		return x;
	else
		return y;
}
int main() {
    
    
	int a=7,b=8,*p,*q,*r;
	p=&a;
	q=&b;
	r=fun(p,q);
	printf("%d,%d,%d\n",*p,*q,*r);
	return 0; 
}



7,8,7

--------------------------------
Process exited after 0.1331 seconds with return value 0
请按任意键继续. . .

7. ポインタ配列とポインタを指すポインタ変数

ポインタの配列

配列のすべての要素がポインタ型 (アドレス) である場合、その配列はポインタ配列と呼ばれます。

フォーマット:

类型名 *数组名[常量表达式];

int *a[5];

ノート:

(1) m要素からなる1次元配列を指すポインタ変数の定義との違いに注意

类型名 *数组名[常量表达式];本質的には 1 次元配列であり、アドレス定数は配列に格納されます。

类型名 (*指针名)[常量表达式m];本質的には、これは 2 次元配列であり、1 次元配列を指すポインタ変数です。

(2) 各要素はポインタ型(アドレス)、つまりポインタ変数に相当します。

例:

#include<stdio.h>
int main() {
    
    
	char ch[3][4]= {
    
    "123","456","78"},*p[3];
	int i;
	for(i=0; i<3; i++)
		p[i]=ch[i];
	for(i=0; i<3; i++){
    
    
		printf("p = %p \n" ,p[i]);
		printf("ch =%p\n" ,&ch[i][0]);
		printf("p = %s \n" ,p[i]);
		printf("ch =%s \n" ,ch[i]);
		printf("\n");
	}	
	return 0; 
}

実行結果と分析:

ここに画像の説明を挿入

ポインタ変数からポインタへ

ポインタ変数のアドレスを格納するポインタ変数をポインタへのポインタ変数といいます。

定義形式:

型名**ポインタ変数名;

int a=3;

int *p=&a;

int **k=&p;

次に、 *k は変数 p (変数 a のアドレス) を取得します。**k は変数 a の値 (a のデータ 3) を取得します。

8. ヌルポインタ

ポインタ変数は NULL 値を持つことができます。つまり、ポインタ変数はどの変数もポイントせず、有用な記憶ユニットも参照しません。

NULL はシステムで 0 として定義されています。つまり、NULL の値は 0 です。

int a,b, C, *p=NULL;

現時点では、p の値はヌル ポインターです。つまり、p は有用なストレージ ユニットを指しません。NU LL の値は 0 ですが、p がアドレス 0 のメモリユニットを指しているとは考えられません。

ノート:

(1) ポインタ変数の値がヌルポインタの場合、その変数が指す記憶装置を参照できません。

(2) ポインタ(アドレス)の基底型が void 型の場合、参照がある場合は対応する強制型置換を行う必要があります。

9. まとめ

ポインター変数に関するいくつかの注意点

  1. ではint *a;、変数名が a です。これは、*a変数名が変数名の命名規則 (つまり、英数字のアンダースコアに従っている) に従っているためではなく、数字で始めることができないため、ポインター変数の変数名は型名ではなく、型名です。ポインタ型と呼ばれます。*aint *

  2. intでは*a;、 a は 8 バイト、もちろんint 型なので 4 バイト、*aその中の b も 8 バイトですが1 バイトを占有します。したがって、それがどのような種類の基本型ポインタ変数であっても、8 バイトを占有し、この型のバイトも占有します。(異なるオペレーティング システムの結果は同じではないことに注意してください。しかし、同じことが 1 つあります。それは、基本型に関係なく、ポインタ変数によって占有されるバイト数が同じであるということです。)*achar *b*b*指针变量

  3. ポインタ*とでは&、これらは逆演算であり、&相互[]作用します。つまり、次のように互いに打ち消し合うことができます。

    int a[3]; 
    int b;
    int *a;
    int *p;
    a=&b;
    p=&a[0]; <=> p=a;
    p=&a[3]; <=> 
    p=a+3;
    
    &* 互逆
    
    *[] 等价
    
    &[] 互逆
    
  4. ポインタには 5 つの公式があります

    ① アドレス変数のアドレスを取得し、アドレスを取得した人が誰を指すか

    ② 内容の値として * があり、読み取りまたは書き込みのいずれかで、割り当て番号の左側に置くと書き込み、残りが読み取られます。

    ③ アドレスに*が無い場合、アドレスの割り当てとは、 に変更することを意味します。

    ④ ポインタ変数は定義し、初期化しないと使用できません。

    ⑤ address変数にはアドレスが格納され、content変数にはコンテンツが格納されます。

  5. 重要な同等性

    int fun(int a[10]) <=> int fun(int *a) <=> int fun(int a[])

  6. 文字列へのポインタ変数では、

    char *p="qweqweqweqw";
    char a[]="qweqwewqeq";
    char *q;
    q=a; a是数组的地址,是一个地址常量,同时也是数组的首地址,即'q'的地址
    p="asdasd"; 虽说p是个地址变量,但是,由于对于字符串来说,"asdasd"也就相当于一个地址常量,也就是‘a’的地址。
    
  7. 区別

    (1) m要素からなる1次元配列を指すポインタ変数の定義との違いに注意

    类型名 *数组名[常量表达式];本質的には 1 次元配列であり、アドレス定数は配列に格納されます。

    类型名 (*指针名)[常量表达式m];本質的には、これは 2 次元配列であり、1 次元配列を指すポインタ変数です。

  8. ヌルポインタ

    char *q =NULL;

  9. ポインタには 5 つの公式があります

    ① アドレス変数のアドレスを取得し、アドレスを取得した人が誰を指すか

    ② 内容の値として * があり、読み取りまたは書き込みのいずれかで、割り当て番号の左側に置くと書き込み、残りが読み取られます。

    ③ アドレスに*が無い場合、アドレスの割り当てとは、 に変更することを意味します。

    ④ ポインタ変数は定義し、初期化しないと使用できません。

    ⑤ address変数にはアドレスが格納され、content変数にはコンテンツが格納されます。

  10. 重要な同等性

    int fun(int a[10]) <=> int fun(int *a) <=> int fun(int a[])

  11. 文字列へのポインタ変数では、

    char *p="qweqweqweqw";
    char a[]="qweqwewqeq";
    char *q;
    q=a; a是数组的地址,是一个地址常量,同时也是数组的首地址,即'q'的地址
    p="asdasd"; 虽说p是个地址变量,但是,由于对于字符串来说,"asdasd"也就相当于一个地址常量,也就是‘a’的地址。
    
  12. 区別

    (1) m要素からなる1次元配列を指すポインタ変数の定義との違いに注意

    类型名 *数组名[常量表达式];本質的には 1 次元配列であり、アドレス定数は配列に格納されます。

    类型名 (*指针名)[常量表达式m];本質的には、これは 2 次元配列であり、1 次元配列を指すポインタ変数です。

おすすめ

転載: blog.csdn.net/LOL_toulan/article/details/109356397