1. ホーム
  2. C

ポインタ定数および定数ポインタ

2022-02-14 06:14:47
<パス
  • const char *p, char * const p, const char * const p の違いについて

1. const
  constはconstantの略記です。変数の前にconstが付くと、その変数内のデータはアクセス可能であり、変更できないことを意味します。つまり、constは"read-only"を意味します。変数を変更しようとすると、コンパイルエラーになります。constは、コンパイラがコンパイル時にチェックを行うことで確保されます(つまり、const変数が変更できないのは、実行時エラーではなくコンパイルエラーとなります)。そのため、コンパイラを騙す方法を見つければ、実行時エラーにならずにconst定義された定数を変更することができるのです。

ルールです。

  • constが近くにある人は、改造できない。
  • 定数は定義された後では変更できないため、const を使って変数を定義する場合は初期化する必要があります。
#include <stdio.h>  
int main(void) 
{ 
    int i = 10;    
    int j = 20; 
    const int *ptr = &i;  
   
    printf("ptr: %d\n", *ptr);  
    *ptr = 100; /* error: object pointed cannot be modified using the pointer ptr */
   
    ptr = &j; /* valid */ 
    printf("ptr: %d\n", *ptr); 
    return 0; 
} 


2.定点
  ポインタを宣言するとき、キーワードconstは型の前でも後でも、また両方の位置で使うことができます。定義の形式は次の3つです。

  • 定数へのポインタ
    • const int *ptr;
    • int const *ptr;
  • 変数への定数ポインタ
    • int *const ptr;
  • 定数ポインタ
    • const int *const ptr;

個々の意味は以下の通りです。

  • const int * pOne;
        ptrが指す値は変更できないが、ポインタ自体は変更できる。
    • int * const pTwo;
          ポインタpを変更することはできませんが、ptrが指す値を変更することは可能です。
      • const int *const pThree;
            ptr が指す値もポインタ ptr も変更できない。

        3つの文章を覚えるために記憶を深めましょう。

        ポインタとconstの前に誰が先に読むか。
        * はアドレス、const は内容を表します。
        前にいる人は変更不可。

        <ブロッククオート


        int const *p1 = &b; //前面にconstがあり、定数ポインタとして定義されています。
        int *const p2 = &c; // *先行、ポインタ定数として定義されます。

        ポインタ定数 は定数へのポインタです。その名の通り、定数へのポインタです。つまり、変数を指すことはできず、指すものは変更できず、ポインタによって指すものは変更できませんが、ポインタ自体は定数ではなく、別の定数を指すようにそれ自体の値を変更することができます。

        ポインタ定数 は、ポインタそのものが定数であることを意味します。それが指すアドレスは不変ですが、そのアドレスの内容はポインターによって変更することができます。それが指すアドレスは、その寿命が尽きるまで生き続けます。注意点としては、ポインタ定数は定義と同時に初期値を割り当てる必要があることです。

        ケース1:ポインタbが指すアドレスの内容は変更できないが、ポインタbは他のアドレスを指すことができる。

        #Change the contents of the address pointed to by pointer b
        int main()
        {
        	int a = 2;
        	int const *b = &a;
        	*b = 3; // report error: expression must be a modifiable lvalue	
        	printf("albert:%d\n",a);
        }
        ----------------------------------------------------------------------------
        #Change the pointer b to
        int main()
        {
        	int a = 2;
        	int b = 3;
        	int const *c = &a;
        	printf("albert:%p\n", c);    
        	c = &b;
        	printf("albert:%p\n",c);
        }
        
        
        

        ケース2:ポインタが指すアドレスは再割り当てできないが、中身は変更できる場合。

        int main()
        {
        	int a = 2;
        	int b = 3;
        	int *const c = &a;
        	printf("albert:%p\n", c);
        	c = &b; //rare: expression must be a modifiable lvalue	
        	printf("albert:%p\n",c);
        }
        --------------------------------------------------------------------------
        int main()
        {
        	int a = 2;
        	int b = 3;
        	int *const c = &a;
        	*c = 4;
        	printf("albert:%d\n",*c); //4
        }
        
        
        

        3. C言語におけるポインタと配列の違い
          これまで見てきたように、配列を宣言すると、配列のセルに対して連続したメモリブロックが確保され、(適切な型の)ポインタセルが作成されます。 このポインタセルには配列の名前が与えられます。配列のセルにメモリが割り当てられると 配列のセルにメモリが割り当てられると 開始アドレスは固定です。 つまり、それは 変更不可 したがって、ポインタ・セルの値を変更してはならない。このポインタを変更しないようにするために、C言語では配列名を変数として使ってはいけません。 変数として使ってはいけません。 左側 として使用することはできません。 L値 . その代わり、必要であれば、適切な型の別のポインタ変数を宣言し、L値として使用することができる。

        #include <stdio.h>
        
        int main()
        {
        	int a[] = { 1,5,10,20 };
        	int b = *a++; //*a++ is the same as *(a++) 
        	printf("b = %d\n",b);    
        }
        
        
        

        上に示すように、コンパイルはエラーを報告します:配列名が定数ポインタであるため、a++を実行できないので、変更を行います。

        参考
        http://ee.hawaii.edu/~tep/EE160/Book/chap7/subsection2.1.3.2.html
        https://www.geeksforgeeks.org/difference-const-char-p-char-const-p-const-char-const-p/