C言語のデータ構造(3) - ポインタ型

ポインタは C 言語の主な特徴的な応用であり、C 言語においてポインタは基本的なデータ型であり、配列、関数、構造体などと組み合わせてさまざまな場面で使用できます。ポインタを学習するときは、2 つのことを行う必要があります。1 つは、各ポインタがどこを指しているのかを常に把握していること、もう 1 つは、ポインタが何を指しているのかを常に把握していることです。

ポインタとポインタ変数

ポインタを学習する前に、変数の意味を理解する必要があります。各変数には 2 つの物理的な意味があり、1 つはそれ自身の内容であり、もう 1 つは変数のアドレスです。コンピューターのメモリは、長い通りに並ぶ家々のようなものと考えることができます。各家にはデータを格納でき、家番号によって識別されます。このとき、家の中のデータが変数の内容そのもので、部屋番号が変数のアドレスになります。より正確に説明すると、
変数の内容は、変数によって割り当てられた記憶領域に保存されたデータです。
変数のアドレスは、変数によって割り当てられた記憶領域の最初のアドレスです。

次に、直接アクセスと間接アクセスを理解する必要がありますプログラム内の変数へのアクセス操作は、特定のアドレスにある数バイトの記憶単位に対して操作します。一般に、プログラム中では変数名を指定するだけでよく、各変数のメモリ上の具体的なアドレスを知る必要はなく、各変数と特定のアドレスとの接続はCコンパイラシステムによって行われます。プログラム実行時における変数へのアクセス操作は、実際にはあるアドレスの記憶装置に対する操作となります。このように、変数のアドレスに従って変数の値に直接アクセスする方法を「ダイレクトアクセス」といいます。たとえば、プログラム内で整数変数名 a を引用すると、システムは a のアドレス値に従って変数 a の 4 バイトの内容に自動的に直接アクセスします。
特別な変数を定義する場合、この変数は、図に示すように、ポインター変数と呼ばれるメモリ アドレスを格納するために特別に使用されます。たとえば、独自のアドレス 2004 を持つ変数 pa を定義するとします。変数aのアドレスを変更したい場合(1001)が変数paに格納されています このとき、変数aが表す記憶装置にアクセスするには、まずpaのメモリアドレス(2004)を求め、そのアドレスを取り出しますそこから(1001)を取得し、1001の記憶装置から始まるアドレスにアクセスします。このように、変数paを通じて間接的に変数aのアドレスを取得し、aの値にアクセスする方法を「間接アクセス」といいます。
ここに画像の説明を挿入

上記の 2 つの概念を理解すると、ポインターについてより簡単に学習できるようになります。——ポインタは「アドレス」であり、変数のアドレスをポインタといいます。ポインタもデータ型としてポインタ定数とポインタ変数に分けることができ、それらの変数は他の種類の変数と同じ意味を持ち、計算や演算を行う前に定義する必要があります。ポインタ変数の値は変数のアドレスでなければなりません。
ポインタ変数が既にアドレス値を持っている場合、そのポインタ変数はある変数を指していると言え、ポインタ変数が指す変数を対象変数と呼びます。たとえば、ポインタ変数 pa は、pa=&a を通じて変数 a との指示関係を確立します。変数 a を指すポインタ変数 pa を呼び、変数 a はポインタ変数 pa のターゲット変数です。

: ポインタの型とポインタが指す型は 2 つの概念です。ポインタの型はポインタ自体の型であり、ポインタが指す型はポインタが指す変数の型です。例えば:

int a=5,b;
float x;
int *pi;
float *pf;

その中には、pi ポインタの型: int *、pf ポインタの型: float *、pi が指す型: int、pf: float が指す型があります。

ポインタ変数の定義

ポインタ変数の一般的な定義形式は次のとおりです。

データ型* ポインタ変数名
このうち、「*」は次の変数名がポインタ型であることを示します、「データ型」とは、ポインタ変数が指す対象変数を定義するデータ型であり、ベースとも言えますポインタ変数の型。

ポインタ変数の初期化

ポインタ変数も他のタイプの変数と同様に定義時に初期値を割り当てることができます。一般的な形式は次のとおりです:
データ型 * ポインタ変数名 = 初期アドレス値

#include <stdio.h>
void main()
{
    
    
	float x,*p1=NULL,*p2=&x;
	int y,*p3=&y;
	char name[30],*cp=name;
	...
}

上に示したように、p1 を null ポインタにし、p2、p3、cp にそれぞれ x のアドレス、y のアドレス、名前配列の最初のアドレスを格納します。p2 は x を指し、p3 は y を指し、cp は name[0] を指すと言えます。
: 1. 変数のアドレスを初期値としてポインタに代入する場合は、最初に変数を定義し、変数のデータ型とポインタのデータ型が一致している必要があります。

int n;
int *p=&n;

または次の同等の定義を使用して

int n,*p=&n;

2. 初期化されたポインタ値を初期値として別のポインタに割り当てることもできます。

float x,*p=&x,*q=p;

p と q は両方とも同じアドレス値を持ち、両方とも同じ変数 x を指します。
3. 特定のタイプのヌルポインタは初期化によって定義することもできます

int *p=0;/*值0是唯一能够直接付给指针变量的整型数*/
int *p=NULL;

上記の 2 行のコードは同じ効果があり、どちらも null ポインターを定義します。

ポインタの基本操作

1. アドレス演算
& では、演算変数が変数または配列要素である必要があり、変数または配列要素を指すアドレスを返します。一般的な形式は次のとおりです:
& 変数名または配列要素名

int a,*p;
pa=&a;

実装では変数のアドレスをポインタ pa に代入しており、このときポインタ pa は整変数 a を指します。
2. 間接アクセス演算
間接アクセス演算子「*」は、通常「間接アクセス演算」と呼ばれる単項演算子で、右オペランドはポインタ値であり、戻り値は指定されたアドレスの値になります。一般的な形式は次のとおりです。
*ポインタ変数またはターゲット変数のアドレス

int a,b,*p;
pa=&a;
b=*pa;

演算子「*」はpaのアドレスの記憶領域にアクセスし、paには変数aのアドレスが格納されているため、*paがアクセスするアドレスは変数aの先頭アドレスとなり、*paが占有する記憶領域となります。 a、上記すべて の代入式は、b=a と同等です。

「&」演算子と「*」演算子は逆演算であることがわかります。次の式の意味の違いに注意してください。
pa —— ポインタ変数
*pa —— ポインタ pa の対象変数
&pa —— ポインタ変数 pa は記憶領域のアドレスを占有します
。 3. 代入演算
(1) を代入します。変数のアドレスを同じタイプのポインターに変換します。たとえば、次のようになります。

int  a, *pa;
pa=&a; 		/* 使pa指向变量a */

(2) 次のように、ポインターの値を同じ型の別のポインターに代入します。

char  c, *s1=&c, *s2;
s2=s1;      /* 结果s1和s2指向同一变量c */

(3) 次のように、配列名などのアドレス定数を同じ型のポインターに割り当てます。

char  *str,ch[80];
str=ch;      /* 使str得到字符数组ch的首地址,即str指向数组ch */

(4) 同じ型のポインタの算術演算の結果がアドレスであれば、同じ型のポインタに代入できます。例えば:

int *p1,*p2,a[20];
p1=a;  p2=p1+5;  p1=p2-3

初期値の代入と代入操作の違い:初期値の代入は、変数の定義時に変数に値を代入することであり、間接的なアクセス操作ではありません。間接アクセス操作の結果は、アドレス値を受け取ることができない整変数になります。

4. ポインタの算術演算
(1) ポインタに整数を加算または減算します。

int a[10],*pa=a,*p;
pa+=5;/*pa指向变量a[5]*/
pb=pa-3;/*pa指向变量a[2]*/

(2) 同じ型の 2 つのポインタを減算します。

int a[10],*pa=&a[1],*pb=&a[8];
int dist;
dist=pb-pa;/*diat为7,表明pa与pb两个指针所指向的数组元素之间的距离*/

ポインタ加算・減算のポイント:
① ポインタ変数を2つ加算することはできません。
② ポインタの加減算は、ポインタ変数が配列を指している場合と、演算結果が同じ配列内の要素を指している場合にのみ意味を持ちます。
③ ポインタの加減算の結果はバイト単位ではなく、データ型のサイズ(sizeof(type))になります。
④ 2 つのポインタ変数が同じ配列を指している場合にのみ、ポインタ減算を実行することが現実的です。
⑤ *(p1+n) と (*p1)+n は 2 つの異なる概念です。
5. ポインタの関係演算
(1) 同じ配列を指す 2 つのポインタは関係演算に使用できます
(2) ポインタと整数データの比較は無意味であり、型の異なるポインタ変数間の比較は不正です。
⑶ NULL は、任意のタイプのポインタに対して == および != 演算を実行して、ポインタが null ポインタかどうかを判断できます。

ポインタと配列

C 言語では、ポインタと配列の関係は非常に密接です。配列を導入するとき、コンピュータのメモリにおける配列要素の格納方法と配列のアドレスがどのように割り当てられるかはすでにわかっており、意味があるのは同じ配列を指すポインタ間の算術演算と関係演算だけであるため、配列とポインタの組み合わせは重要です。 C言語のアプリケーションも多数あります。

ポインタと 1 次元配列

1. 1 次元配列へのポインタを作成するには、まずそれを定義してから、そのポインタに値を割り当てます。例えば:

int a[5],*pa,*p;
pa=a;/*  pa=&a[0];  */

配列名 a は配列の先頭アドレス、つまり a[0] のアドレスであるため、ポインタ pa は配列の先頭アドレスを指します。このとき、*paの値はa[0]の値となります。pa+1 は次の要素を指します。特殊なケースでは、p=&a[5]; のように、ポインタが定義された直後に、ポインタが配列要素を指すようにすることもでき
ます
。配列要素を参照します:
(1) a[i] の形式などの添え字メソッド;
(2) 配列名アドレスメソッド、配列名は配列の最初のアドレスであるため、アドレスの計算規則に従っての場合、a+i は配列名 a i 要素から始まる最初のアドレス、つまり a[i] のアドレスを意味します。その場合、*(a+i) は a[i] (3) ポインタメソッド、2 つあり
ます形式
①ポインタアドレス方式。pa=a があるので、pa+i は pa から始まる i 番目の要素、つまり a[i] のアドレスを意味しますので、*(pa+i) は a[i] ② ポインタダウンの標準的な方法です
pa=a であるため、*(pa+i) は a[i] と同等であるため、pa[i] の形式を C 言語で直接使用して、同じ型の i 番目のデータを表すことができます。 paで示される位置。

要約すると、同じデータ型のポインタ pa と配列 a の場合、両者の間に pa=a の関係が確立されると (つまり、ポインタ pa が配列 a の最初のアドレスを指す)、次のようになります。配列要素 a[i] の表現は同等です:
a[i]、* (a+i)、* (pa+i)、pa[i]

ポインタメソッドを使用して配列要素にアクセスする場合は、次の点に注意してください。
(1) ポインタ変数の値は変更できますが、配列名として使用されるポインタ定数の値は変更できません。aが配列名、pがポインタ、bが変数の場合、aの値を変更しようとするのは間違いですが、ポインタはアドレス変数であり、その値は変更できます。
(2) ポインタ変数を使用して配列要素にアクセスする場合は、配列の範囲外の問題を考慮する必要があります
(3) ポインタ変数を配列要素の添字表現で使用する場合、その添字は負の値を持つ可能性があります。ポインタ p と配列 b があり、p=&b[2] のとき、b[1] 要素がポインタ変数 p を使用する必要がある場合、添え字表記は p[-1] で、ポインタ メソッドは * です。 (p-1)
(4) ポインタ変数演算における単項演算子の正しい結合性に特別な注意を払う必要があります。たとえば、y=* p++ は y=* (p++) と同等、y=* ++p は y=*(++p) と同等です。

ポインタと 2D 配列

2 次元配列は 1 次元配列に似ており、ポインタと配列の関係を確立した後は、ポインタを使用して配列の要素を表すことができます。
ここで、配列 a[3][4] がある場合、
行要素を示す方法は次のとおりです。
a[0] は * (a+0) で示すことができます。つまり、 * a を使用してそれを示すことができます。 a[i]
* (a+i) で &a[1][3] は a[1]+3 または * (a+1)+3 &a[iで表すことができます。][j] は a[i]+j または * (a+i )+j で表すことができます。


2 次元配列の要素は次の方法で表現できます。
配列の添字: a[i][j]
ポインタ表記: * (* (a+i)+j)
行番号の添字表記: * (a[ i]+j) )
列番号の添え字表記: * (a+i)[j]

要約する

ポインタは C 言語の重要な部分であり、C 言語の主要な機能です。ポインタをよく学ぶには、変数の 2 つの物理的な意味、変数のアドレスと変数の内容を明確にする必要があります。ポインタ変数にも変数アドレスと変数の内容がありますが、ポインタ変数の変数の内容はその他の種類の変数のアドレス ポインタ変数を参照する場合 間接アクセスポインタ変数が指す変数の内容です。ポインタ変数を使用する場合は、識別子「*」に注意する必要があります。*p はポインタ p の対象変数を表し、&p はポインタ p のアドレスを表します。ポインタと配列の組み合わせの適用と、ポインタによって配列が表現される方法に注意してください。

おすすめ

転載: blog.csdn.net/Tao_9/article/details/129836560