目次
1. ポインタとは何ですか?
- ポインタはメモリ内の最小単位の番号、つまりアドレスです。
- 通常、口頭ポインタはポインタ変数 (メモリ アドレスを格納するために使用される変数) を指します。
ポインタに関しては、メモリについても理解する必要があります。メモリはコンピュータ上で特に重要なメモリです。コンピュータ内のプログラムはすべてメモリ内で実行されます。したがって、メモリを有効に使用するために、メモリは小さなメモリ単位に分割され、各メモリ単位のサイズは 1 バイトになります。
メモリの各ユニットに効果的にアクセスできるようにするために、メモリ ユニットには番号が付けられており、これらの番号はメモリ ユニットのアドレスと呼ばれます。
変数はメモリ内に作成され (メモリ内に空間が割り当てられ)、各メモリ単位にはアドレスがあるため、変数にもアドレスがあります。
1. ポインタ変数
& (アドレス演算子) を使用して変数のメモリ アドレスを取り出し、そのアドレスを
ポインタ変数である変数に格納できます。
#include<stdio.h>
int main()
{
int a = 10;
int* p = &a;//&a拿到a的地址
//注意a占用4个字节,这里只是将a的第一个字节的地址放到p变量中
//p 是一个指针变量
return 0;
}
ポインタ変数は、アドレスを格納するために使用される変数です。(ポインタに格納されている値をアドレスとして扱います)
2. ポインタのサイズ
ポインタはアドレスを格納するために使用され、アドレスはアドレス空間を識別する唯一のものです
ポインタのサイズは、32 ビット プラットフォームでは 4 バイト、64 ビット プラットフォームでは 8 バイトです。
ここではアドレス行について簡単に紹介します。
32 ビット マシンの場合、アドレス ラインが 32 本あると仮定し、アドレス指定が (1 または 0) のときに各アドレス ラインがハイ レベル (高電圧) とロー レベル (低電圧) を生成すると仮定します。
00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000001
...
11111111 11111111 11111111 11111111
2 ^ 32 (2 の 32 乗ビットの GB は 4GB です。つまり、4G と同じ大きさの空間をアドレス指定できます)
32 ビット マシンでは、アドレスは 32 個の 0 または 1 で構成されるバイナリ シーケンスであり、アドレスは 4 バイトに格納する必要があるため、
ポインター変数のサイズは 4 バイトにする必要があります。
64 ビット マシンでアドレス行が 64 行ある場合、アドレスを格納するためのポインター変数のサイズは 8 バイトになります
。
2. ポインタ型
変数の定義には、int、float などの多くの型があることがわかっています。もちろんポインタにも型があります
char *pc = NULL;
int *pi = NULL;
short *ps = NULL;
long *pl = NULL;
float *pf = NULL;
double *pd = NULL;
タイプ *
char*型のポインタは、char型変数のアドレスを格納するために使用されます。
short* 型ポインタは、short 型変数のアドレスを格納するために使用されます。
int* 型のポインタは、int 型の変数のアドレスを格納するために使用されます。
ポインタ型の意味 ポインタ型は、逆参照操作の実行時にアクセスされるバイト数を決定します。
ポインタ + - 整数
ポインタのタイプによって、ポインタが 1 歩前進または後退する距離 (距離) が決まります。
ポインタの逆参照
ポインターの型によって、ポインターを逆参照するときにどの程度の権限があるか (操作できるバイト数) が決まります。
char* のポインター逆参照は 1 バイトのみにアクセスできますが、int* のポインターの逆参照は 4 バイトにアクセスできます。
以下のコードは、コードの最初の 3 行を実行するためにデバッグされています。
1 行の実行で次の結果が得られます
ここでは、さまざまな権限の逆参照の違いを確認できます。
3、ワイルドポインター
ワイルド ポインターとは: ワイルド ポインターとは、ポインターが指す場所が不明であることです (ランダム、不正確、明確な制限がない)。
ワイルドポインタの原因
1. ポインタが初期化されていない
#include<stdio.h>
int main()
{
int* p;//因为局部变量指针未初始化,默认为随机值,不知道指针指向哪里
*p = 20;
return 0;
}
2. ポインタの境界外アクセス
#include <stdio.h>
int main()
{
int arr[10] = {0};
int *p = arr;
int i = 0;
for(i=0; i<=11; i++)
{
//当指针指向的范围超出数组arr的范围时,p就是野指针
*(p++) = i;
}
return 0;
}
*(p++) は ++ の最初の逆参照ですが、 *p = i; p++; に変更できます。
ここでは、ポインタが範囲外にアクセスされ、自分に属さない空間にはアクセスできません。
3. ポインタが指すスペースを解放します。
では、ワイルド ポインタを回避するにはどうすればよいでしょうか?
- 初期化するポインタ
- 境界外への注意深いポインタ
- ポインタは、解放されて NULL に設定されたスペースを指します。
- ローカル変数のアドレスを返さないようにする
- ポインタを使用する前にその有効性を確認してください
//检查有效性
#include <stdio.h>
int main()
{
int arr[10] = { 0 };
int* p = arr;
int i = 0;
for (i = 0; i <= 11; i++)
{
//当指针指向的范围超出数组arr的范围时,p就是野指针
*(p++) = i;
}
return 0;
}
4. ポインタ演算
1. ポインタ + - 整数
#include<stdio.h>
int main()
{
int i = 0;
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
int* p = arr;
for (i = 0; i < 10;i++)
{
printf("%d ",*p+i);
}
return 0;
}
2. ポインタ-ポインタ
ポインター - ポインターの絶対値は、ポインターとポインターの間の要素の数です (これらが同じ領域を指しており、ポインターが同じ型である場合)
これを説明するためのアナログ strlen() を示します。
#include<stdio.h>
int my_strlen(char * str)
{
char* a = str;
while (*a != '\0')
a++;
return a - str;
}
int main()
{
char* p = "abcdef";
int ret = my_strlen(p);
printf("%d\n",ret);
return 0;
}
【結果】
return a - str ; //間の要素の数 (つまり、文字数) を返します。
3. ポインタの関係演算
注:配列要素へのポインタは、配列の最後の要素の後のメモリ位置へのポインタと比較できますが、最初の要素より前のメモリ位置へのポインタとは比較できません。
5. ポインタと配列
#include <stdio.h>
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,0 };
printf("%p\n", arr);
printf("%p\n", &arr[0]);
return 0;
}
配列名と配列の最初の要素のアドレスが同じであることがわかりました。
ここでもう一度言います
配列名は通常、配列の最初の要素のアドレスを表します。
特殊なケース sizeof(arr)、&arr は配列全体を意味します
次に、配列名をアドレスとしてポインタに保存します。
#include <stdio.h>
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9,0 };
int*p = arr; //指针存放数组首元素的地址
int sz = sizeof(arr) / sizeof(arr[0]);
for (int i= 0; i < sz; i++)
{
printf("&arr[%d] = %p <====> p+%d = %p\n",i,&arr[i],i,p + i);
}
return 0;
}
の結果
同じことがわかったので、p+i は実際に配列 arr の添え字 i のアドレスを計算します。
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
// 0 1 2 3 4 5 6 7 8 9
//使用指针打印数组的内容
int * p = arr;
int i = 0;
//arr-->p
//arr == p
//arr+i == p+i
//*(arr+i) == *(p+i) == arr[i]
//*(arr+i) == arr[i]
//*(i+arr) == i[arr]
//3+5
//5+3
for (i = 0; i < 10; i++)
{
//printf("%d ", *(p + i));
printf("%d ", *(arr + i));
//printf("%d ", arr[i]);
//printf("%d ", i[arr]);
//p指向的是数组首元素
//p+i 是数组中下标为i的元素的地址
//p+i 起始时跳过了i*sizeof(int)个字节
}
return 0;
}
6. セカンダリポインタ
ポインタ変数にはアドレスの変数が格納され、ポインタ変数も変数であることはすでに学習しましたが、ポインタ変数のアドレスはどこに配置されるのでしょうか?
ポインタ変数を格納するための二次ポインタ
*ppa は、pa を見つけるために ppa 内のアドレスを逆参照します。 *ppa は実際に pa を訪問します。
//**ppa 先通过 *ppa 找到 pa ,然后对 pa 进行解引用操作: *pa ,那找到的是 a
int b = 20;
*ppa = &b;//等价于 pa = &b;
**ppa = 30;
//等价于*pa = 30;
//等价于a = 30;
7、ポインタ配列
ポインタの配列は、ポインタを格納する数値である array です。
int*arr[5];
整数ポインタの配列