C/C++ポインタを深く理解する(二)

6763 ワード

針に大きなメモをとりましょう.ちょうどお役に立てば幸いです.文章名は『C/C++ポインタを深く理解する(一)』、『C/C++ポインタを深く理解する(二)』と呼ばれ、必要なブロガーは連ここで、郭有強、譚浩強の2人の教授のC++書籍とネットユーザーのすばらしい文章にも感謝します.(二)の目次は以下の通りである.
  • ポインタ配列
  • ポインタを指すポインタ
  • ポインタと関数
  • constポインタ
  • 1ポインタ配列2 D配列ポインタ変数(
    単一変数→二次元配列):int(*p)[列数](指向,p=a;変数pにより独自のアドレスを開き,&p≠a/p(配列名すなわちアドレスa=&aに注意)).
    ポインタ配列(
    配列、その要素は1つのポインタ変数格納アドレス):int*pa[4]//(pa→pa[4]→*pa[4]).例えば、ポインタ配列は2次元配列を指す:各要素は各行を指す(pa[i]=a[i]);ポインタ配列は文字列のセットを指します.各要素は文字列を指します.【一般配列は「値」を格納し、ポインタ配列は「アドレス」を格納する.ポインタ配列&元配列は2つの異なる配列であり、pa=&pa≠a=&a】
    1.1数値型ポインタ配列
    	int a = 1, b = 2, c = 3;
    	int *p[3] = { &a,&b,&c }; //     
    	cout << "       ,   :" << p[0] << endl;
    	cout << "           :" << *p[0] << "=" << *(*(p + 0) + 0) << endl;
      。  :p[i]    ; :*p[i]    ;   :  。
    ////////////////////////////////////////////////////////////////////////////////////////////////////////
    	int a[3][3] = { 1,2,3,4,5,6,7,8,9 };
    	int *pa[3] = { a[0],a[1],a[2] }; //    :        
    	for (int i = 0;i < 3;i++)
    		cout << pa[i] << "=" << a[i] << "," << *pa[i] << "=" << *(*(pa + i) + 0) << "=" << a[i][0] << endl;
    	for (int i = 0;i < 3;i++)
    	{
    		for (int j = 0;j < 3;j++)
    			cout << pa[i][j] << "=" << *(*(pa + i) + j) << "=" << a[i][j] << "   ";
    		cout << endl;
    	}
    	cout << pa << "=" << &pa << "≠" << a << "=" << &a << endl; //          
    ///////////////////////////////////////////////////////////////////////////////////////////////////////
    	int a[3][3] = { { 1,2,3 },{ 4,5,6 } ,{7,8,9} };
    	int(*p)[3] = a; //        
    	for (int i = 0;i < 3;i++)
    		cout << p[i] << "=" << a[i] << endl;
    	for (int i = 0;i < 3;i++)
    	{
    		for (int j = 0;j < 3;j++)
    			cout << p[i][j] << "=" << *(*(p + i) + j) << "=" << a[i][j] << "   ";
    		cout << endl;
    	}
    	cout << p << "=" << a << "=" << &a << "≠" << &p << endl; //      
    
    

    総括する.ただ黙って言いたいのは、ポインタ配列が2次元配列のポインタ変数に「何に近いのか」ということです.しかし、前者:一連の変数、2次元配列(すなわち、2次元配列のみを指す)を指すことができ、1つの配列(通常の配列とは異なり、アドレスを格納する)である.後者:2 D配列(固有)のみを指す変数です.いずれも「新規」なので、メモリスペース(&p≠a=&a、pa=&pa≠a=&a)が開き、定義には定数が必要です.要するに、ポインタ配列は2 Dポインタ変数よりも一般的である(2 D配列に相当する)が、後者は前者よりもメモリ領域を節約する(sizeof( T*p)(n ))。1.2文字列型ポインタ配列(列不等2次元配列)
    	char *name[] = { "BASIC","FORTRAN","C+3","C#","C@@" };//      
    	for (int i = 0;i < 5;i++)
    	{
    		cout << "name[" << i << "]   :" << name + i << endl; //  name[i]    ,  name+i        (       )
    		cout << "name[" << i << "]  (  →  →   ):" << name[i] << endl;
    	}
    	cout << "       :" << name + 1 << "=" << &name[1] << endl; //  
    	cout << "       (  →    →   ):" << name[1] << "=" << *(name + 1) << endl; //FORTRAN
    	cout << "name[3]→'C#',      *(*(name+3)+1):" << *(name[3] + 1) << "=" << *(*(name + 3) + 1) << "=" << name[3][1] << endl; //#
    	cout << "name[4][3]   (  ,    ):" << name[4][3] << endl; // 
    
    

    ジルコニウムの本質は「不等な2次元配列」であり、2次元配列のポインタ理論に適応する.ただし、そのカラムアドレスname[i](全体的な役割)は、ある文字列(行アドレスname+iはアドレス→name[i])を表し、name[i][j](i,jは境界を越えることができる)はある文字列のある文字を表す. 添付:文字列グループへのポインタ配列を使用してソートアルゴリズムを書き込みます.
    //      (     )    
    void charSort(char *name[], int n)
    {
    	/*name[i]   ,        。     :(n-1)+...
    	(n-2)+...+2+1=(n-1)n/2=O(n²)。*/
    	char *temp;
    	int i, j, k;
    	for (i = 0;i < n - 1;i++) //       
    	{
    		k = i; //  :        
    		for (j = i + 1;j < n;j++) //     
    			if (strcmp(name[k], name[j]) > 0) //       (  )
    				k = j;
    		if (k != i) //       
    		{
    			//   “   ”        
    			temp = name[i];
    			name[i] = name[k];
    			name[k] = temp;
    		}
    	}
    }
    
    

    2ポインタへのポインタ
    2.1単一変数を指す場合
    	int **p1, *p2, n = 3;
    	p2 = &n; p1 = &p2;
    	cout << "*p2=n:" << *p2 << "=" << n << endl;
    	cout << "*p1=p2:" << *p1 << "=" << p2 << endl;
    	cout << "**p1=*(*p1)=*p2=n:" << **p1 << "=" << n << endl;
    
    

    2.2 2 D配列を指す場合
    ①a     **p:
    int a[3][4] = { 100,1,2,3,4,5,6,7,8,9,10,11 };
    	int ** p = (int **)a; /*            */
    	cout << "p:" << p << ", a:" << a << ", &a[0][0]:" << &a[0][0] << endl;//   
    	cout << "*p:" << *p << endl; //100(    )
    //cout << "**p:" << **p << endl; //  :   100   ( :*(100))
    
    

      まとめ:階層が少なくなり、pは直接a[0][0](本来は「行アドレス」レベルを指すべき)を指す.
    ②      :
    char *name[] = { "BASIC","FORTRAN","C+3","C#","C@@" };
    	char **p = name; //p+i=name+i(name+i       ,  name[i])
    	for (int i = 0;i < 5;i++)
    	{
    		for (int j = 0;j < strlen(p[i]);j++)
    			cout << *(*(p + i) + j); //p[i][j] 
    		cout << endl;
    }
    
    
    ③        :
    int a[2][3] = { 1,2,3,4,5,6 };
    	int **p, *q;
    	for (int i = 0;i < 2;i++)
    	{
    		q = a[i];p = &q;
    		for (int j = 0;j < 3;j++)
    			cout << *(*p + j) << " ";//p[0][j]
    		cout << endl;
    	}
    
    

      まとめ:2次元配列をポインタに付与するポインタは、「単一ポインタ変数」、「ポインタ配列」を借りて移行する必要があり、直接付与(T**p=a)はできません!
    3ポインタと関数
    3.1関数ポインタ(関数を指すポインタ変数)
    関数を呼び出します.
    関数タイプ(*変数名)(パラメータテーブル)関数タイプ(*変数名)(パラメータテーブル)(関数プロトタイプ宣言の関数名を「(*変数名)」のみで置き換え、int(*p)(int,int)など)(p→*p→(*p)(int, int)).また、定義時にint(*p)(int,int)=func;//funcはパラメータを持つことができません.関数名は関数のエントリアドレスを表します.
      関数ポインタは、別の関数のパラメータとして使用することができる(Cは、関数全体がパラメータとして使用できないが、実パラメータとして使用できることを規定する).double f 1(double){f 1の関数体}のように、
    double func(double a,double b,double(*p){funcの関数体},mainでfunc(a,b,f 1)を呼び出す.
    3.2ポインタ関数(ポインタ値を返す関数)  関数の戻り値はポインタ型データである.タイプ*関数名(パラメータテーブル).(一級、多段ポインタ可能)
    4 constポインタ
    int a=1,b=3;
    

      (1)定数を指すポインタ変数:ポインタでコンテンツをリフレッシュできないconst int*p=&a;//p=10; エラーですが、p=&bとa=10は、p=&b;エラーが発生しましたが、p=10が正しい  (3)定数のポインタを指します.コンテンツをリフレッシュできない以上、ポインタを変更することはできません.const int*const p=&a;/*p=10またはp=&bは間違っているが、a=10対の
    『C/C++ポインタを深く理解する(一)』が完成し、検閲をお待ちしております(▽)~~