ポインタ
ポインタを学習するときは、最初に()の優先度が[]よりも高く、[]の優先度が*よりも高いことを知っておく必要があります。
これを知っていると、ポインタを含む文を分析するときに複雑な文をすばやく理解できます。
AB |
Aはオブジェクトまたは構造体であり、AのメンバーBにアクセスします。Aがポインターの場合、適用されません。 |
A-> B | Aはポインター、->はメンバー抽出、A-> BはAのメンバーBを抽出し、Aはクラスまたは構造体へのポインターのみになります。 |
::: | スコープ演算子、A :: BはスコープAの名前Bを表し、Aは名前空間、クラス、構造です。 |
: | 継承を表します |
::: | スコープ解決 |
() | |
[] | アレイ |
-> | メンバーアクセス |
++ | 接尾辞の増分 |
- | |
! | 非 |
〜 | ビットではありません |
+ | ポジティブプラス |
- | |
++ | プレフィックスの増分 |
- | |
& | 住所 |
* | 連絡先参照(ポインタ) |
- ポインタは、アドレス情報を格納する変数です。
- Pの値はアドレスです
- * Pの値はポイントされたデータ値であり、* Pは通常の変数と見なすことができます。
- アドレス値を取得&
- 通常の変数の場合、変数のアドレスを取得する場合は、アドレス演算子&を使用する必要があります。つまり、&aは変数aのアドレスを意味し、Pと同じ意味です。
- *演算子は、間接参照演算子とも呼ばれます
- ポインタを宣言する
- ポインタの宣言では、int * pなどのポイントされたデータ型を指定する必要があります。
- ここでの* pのデータ型はintです。
- pはint型へのポインタです
- int * pは、* pがint型であることを強調し、* pを通常のint型変数として扱います。
- int * pは、int *が型であることを強調します。つまり、pはint型へのポインタであり、int *を強調し、int *をデータ型として扱います。
- ポインタの宣言では、int * pなどのポイントされたデータ型を指定する必要があります。
double * p; //定义声明了一个指向double类型的指针p
double* p; //定义声明了一个指向double类型的指针p,强调p是指向double类型的指针变量
double *p; //定义声明了一个指向double类型的指针p,强调*p是double类型
- ポインタの初期化
- 質問:ポインタアドレスまたはポインタが指すデータ値は初期化されていますか?
- 初期化されるのは、ポインタが格納されているアドレスです
- int * p =&a;
- 質問:ポインタアドレスまたはポインタが指すデータ値は初期化されていますか?
- ポインタは、==、<、>などの関係演算子を使用して比較できます。p1とp2が、同じ配列内の異なる要素など、2つの関連する変数を指している場合、p1とp2を比較できます。
- &オペレーターノート
- 配列名に対して&操作を実行すると、最初の要素のアドレスではなく、配列全体のアドレスが取得されます(配列名=最初の要素のアドレス、&演算子なし)。ただし、2つのアドレスは同じですが、ただし、次のように、操作には明らかな違いがあります。
- &array [2]と&arrayは2バイトのメモリブロックのアドレス値を取得し、&arrayは20バイトのメモリブロックの最初のアドレスです
- ストレージ
- 自動ストレージ
- 関数内で定義された通常の変数は自動ストレージスペースを使用します。これらの変数は自動変数とも呼ばれ、使用するストレージスペースがスタックになります。
- 自動変数は実際にはローカル変数であり、関数呼び出しで生まれ、終了して終了します
- スタックはLIFO後入れ先出しです
- 静的ストレージ
- プログラムの実行中に存在するストレージメソッドは、関数本体の外部で定義するか、変数を宣言するときにキーワードstaticを使用して定義できます。
- プログラムが生きている限り、静的変数は常に存在します
- 動的ストレージ
- new演算子とdelete演算子は、自動ストレージのスタックに相当するメモリプールを管理しますが、ここではヒープと呼びます。スタックとヒープは互いに独立しています。
- 動的ストレージは、プログラムの実行時にオンデマンドでメモリを割り当て、使い果たされるとメモリを削除します
- 自動ストレージ
- テンプレートクラスベクトル
- ベクトルも動的配列であり、メモリもnewとdeleteによって管理されますが、ベクトルは自動的に完了します
- vector <typeName> name(number);
- vector <typeName>名;
- ポインターと演算子
- ポインタ演算は上で説明されていますが、ここではインクリメント演算子と組み合わされています
- * ++ p、ここでは、pのアドレス値が最初にバイト数の1単位だけ増加され、次に*リリース演算子と組み合わされることを意味します。
- * p ++、同じ
- ++ * p、ここでは* pを通常の変数として直接扱い、++インクリメント演算子と組み合わせます
- (* p)++ここでは、* pを通常の変数と見なします。
ポインタと配列
配列へのポインタ
次の2つのIDarrは配列名、pは配列名アドレス値割り当てへのポインター、p = arrであることに注意してください。
左側は配列表記、右側はポインタ表記です
arr [i] == *(p + i)
&arr [i] == p + i
- 配列の先頭へのポインター。ポインター算術演算または配列のインデックス付け(つまり、上記の2つの式)を使用してアクセスできます。
- ポインタと配列は完全に互換性があるわけではありません。配列名はポインタ定数と同等であり、その値は配列の0番目の要素の最初のアドレスです。
- 配列名の値は変更できませんが、ポインター変数は操作できます
- ポインタ演算子*は配列arrに適用できますが、arrに格納されている値を変更することは違法であり、varで表される配列の配列メンバーの値のみを変更できます。
- 例外:sizeof演算子は配列名の長さを取り、配列全体の長さを返します
#include <iostream> using namespace std; const int MAX = 3; int main () { int var[MAX] = {10, 100, 200}; int *ptr; ptr = var; for (int i = 0; i < MAX; i++) { *var = i; // 这是正确的语法 var++; // 这是不正确的 ptr++; //TRUE, 指针加1个int单位字节,从而指向了下一个元素 } return 0; }
- 配列名は、0番目の要素の最初のアドレスと同じです
- arrは&arr [0]と同じです
- arrと&arrの値は同じですが、arrは0番目の要素の最初のアドレスであり、&arrは配列全体の最初のアドレスです。
- 算術演算のポインタに割り当てられると、前者は1つの要素メモリバイトのサイズを増減し、後者は配列バイトのサイズを増減します。
- 配列へのポインタを作成します
- 以下のコードでは:
- paは、配列の0要素の最初のアドレスへのポインタです。
- pbはポインタであり、paと同じです
- pbbはポインタであり、paおよびpbと同じ値ですが、pbbは配列全体を指します。
- pcは、100個のdoubleを含む配列へのポインターです。
- pdは配列であり、配列には100個のdouble *ポインター要素が含まれます
- 以下のコードでは:
double arr[100];
double *pa = arr;
double *pb = &arr[0];
double *pbb = &arr;
double (*pc)[100];
double *pd[100];
新しいオペレーターがメモリーを割り当てます
- C言語では、ライブラリ関数malloc()を使用してメモリを割り当てます。これはC ++でも互換性がありますが、新しい演算子を使用してメモリを割り当てることをお勧めします。
- 新しいオペレーターの操作フロー:
- ①運用フェーズでは、int値、つまりint型データです。データに名前、つまりメモリラベルがありません。このデータには名前のないメモリが割り当てられます(このint型データはメモリ空間を占有しますが、スペースタグなし)
- ②名前のないメモリを割り当て、ポインタを使用してこのメモリブロックにアクセスします
- ③作成する必要のあるデータの種類を新しく伝える必要があります
- ④新しいオペレータは自動的に適切なメモリブロックを見つけ、メモリブロックのアドレスを返します
- ⑤要するに、新しいデータ型を伝えるだけで、newは対応するメモリブロックアドレスをポインタに与えます
- int * ptr = new int;
- 前後の2つのデータ型が対応している必要があります。ここにintがあります。
- Q:ポインタの初期化とメモリの新しい割り当ての違いは何ですか?
- int * p =&a;ポインタの初期化、データは非ポインタ変数を介してアクセスできます。つまり、メモリブロックの値は、aを介して変更するか、* pを介して変更できます。
- int * ptr = new int; newはメモリを割り当て、* ptrを介してのみアクセスデータを変更できます。
- Q:住所についてどのくらい知っていますか?
- アドレスはメモリブロックの名前であり、変数はメモリブロックのラベルです
- 注:アドレスは、x01010101などのメモリブロックの最初のビットの名前のみです。int型の場合、32を加算すると、メモリブロックの最後のビットのアドレス名になります。
- 動的配列を作成するための新しい演算子
- データ量が多い場合は、従来の配列、文字列、構造などの使用を避け、新しい演算子を使用して動的配列を作成するようにしてください。
- シナリオ:プログラムを作成するときに、配列を使用するかどうかわからないことがわかったため、配列が必要かどうかは、入力するデータによって異なります。現時点では、静的バインディングの問題を理解し、動的バインディング。
- 静的バインディング:メモリはコンパイル時に割り当てられ、プログラムが配列を呼び出すかどうかに関係なく、対応するメモリスペースを占有します
- 動的バインディング:コンパイル時にメモリは割り当てられません。つまり、プログラムが呼び出されるかどうかに応じて、実行時に対応するサイズのメモリスペースが作成されます。作成される長さは実際の長さと同じです。この特別な配列は呼び出されます。動的配列。
- int * ptr_dynamic_array = new int [11];
- []は要素の数を示します
- newは、配列の最初の要素のアドレスを返します
- 使い終わったら、delete演算子を使用して配列を解放し、ptr_dynamic_arrayを直接削除します。
- 動的配列を使用するには、ptr_dynamic_array [10]などの配列名としてポインターを使用するだけで済みます。
- 新しいオペレーターの補足
- new演算子を使用すると、動的配列を作成したり、動的構造と動的クラスを作成したりできます。原理は、動的配列を新しく作成する場合と同じです。つまり、メモリブロックは、実行時にオンデマンドで割り当てられ、最初のアドレスが割り当てられます。メモリブロックのが返されます。
- インフレータブル* ps =新しいインフレータブル;
- * psはインフレータブル構造タイプのポインタであり、インフレータブル*とint *は同じです
- ここで追加するには、ポインタが構造体の場合、メンバーアクセスはメンバー演算子->を使用してアクセスする必要があります
ポインタ演算
- ポインタ+1、ここで+1は、int * pなどのタイプのメモリバイト数に1つのポインタを追加することを意味します。p+ 1は、pに格納されているアドレス値に8バイトを追加することを意味します。
- 配列、アドレス、ポインタ
short tell[10]; cout<<tell<<endl; cout<<&tell<endl; cout<<&tell[0]<<endl;
- 変数のポインタをインクリメントすることができ、アレイをインクリメントすることができない、およびアレイは一定のポインタとみなすことができます。
- 配列名は最初の要素のアドレス値であり、ポインター変数はアドレス値を格納するため、ポインター変数を配列名として使用できます。
- 配列名のアドレスを取得するには、次の点に注意する必要があります。
- 配列名のアドレスを取得すると、&tellは配列全体のアドレスを取得します。つまり、取得するのは、配列が占有しているメモリブロックのブロックアドレスです。&tell
- 配列名のアドレスを取得した後、&tellが10個の要素を含む配列にポイントを割り当てることができるポインタ
- &tell + 2とすると、アドレスは20増加する必要があります
- 配列の最初の要素のアドレスを取得します。&tell [0]は、要素が占めるメモリブロックのブロックアドレスであり、多くの場合、デフォルトで配列名tellになります。
- 配列名tellをポインタと見なします。tellは&tell [0]であるため、tellは短い型を指すポインタ定数です。
- tell + 1を追加すると、アドレスが2バイト増加します
- 配列名のアドレスを取得すると、&tellは配列全体のアドレスを取得します。つまり、取得するのは、配列が占有しているメモリブロックのブロックアドレスです。&tell
- したがって、配列とポインターの場合、配列表記またはポインター表記のいずれかを使用できます。
int main(){
double * ptr = new double;
ptr[1] = 100.21;
cout<<*(ptr+1)<<endl;
return 1;
}
// 100.21
#include <iostream>
using namespace std;
const int MAX = 3;
int main ()
{
int var[MAX] = {10, 100, 200};
int *ptr;
// 指针中最后一个元素的地址
ptr = &var[MAX-1];
for (int i = MAX; i > 0; i--)
{
cout << "Address of var[" << i << "] = ";
cout << ptr << endl;
cout << "Value of var[" << i << "] = ";
cout << *ptr << endl;
// 移动到下一个位置
ptr--;
}
return 0;
}
Address of var[3] = 0xbfdb70f8
Value of var[3] = 200
Address of var[2] = 0xbfdb70f4
Value of var[2] = 100
Address of var[1] = 0xbfdb70f0
Value of var[1] = 10
int *pt = new int [10];
*pt = 5;
pt[0] = 6;
pt[9] = 44;
int coats [10];
* (coats +4) = 99;
//解释:
//coats表示数组第一个元素地址,+4则地址增加4个int内存空间,即移动到coats[4]的位置
//*(coats+4) 和 coats[4] 等价
差別:
short (*pas)[20];
short *pas[20];
int *ar2[4];
int (*ar2)[4];
//解析
//牢记优先级() [] *
short (*pas)[20]; //pas是指针,指向由20个short元素组成的数组的指针
short *pas[20]; //pas是个数组,包含了20个指向short类型指针的数组
int *ar2 [4]; //ar2是个数组,表示由4个int指针组成的数组
//-1-参考int arr[4];表示arr是个int数组,长度为4
//-2-int* ar2[4];表示ar2是个int*数组,长度为4,只不过元素都是只想int的指针
int (*ar2) [4];//ar2是个指针,指向一个包含了4个int的数组
//-1-先看括号里面,(*ar2),表示ar2是个指针
//-2-再看括号外面,int ()[4];表示名为()的int数组,长度为4
ポインタと定数
- constは定数を意味します。ポインターの場合、2つの目標があります。1つはポインターによって格納されたアドレス値であり、もう1つはポインターが指す変数値です。
- 栗を直接引用する
// NO.1 right
int age = 30;
const int * p = &age; //无法通过p修改age,age自身可修改
// NO.2 right
const int age = 30;
const int * ptr = &age; //无法通过ptr修改age,age自身不可修改
// NO.3 false
const int age = 30;
int *ptr = &age; //可通过ptr修改age,age自身不可修改,矛盾,ERROR
- 次に、主にポインタ定数、ポインタ変数、および定数と変数の関係の4つのタイプを分析します。
- int型のエンティティはconstintで初期化できません。その逆も同様です。
#include <iostream>
using namespace std;
int main() {
/*当对象是变量或者常量时,思考const的存在或不同位置,能否导致指针自身和指针指向的对象 改变?*/
int num = 10; //当对象是int变量
int test = 1;
int * num_ptr0 = # // TRUE, *num_ptr0,可修改,num_ptr0可修改,num可修改
const int * num_ptr1 = # // TRUE, *num_ptr1常量不可修改,num_ptr1可修改,num可修改
int * const num_ptr2 = # // TRUE, *num_ptr2可以修改, num_ptr2不可修改,num可修改
const int* const num_ptr3 = # // TRUE, *num_ptr3常量不可修改, num_ptr3常量不可修改,num可修改
const int price = 100; //当对象是int常量
int * price_ptr0 = &price; //ERROR, 指针指向类型和指向对象类型不匹配, nt类型的实体不能用const int来初始化,反之可以
const int *price_ptr1 = &price; //TRUE,
int * const price_ptr2 = &price; //ERROR, 指针指向类型和指向对象类型不匹配, int类型的实体不能用const int来初始化,反之可以
const int* const price_ptr3 = &price; //TRUE
return 0;
}
- 変数が宣言されているときに、割り当てる正確なアドレスがない場合は、ポインター変数にNULL値を割り当てることをお勧めします。NULL値が割り当てられたポインターは、NULLポインターと呼ばれます。
- nullポインターをチェックするには、ifステートメントを使用できます。
#include <iostream>
using namespace std;
int main ()
{
int *ptr = NULL;
cout << "ptr 的值是 " << ptr ;
return 0;
}
ptr 的值是 0
if(ptr) /* 如果 ptr 非空,则完成 */
if(!ptr) /* 如果 ptr 为空,则完成 */
関数ポインタ
機能へのポインタ
- 関数のアドレスを取得します
- 次のように、関数名を直接使用します(関数名と関数の戻り値の違いに注意してください)。thinkは関数名、think()は関数の戻り値です。
process(think);
process(think());
- 関数ポインタを宣言し、割り当てを初期化します
- 優先度に注意してください()[] *順序は高いものから低いものへです
- 関数の戻り値の型とシグネチャを指定する必要があります(次のように、関数によって宣言された関数名をポインタに直接置き換えます)
double pam(int);
double (*f)(int);
f = pam;
- 関数ポインタ呼び出し
- 関数名pamとして(* f)またはfを直接使用できます。どちらも問題ありません。(* f)の括弧()に注意してください。
double pam(int);
double (*f)(int);
f = pam;
double x = pam(4);
double y = (*f)(4);
double z = f(4);
- 差別
double pam(int);
double *f1(int);
double (*f2)(int);
int arr[10];
int *p = &arr[0];
int *a = arr;
int *b = &arr;
int *c[10] = &arr;
int (*d)[10] = &arr;
上記のコードでは:
f1は関数であり、戻り値の型はdouble *ポインターです。
f2は関数を指すポインターであり、型はdouble(*)(int);です。
pは、配列arrの要素0の最初のアドレスへのポインタです。
aはpと同じポインタです(配列名arrは要素0の最初のアドレスです)
bはポインタです。aと同じ値ですが、配列メモリブロック全体の最初のアドレスを指します。bが+1演算を実行すると、配列メモリ全体のバイト数が増加します。
cは配列であり、10個のint *ポインタ要素を含む配列です。
dは、10個のint要素を含む配列を指すポインターです。
関数ポインタの詳細な研究
関数プロトタイプ
関数プロトタイプでは、シグニチャまたはパラメータリストには次のルールがあります。
- arr []は* arrと同等です
- 識別子constdouble ar []は省略して、const double []に簡略化できます。constdouble* arrはconstdouble *と同じです。
- 関数定義では省略できません!
関数ポインタを含む配列
const double * (*pa[3])(const double *,int) = {f1,f2,f3};
ステートメントの記述のアイデア:最初に戻り値の型とシグネチャを記述しますconst double *()(const double *、int)次に、ポインター* pa [3]を含む配列式を()に入力します
関数ポインタを含む配列へのポインタ
const double * (*(*pa)[3])(const double *, int) = &pa;
ステートメントの記述のアイデア:最初に戻り値の型と署名const double *()(const double *、int)を記述し、次にポインター要素を含む配列にポインター式を追加します*(* pa)[3]