コンテンツ
1.ポインタとは何ですか?
ポインタを理解するための2つのポイント:
1.ポインタは、メモリ内の最小単位の番号、つまりアドレスです。
2.通常、話し言葉で話されるポインタは、通常、メモリアドレスを格納するために使用される変数であるポインタ変数を参照します。
メモリは1つのメモリユニットに分割され、各メモリユニットは1バイトを占有します
メモリセル番号:
32ビットコンピューターの場合、電源を入れると正(1)/負(0)の電力を生成する32本のアドレスライン/データライン(ワイヤー)があります。
電源投入後に生成される2^32種類の2進シーケンス(数字)があり、それぞれがメモリ内のメモリユニットに対応しており、その数字はメモリユニットのアドレスと呼ばれます。
概要:ポインターはアドレスであり、口語のポインターは通常、ポインター変数を参照します。
ポインター変数:
&(アドレス演算子)を使用して、変数のメモリの実際のアドレスを取り出し、そのアドレスをポインタ変数である変数に格納できます。
void test1()
{
int a = 10; //向内存申请四个字节的空间,存放10
printf("%p\n", &a); //取地址 00AFFC8C
int* pa = &a; //地址也是一个值,可以存储到pa变量中 - pa就是指针变量
//此时的*是告诉读者pa是一个指针变量
//此时的int表示a的类型是int类型
*pa= 20; //解引用操作符
printf("a = %d\n", a);
char ch = 'w'; //向内存申请一个字节的空间,存放'w'
char* pc = &ch;
*pc = 'a';
printf("ch = %c\n", ch); //ch = a
}
要約:
ポインタはアドレスを格納するために使用され、アドレスはアドレス空間の一部を一意にマークします。
ポインターのサイズは、32ビット(32ビット)プラットフォームでは4バイト、64ビットプラットフォームでは8バイトです。
2.ポインタとポインタの種類
ポインタのサイズは4バイト(32ビット)なので、なぜポインタ変数をint、char、...に分割する必要があるのでしょうか。
ポインタ型の意味:
1.ポインターのタイプによって、ポインターを逆参照するときに持つ権限の量が決まります(数バイトを操作できます)。
2.ポインターが+-整数の場合、ポインターのタイプによって、ポインターが前進または後退する距離(距離)が決まります。
void test2()
{
//指针类型的意义:
//1.指针类型解引用操作时,能一次性访问几个字节
//2.指针进行+ -整数的时候,指针的类型决定了指针向前或者向后走一步有多大(距离)。
int a = 0x11223344;
int* pa = &a;
char* pc = &a;
*pa = 0;//访问4个字节
*pc = 0;//访问1个字节
//当使用int*pa定义时,*pa = 0让a的4个字节全部改成了0
// 而用char*pa定义时,*pa = 0只让a的1个字节变成了0
printf("%p\n", pa); //010FFB60
printf("%p\n", pa + 1);//010FFB64 向后走了4个字节
printf("%p\n", pc); //010FFB60
printf("%p\n", pc + 1);//010FFB61 向后走了1个字节
}
void test3()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int* pa = arr;
char* pb = arr;
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d\n", *(pa + i));//1 2 3 4 5 6 7 8 9 10
}
for (i = 0; i < 10; i++)
{
printf("%d\n", *(pb + i));//1 0 0 0 2 0 0 0 3 0
}
//因为1 2 3在内存中为 01 00 00 00 02 00 00 00 03 00 00 00
//用char定义指针变量,pb每次+1,只向前走了一个字节
char arr1[] = "abcdef";
char* pc = arr1;
int* pd = arr1;
for (i = 0; i < 6; i++)
{
printf("%c\n", *(pc + i));// a b c d e f
}
}
3.ワイルドポインタ
概念:ワイルドポインターとは、ポインターが指していることがわからない場所です(ランダム、正しくない、指定されていない)
3.1ワイルドポインタの原因
1.ポインタが初期化されていません
void test4()
{
int* p;
//p就是野指针
//p是指针变量,也是局部变量。局部变量不主动初始化,默认是随机值
*p = 20;//内存中随机找了一个位置 放入20
}
2.ポインタの範囲外アクセス
ポインタが指す範囲が配列の範囲を超える場合、pはワイルドポインタです。
void test5()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int* p = arr;
int i = 0;
for (i = 0; i <= 10; i++) //循环11次时,越界访问了
{
*p = 0;
p++;
}
}
3.ポインタが指すスペースが解放されます
int* test6()
{
int a = 10;
return &a;
}
int main()
{
int *p = test6();//p就是一个野指针
return 0;
}
test6()の呼び出しが終了すると、ローカル変数aによって作成されたスペースは破棄され、*pによって記録されたアドレスは現時点では無意味です。
3.2ワイルドポインタを回避する方法
1.ポインタの初期化
2.範囲外のポインタに注意してください
3.ポインターは、解放されて時間内にNULLに設定されるスペースを指します。
4.ローカル変数のアドレスを返さないようにします
5.使用する前にポインタの有効性を確認してください
4.ポインタ演算
4.1ポインタ+-整数
void test8()
{
char arr[] = { 'a', 'b', 'c', 'd' };
char* p = arr;
int i = 0;
for (i = 0; i < 4; i++)
{
printf("%c\n", *p++);//++优先级高于*
}
/*int i = 0;
for (i = 0; i < 4; i++)
{
printf("%c\n", *(p+i)); // a b c d
}*/
}
#define N_VALUES 5
float values[N_VALUES];
float *vp;
//指针+-整数;指针的关系运算
for (vp = &values[0]; vp < &values[N_VALUES];)
{
*vp++ = 0;
}
注:ポインターはサイズより大きくすることもできます
4.2ポインター-ポインター
ポインター-ポインターは、ポインターとポインターの間の要素数を取得します
前提条件:2つのポインターが同じスペースを指している
void test9()
{
char ch[5] = { 0 };
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
printf("%d\n", &arr[9] - &arr[0]); //9
printf("%d\n", &arr[9] - &ch[0] ); //err
//指针 - 指针 得到的是指针和指针之间的元素个数
//前提条件:两个指针指同一块空间
}
例:文字列の長さを見つける
int my_strlen(char* str)
{
char* start = str;
while (*str != '\0')
{
str++;
}
return str - start;
/*int count = 0;
while (*str != '\0')
{
count++;
str++;
}
return count;*/
}
void test10()
{
char arr[] = "abcdef";
//int len = strlen(arr);
int len = my_strlen(arr);
printf("%d\n", len);
}
int main()
{
test10();
return 0;
}
4.3ポインタの関係演算
既知:配列は継続的に格納され、配列のインデックスが大きくなると、アドレスが低から高に変化します
void test12()
{
#define N_VALUES 5
float values[N_VALUES];
float* vp;
for (vp = &values[N_VALUES]; vp > &values[0];)
{
*--vp = 0;
}
}
これは次のように簡略化できます。
for(vp = &values[N_VALUES-1]; vp >= &values[0];vp--)
{
*vp = 0;
}
実際、ほとんどのコンパイラでタスクを正常に完了することができますが、標準では動作が保証されていないため、これを記述しないようにする必要があります。
標準規制:
配列要素へのポインタは、配列の最後の要素の後のメモリ位置へのポインタと比較できますが、最初の要素の前のメモリ位置へのポインタとの比較はできません。
5.ポインタと配列
アレイ名は何ですか?例を見てみましょう
void test13()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,0 };
printf("%p\n", arr);
printf("%p\n", &arr[0]);
}
演算結果:
配列名と配列の最初の要素のアドレスが同じであることがわかります。
結論:配列名は、配列の最初の要素のアドレスを表します。(2つの例外、1。&arrayname、2.sizeof(arrayname))
次に、次のようなコードを書くことができます。
void test14()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,0 };
int* p = arr;//p存放的是数组首元素的地址
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d\n", *(p + i));
}
}
配列はポインタですか?いいえ
ポインタは配列ですか?いいえ
ただし、配列にはポインタを介してアクセスできます
6.セカンダリポインタ
void test15()
{
int a = 10;
int* pa = &a;
int* * ppa = &pa;//此时的ppa为二级指针
int** * pppa = &ppa;//pppa为三级指针
}
上記のコードの場合:aのアドレスはpaに格納され、paのアドレスはppaに格納されます。paは第1レベルのポインター、ppaは第2レベルのポインターです
セカンダリポインタの操作は次のとおりです。
* ppaはppaのアドレスを逆参照することによってpaを見つけ、*ppaは実際にpaにアクセスします
int b = 20;
*ppa = &b;//等价于 pa = &b;
**ppaは最初に*ppaを介してpaを検索し、次にpa:*paを逆参照します。
**ppa = 30;
//等价于*pa = 30;
//等价于a = 30;
7.ポインタの配列
ポインターの配列はポインターですか、それとも配列ですか?回答:それは配列です。ポインタの配列です。
私たちがすでに知っている配列
整数配列intarr1[10]は10個の整数を格納します
文字配列chararr2[6]は6文字を格納します
ポインタの配列はどうですか? int * arr3 [5]とは何ですか?
arr3は5つの要素を持つ配列であり、各要素は整数ポインターです。
void test16()
{
int a = 10;
int b = 20;
int c = 30;
int* arr3[5] = { &a, &b, &c };
int i = 0;
for (i = 0; i < 3; i++)
{
printf("%d\n", *arr3[i]);//arr3存放a,b,c的地址,解引用即可访问
}
}