ドキュメントのバージョン | 更新時間 | コンテンツを更新する |
---|---|---|
v1.0 | 2020-09-13 | 最初のドラフトが完了しました |
1.ポインターの重要性
- いくつかの複雑なデータ構造を表す
- データをすばやく転送し、メモリ消費量を削減(アドレスを直接送信)
- 関数が複数の値を返すようにする
- ハードウェアに直接アクセスできる
- 弦を簡単に扱える
第二に、ポインターの定義
1.アドレスとは
アドレスはメモリユニットの番号であり、ゼロから始まる非負の整数であり、CPUのアドレスバスの幅がアドレス指定範囲を決定します。
2.ポインターとは
ポインタはアドレスであり、アドレスはポインタです。
3.どのポインタ変数
アドレス/ポインタを格納する変数。
通常、ポインター変数は、使用時に略してポインターと呼ばれますが、ポインター変数とポインターは同じものではありません。
4.ポインタ変数が占めるバイト数
ポインタ変数は、変数が指すバイト数に関係なく、ポインタ変数自体が占めるバイト数は、CPUアドレスバスの幅です。
- 32ビットCPUの場合:ポインター変数は4バイトを占有します。
- 64ビットCPUの場合:ポインター変数は8バイトを占有します。
/**
* CPU:64bit
* OS:Windows10
* IDE:Clion
* Compiler:MinGW-64
*/
#include <stdio.h>
int main() {
printf("sizeof(void *):%d\r\n", sizeof(void *));
printf("sizeof(char *):%d\r\n", sizeof(char *));
printf("sizeof(int *):%d\r\n", sizeof(int *));
printf("sizeof(long *):%d\r\n", sizeof(long *));
printf("sizeof(float *):%d\r\n", sizeof(float *));
printf("sizeof(double *):%d\r\n", sizeof(double *));
return 0;
}
実行結果は次のとおりです。
sizeof(void *):8
sizeof(char *):8
sizeof(int *):8
sizeof(long *):8
sizeof(float *):8
sizeof(double *):8
第三に、ポインターの分類
1.基本型ポインター
①ポインターを定義します。
int *p = NULL;
変数pが定義されています。pの型は、int型の変数のアドレスint *
を格納する型です。
ポインターはハードウェアにアクセスできるため、危険を防ぐために、ポインターのデフォルト値には通常NULLが割り当てられます。
NULL
0の番号が付けられたアドレススペースを示します。このアドレスでは書き込みも読み取りも許可されていません。
②ポインターの割り当て:
int i = 99;
p = &i;
呼び出されるポインター変数pにint型変数iのアドレスを格納します。ポインター変数pは変数iを指します。
③ポインタを使用:
int j;
j = *p; //执行之后j的值为99
* pは、アドレスがpの内容である変数を意味します。
2.ポインターと配列
2.1。ポインターと1次元配列
①1次元配列名:1次元配列の最初の要素のアドレスを格納するポインタ定数です。
②下付き文字とポインタの関係:p [i] = *(p + i)
③関数が配列を処理する場合、少なくとも2つのパラメーターを受け取る必要があります。配列の最初の要素のアドレスと配列の長さです。
2.2。ポインタ変数の操作
①ポインター変数を追加、乗算、または分割できない
②2つのポインタ変数が同じ連続メモリ(配列)を指す場合のみ、それらを減算して、2つのアドレスの差を示すことができます。
2.3。ポインタ配列と配列ポインタ
ポインタ配列:ポインタ変数の配列で、各要素はポインタ変数です。
int *a[10];
配列ポインター:配列の名前である配列を指すポインター変数を表します。
int a[10];
int *p = a;
3.ポインターと構造
4.ポインターと関数
4.1。関数ポインタとは
関数を定義する場合、関数名はアドレスであるため、最初にポインター変数を定義して関数のアドレスを保存すると、このポインター変数が関数ポインターになります。
4.2。関数ポインタの役割
- コールバック関数として使用:ユーザーは関数をコールバック関数として実装できます。
- 抽象レイヤードデザイン
4.3。関数ポインタの使用
①定義方法:
int (*add)(int a, int b);
②使い方:
int my_add(int a, int b)
{
return a+b;
}
int main()
{
add = my_add;
printf("myadd result is:%d\r\n", my_add(1,1));
printf("add result is:%d\r\n", add(1,1));
return 0;
}
実行結果は次のとおりです。
myadd result is:2
add result is:2
5.マルチレベルポインター
int i = 10;
①レベル1ポインター:
int *p = &i;
評価済み:pは、変数型のアドレスint *
を格納するポインター変数pのint
型です。
②二次ポインタ:
int **q = &p;
感謝:qはint **
ポインター変数のタイプですqはint *
変数タイプのアドレスを格納するために使用されます。
③3レベルポインター:
int ***r = &q;
感謝:rはint ***
ポインター変数の型ですrはint **
変数型のアドレスを格納するために使用されます。
④使用:
printf("i = %d\r\n", ***r);
サンプルプログラムは以下のとおりです。
/**
* CPU:64bit
* OS:Windows10
* IDE:Clion
* Compiler:MinGW-64
*/
#include <stdio.h>
int main() {
int i = 10;
int *p = &i;
int **q = &p;
int ***r = &q;
printf("i = %d, &i = %p\r\n", i, &i);
printf("p = %p, &p = %p, *p = %d\r\n", p, &p, *p);
printf("q = %p, &q = %p, *q = %p, **q = %d\r\n", q, &q, *q, **q);
printf("r = %p, &r = %p, *r = %p, **r = %p, ***r = %d\r\n", r, &r, *r, **r, ***r);
return 0;
}
実行結果は次のとおりです。
i = 10, &i = 000000000061FE1C
p = 000000000061FE1C, &p = 000000000061FE10, *p = 10
q = 000000000061FE10, &q = 000000000061FE08, *q = 000000000061FE1C, **q = 10
r = 000000000061FE08, &r = 000000000061FE00, *r = 000000000061FE10, **r = 000000000061FE1C, ***r = 10
4番目に、void *ポインターの魔法の効果
①ボイドポインタを表す型なしポインタをので、空隙型ポインタは、ポインタの任意の型に変換することができます。
たとえば、一般的なAPI関数を設計する場合、パラメーターをvoid *に設定できるため、ユーザーがそれを呼び出すと、int型のポインターまたは構造体型のポインターを渡すことができますが、これにはユーザーがそれを呼び出す必要もあります。自分のポインタのタイプを指定します。
②void *ポインタには型がないため、*演算子を使用して操作したり、逆参照したりすることはできません。
5、動的メモリの割り当てと解放
1.スタティックメモリとダイナミックメモリ
- 静的メモリはスタックに割り当てられ、システムによって割り当てられ、システムによって解放されます。
- 動的メモリはヒープに割り当てられ、手動で適用され、手動で解放されます。
2.動的メモリ使用量
例として、動的配列の構築を取り上げます。
#define NUM 10
int *ptr = (int *)malloc(NUM * sizeof(int));
malloc関数は、システムから動的メモリを適用するために使用されます。パラメータは、要求されたメモリのサイズ(単位:バイト)です。アプリケーションが成功すると、メモリアドレスが返され、アプリケーションが失敗すると、NULLが返されます。
それを使用した後、解放するためにfree関数を使用します:
free(ptr);
ptr = NULL;
3.静的配列と動的配列の比較
静的配列(従来の配列)の欠点は次のとおりです。
- サイズは固定されており、定数ではなく、整数のみを使用できます。
- プログラムの実行中、プログラムの終了後、メモリは常に占有されて解放されます。
- 配列の長さが定義されると、再度変更することはできません。
- 関数で定義された配列は、関数が戻った後に破棄され、他の関数は再び使用できません。
動的メモリを使用する動的配列は、上記の4つの欠点を解決します。
- 任意のサイズで、プログラムの実行中に動的に適用され、動的に変更できます。
- 要求されたメモリが解放されない(無料)限り、すべての機能を使用できます。