C/C++心得-理解ポインタ

29687 ワード

前回筆者は、メモリとCの基本データ型について、あまりよく理解できない論理で紹介しましたが、今は自分が学んだことに基づいてC言語のポインタについてお話しします.
ポインタを理解してこそ、本当にC言語の入門を計算することができます.私が大学時代にフロントエンドのUEに注目しすぎたのかもしれませんが、当初C言語を勉強し始めたときはあまりまじめではなかったのかもしれません.卒業後のある日になって、私はやっと「わかる」指針を理解して、やっとC言語の独特さを理解します.もしC言語を初めて勉強した同業者がポインタに困惑していたら、私のこの浅い認識があなたを助けることができることを望んでいます.
1、紹介
ポインタは元の英語ではpointerで、個人的には翻訳した後の針の意味は指の意味より理解しやすいと思います.pointerはインジケータに翻訳することもできますが、初心者であれば、学習の過程で指の意味をよく考えることをお勧めします.
ポインタは一種の特殊なデータ型と言えるが,前述のメモリ編で紹介したように,プログラム実行時のデータは基本的にメモリに格納され,メモリには異なるデータをデジタルで識別し,各種のコンピュータ言語では,このデジタル識別をアドレスとする.コンピュータシステムはC言語を認識していないが、プログラムコンパイルはC言語をオペレーティングシステムが認識できる言語に翻訳することである.例えば「私」は英語をIに訳し、翻訳しても「私」という字は見えなくなった.プログラムでは整数変数iを定義し,5を付与すると,コンパイル運転を経てオペレーティングシステムにもiが見えず,オペレーティングシステムにとってiはあるメモリアドレス上の整数値に翻訳される.
2、基本使用
ポインタを使用するには、まず2つの記号を認識します.1つは'&'で、プログラムで変数のメモリアドレスを取得できます.1つは'*','*'変数を定義するときにその変数をポインタと表記し、定義した変数の前で使用するときに、そのポインタ変数が格納されているメモリアドレスに格納されている値を取得(設定)することを示します.
 1 #include <stdio.h>

 2 

 3 int main(int arg, char * args[])

 4 {

 5     int i = 5; //       i    5

 6     int * srcI = &i; // &      ,  i     ,    int *         srcI

 7     printf("srcI:%d,%X
", srcI, srcI); // , 16 8 printf("*srcI:%d
", *srcI); // , * , 9 // * , * 10 *srcI = 7; 11 printf("i:%d
", i); // 12 getchar(); // 13 return 0; 14 }

上記のコードの実行結果は次のとおりです(プログラムの実行時に変数のメモリアドレスが異なる場合があります).
1 srcI:7665592,74F7B8

2 *srcI:5

3 i:7

筆者は理解ポインタの重点は'*'という記号を理解することにあると考えているが,ここで強調すると,変数を定義する際に,'*'は後に定義される変数がポインタタイプであることを示し,定義されたポインタの前'*'は後に変数が格納されているメモリアドレスに実際に格納されている値を表し,専門的には後に変数が指す値を取得(設定)することである.「*」の意味を理解していないときに犯す間違いを以下に挙げます.
 1 #include <stdio.h>

 2 

 3 int main(int arg, char * args[])

 4 {

 5     int i = 5, j = 6;

 6     int * src = &i; 

 7     *src = &j; //     src    i     j   ,      *   

 8     printf("*src:%d
", *src); 9 printf("i:%d
", i); // i 10 getchar(); 11 return 0; 12 }

上記のコードコンパイル運転はいずれもエラーを報告していないが,srcに格納されているアドレスを変数jのメモリアドレスに変更しようとしたが,実際には変数iを設定する値となり,これが'*'エラーに対する理解の結果となる.このコードはまた、7行目のアドレスが整数変数に値を割り当てることができることを説明しています.これは、メモリに異なるデータを数字で識別し、このデータ識別がメモリアドレスであることを確認しています.
次にconstポインタをコードで説明します
 1 #include <stdio.h>

 2 #include <stdlib.h>

 3 

 4 int main(int arg, char *args)

 5 {

 6     int num = 5;

 7     const int * p1 = &num; // *   const int   int const   ,                    

 8     int * const  p2 = &num; //

 9     const int * const p3 = &num; //                 

10     p1 = NULL;

11     //* p1 = 0; //   

12     //p2 = NULL;//   

13     *p2 = 0;

14     //p3 = NULL;//   

15     //*p3 = 0; //   

16     return 0;

17 }

 
3、ポインタの高さ
以前は例として整数型しか使用されていなかったが、実際にはchar、float、doubleなどのすべてのデータ型のアドレスを取得するために'&'を使用することができる.ポインタを'*'で定義する場合もchar*,float*,double*といったポインタタイプを定義することができ,異なるポインタタイプがデータを読み出す際の違いはアドレスを解釈する方法が異なることにある.int*タイプの場合、定義後に*を使用して値をとると、int*が表すアドレスからsizeof(int)ビットアドレスのデータが取得され、intでデータが解析され、他のデータタイプが使用されます.
ポインタタイプはすべてのデータ型のアドレスを格納できますが、ポインタはポインタのアドレスを格納できますか?確かにいいです.多段ポインタの問題について、ここでは適切ではない例を挙げるかもしれません.クラスの授業でペンが必要です.そこで、あなたは同じテーブルに、同じテーブルにtaの後ろの人がペンを持っていると言って、それから同じテーブルの後ろの人に、同じテーブルの後ろの人にtaのそばの人がいると言って、そこであなたはまた同じテーブルの後ろのそばの人に......必要なら、このシーンはループできます.マルチレベルポインタは大体このような意味で、あなたはこのアドレスを読んで中がまだアドレスであることを発見して、そこで更に中のアドレスを読みます......もちろん現実にはこのようなペンを借りて半日借りる問題に遭遇するのは間違いないが、コンピュータのポインタにとっては、1つの記号の問題にすぎない.簡単な例を次に示します.
 1 #include <stdio.h>

 2 

 3 int main(int arg, char * args[])

 4 {

 5     int i = 6;

 6     int * src = &i;

 7     int ** srcsrc = &src; //           *,  &

 8     int *** srcsrcsrc = &srcsrc;

 9 

10     printf("*src:%d
", *src); 11 printf("**srcsrc:%d
", **srcsrc); 12 printf("***srcsrcsrc:%d
", ***srcsrcsrc); 13 14 // 15 *src = 8; 16 printf("i:%d
", i); 17 **srcsrc = 10; 18 printf("i:%d
", i); 19 ***srcsrcsrc = 12; 20 printf("i:%d
", i); 21 getchar(); 22 return 0; 23 }

実行結果は次のとおりです.
*src:6

**srcsrc:6

***srcsrcsrc:6

i:8

i:10

i:12

4、ポインタ運用
こんなにたくさん言ったのに、ポインタは実際に使って何の役にも立たない.まず、ポインタが読み取ったのはオペレーティングシステムのメモリアドレスで、すべてのプログラムのデータはシステムのメモリに存在していることを発見しました.もし彼らのすべての値を読み取ることができれば、メモリアドレスを通じてシステムの他のプログラムのデータを修正することができます.これはモディファイヤ、外挂けの一部の原理で、実際にはどうやってアドレスを探すかを考えなければなりません.システムのメモリ保護(注入)方法.
まずポインタと配列について説明します.容量6の整数配列int array[6]を定義するが、このときarrayはポインタであり、そのアドレスはその配列の最初の要素のアドレスである.配列とポインタについては、次のコードとそのコメントを参照してください.
 1 #include <stdio.h>

 2 

 3 int changeArr(int * arr)

 4 {

 5     arr[0] = 20; //           

 6     *(arr + 1) = 40; //           

 7 }

 8 

 9 int main(int arg, char *args)

10 {

11     int i = 0;

12     int array[6] = { 1, 2, 3, 4, 5, 6 };

13     printf("*array:%d
", *array); // * 14 15 // 16 printf("array:"); 17 for (i = 0; i < sizeof(array) / sizeof(int); i++) 18 { 19 printf("%3d ", array[i]); 20 } 21 printf("
"); 22 // 23 printf("array:"); 24 for (i = 0; i < sizeof(array) / sizeof(int); i++) 25 { 26 printf("%3d ", *(array + i)); 27 } 28 printf("
"); 29 30 // 31 changeArr(array); 32 // 33 printf("after change
array:
"); 34 for (i = 0; i < sizeof(array) / sizeof(int); i++) 35 { 36 printf("%3d ", *(array + i)); 37 } 38 printf("
"); 39 getchar(); 40 return 0; 41 }

実行結果は次のとおりです.
1 *array:1

2 array:  1   2   3   4   5   6

3 array:  1   2   3   4   5   6

4 after change

5 array: 20  40   3   4   5   6

上記の例は基本的にスタック上のメモリ空間であり、変数を直接定義するときにスタック上のメモリ空間を申請し、実際のプログラムで使用を申請できるスタック空間は小さいので、大きなデータを処理するときは、スタック上のメモリを手動で申請する必要があります.C言語では、手動で申請するにはいくつかのC言語関数を理解する必要があります.ここでは、malloc、calloc、realloc、free、alloca.
ここではまずmallocとfreeの2つの関数を例に挙げます(データ型メモリ空間に疑問があるのは、前のメモリのエッセイを参照してください):
 1 #include <stdio.h>

 2 #include <stdlib.h>

 3 

 4 int main(int arg, char *args)

 5 {

 6     // malloc      void *malloc(size_t size);   size_t      ,      ,               (void *)

 7     //             0,C   NULL  0   。

 8     int * a = malloc(sizeof(int)); // sizeof                      ,           

 9     if (a == NULL)

10     {

11         //       ,         ,                 ,         

12         printf("malloc failed
"); 13 return -1; 14 } 15 *a = 5; 16 printf("%d,%X
", *a, a); // a a 17 18 if (a != NULL) 19 { 20 free(a); // free 21 // , , 22 // NULL 23 a = NULL; 24 } 25 26 getchar(); // 27 return 0; 28 }

実行結果:
5,1099538

他のメモリ割り当て関連関数はそれほど多くありませんが、ここでは概要のみを記録します.
realloc:割り当てられたメモリのポインタを再割り当てし、freeがメモリを手動で解放して適用する必要があります.たとえば、割り当てられたメモリが小さすぎる可能性があります.この場合、reallocを使用して新しいメモリ領域を申請し、前のメモリ領域をコピーします.新しい領域が元の領域より小さい場合、データが失われます.C言語の動的配列(可変容量)としてreallocを使用できます.
プロトタイプ:void*realloc(void*mem_address,unsigned int newsize);
calloc:2つの数の積の大きさの空間を申請し、freeが手動でメモリを解放する必要があります.例えば、6つの要素を含む整数配列空間を申請したいです.calloc(6,sizeof(int);
プロトタイプ:void*calloc(size_t n,size_t size);
_alloca:スタックでスペースを申請し、使い終わったら自動的に解放する
プロトタイプ:void*_cdecl _alloca(size_t);
 
二次ポインタ応用と三次ポインタ応用は一般的にダイナミックライブラリを開発する際に用いられるが,興味のあるものは自分で関連資料を調べることができ,本稿では後述する.
5、関数ポインタ
一般的に、一人で関数のポインタを使ってそのレベルが入門から熟練に向かっていることを説明することができて、筆者自身が使っている基本はすべていくつかのシステムの呼び出しの下でやっと使って、一般的にコールバックに使って、ここで引子をして、簡単にその定義と使用を言います.下の図のコードとコメントを見てください.
 1 #include <stdio.h>

 2 

 3 void func()

 4 {

 5     printf("hello func pointer
"); 6 } 7 8 float addFloat(float a, float b) 9 { 10 return a + b; 11 } 12 13 14 int main(int arg, char *args[]) 15 { 16 void (*f)() = func; // 17 float(*addf)(float a, float b) = addFloat; // 18 f(); // 19 printf("%.1f+%.1f=%.1f
", 3.0f, 4.0f, addf(3.0f, 4.0f)); 20 getchar(); 21 return 0; 22 }

実行結果は次のとおりです.
1 hello func pointer

2 3.0+4.0=7.0

私はポインタの部分について理解して運行してしばらくここに着いて、もし問題があればまたみんなに指摘してもらって、ありがとうございます!