【c/c++】constとポインタの関係

## 前置き

constとポインタの関係について調べたので、その記録を残します。

実験などしなくとも、今はネット上でいくらでも解説サイトがあります。

しかしながら今回はあえて実験し、そこから法則性を求めることにした。



## 実際にコンパイルし、エラーとなる書き方を探る

まず、下記のソースをコンパイルして、どこでエラーが発生するか試す。



*source : ptr_cnst.cpp*

```c++
#include <iostream>
#include <string>

int main()
{
        using std::literals::string_literals::operator""s;

        char str1_1[] = "char[] test1-1.";
        char str2_1[] = "char[] test2-1.";
        char str3_1[] = "char[] test3-1.";
        char str4_1[] = "char[] test4-1.";
        char str5_1[] = "char[] test5-1.";
        char str6_1[] = "char[] test6-1.";
        char str7_1[] = "char[] test7-1.";
        char str8_1[] = "char[] test8-1.";
        char str1_2[] = "char[] test1-2.";
        char str2_2[] = "char[] test2-2.";
        char str3_2[] = "char[] test3-2.";
        char str4_2[] = "char[] test4-2.";
        char str5_2[] = "char[] test5-2.";
        char str6_2[] = "char[] test6-2.";
        char str7_2[] = "char[] test7-2.";
        char str8_2[] = "char[] test8-2.";

        char *p_ch1 = str1_1;
        const char *p_ch2 = str2_1;
        char const*p_ch3 = str3_1;
        char *const p_ch4 = str4_1;
        const char const * p_ch5 = str5_1;
        char const *const p_ch6 = str6_1;
        const char *const p_ch7 = str7_1;
        const char const *const p_ch8 = str8_1;

        p_ch1 = str1_2;
        p_ch2 = str2_2;
        p_ch3 = str3_2;
        p_ch4 = str4_2;
        p_ch5 = str5_2;
        p_ch6 = str6_2;
        p_ch7 = str7_2;
        p_ch8 = str8_2;

        *p_ch1 = '@';
        *p_ch2 = '@';
        *p_ch3 = '@';
        *p_ch4 = '@';
        *p_ch5 = '@';
        *p_ch6 = '@';
        *p_ch7 = '@';
        *p_ch8 = '@';

        std::cout << p_ch1 << std::endl;
        std::cout << p_ch2 << std::endl;
        std::cout << p_ch3 << std::endl;
        std::cout << p_ch4 << std::endl;
        std::cout << p_ch5 << std::endl;
        std::cout << p_ch6 << std::endl;
        std::cout << p_ch7 << std::endl;
        std::cout << p_ch8 << std::endl;
}
```



下記のコンパイルエラーが発生しました。

```
$ g++ -std=c++20 -Wall -pedantic-errors ./ptr_cnst.cpp -o ./ptr_cnst.exe
./ptr_cnst.cpp: In function 'int main()':
./ptr_cnst.cpp:29:13: error: duplicate 'const'
   29 |  const char const * p_ch5 = str5_1;
      |             ^~~~~
      |             -----
./ptr_cnst.cpp:32:13: error: duplicate 'const'
   32 |  const char const *const p_ch8 = str8_1;
      |             ^~~~~
      |             -----
./ptr_cnst.cpp:37:8: error: assignment of read-only variable 'p_ch4'
   37 |  p_ch4 = str4_2; // error: assignment of read-only variable 'p_ch4'
      |  ~~~~~~^~~~~~~~
./ptr_cnst.cpp:39:8: error: assignment of read-only variable 'p_ch6'
   39 |  p_ch6 = str6_2;
      |  ~~~~~~^~~~~~~~
./ptr_cnst.cpp:40:8: error: assignment of read-only variable 'p_ch7'
   40 |  p_ch7 = str7_2;
      |  ~~~~~~^~~~~~~~
./ptr_cnst.cpp:41:8: error: assignment of read-only variable 'p_ch8'
   41 |  p_ch8 = str8_2;
      |  ~~~~~~^~~~~~~~
./ptr_cnst.cpp:44:9: error: assignment of read-only location '* p_ch2'
   44 |  *p_ch2 = '@'; // error: assignment of read-only location '* p_ch2'
      |  ~~~~~~~^~~~~
./ptr_cnst.cpp:45:9: error: assignment of read-only location '* p_ch3'
   45 |  *p_ch3 = '@'; // error: assignment of read-only location '* p_ch3'
      |  ~~~~~~~^~~~~
./ptr_cnst.cpp:47:9: error: assignment of read-only location '* p_ch5'
   47 |  *p_ch5 = '@';
      |  ~~~~~~~^~~~~
./ptr_cnst.cpp:48:9: error: assignment of read-only location '*(const char*)p_ch6'
   48 |  *p_ch6 = '@';
      |  ~~~~~~~^~~~~
./ptr_cnst.cpp:49:9: error: assignment of read-only location '*(const char*)p_ch7'
   49 |  *p_ch7 = '@';
      |  ~~~~~~~^~~~~
./ptr_cnst.cpp:50:9: error: assignment of read-only location '*(const char*)p_ch8'
   50 |  *p_ch8 = '@';
      |  ~~~~~~~^~~~~
```



## エラー箇所をコメントアウトし、コンパイルを通す

下記のようにエラー行をコメントアウトするとコンパイルが通った。



*source : ptr_cnst.cpp*

```c++
#include <iostream>
#include <string>

int main()
{
        using std::literals::string_literals::operator""s;

        char str1_1[] = "char[] test1-1.";
        char str2_1[] = "char[] test2-1.";
        char str3_1[] = "char[] test3-1.";
        char str4_1[] = "char[] test4-1.";
//      char str5_1[] = "char[] test5-1.";
        char str6_1[] = "char[] test6-1.";
        char str7_1[] = "char[] test7-1.";
//      char str8_1[] = "char[] test8-1.";
        char str1_2[] = "char[] test1-2.";
        char str2_2[] = "char[] test2-2.";
        char str3_2[] = "char[] test3-2.";
        char str4_2[] = "char[] test4-2.";
//      char str5_2[] = "char[] test5-2.";
        char str6_2[] = "char[] test6-2.";
        char str7_2[] = "char[] test7-2.";
//      char str8_2[] = "char[] test8-2.";

        char *p_ch1 = str1_1;
        const char *p_ch2 = str2_1;
        char const*p_ch3 = str3_1;
        char *const p_ch4 = str4_1;
//      const char const * p_ch5 = str5_1;      // error: duplicate 'const'
        char const *const p_ch6 = str6_1;
        const char *const p_ch7 = str7_1;
//      const char const *const p_ch8 = str8_1; // error: duplicate 'const'

        p_ch1 = str1_2;
        p_ch2 = str2_2;
        p_ch3 = str3_2;
//      p_ch4 = str4_2; // error: assignment of read-only variable 'p_ch4'
//      p_ch5 = str5_2;
//      p_ch6 = str6_2; // error: assignment of read-only variable 'p_ch6'
//      p_ch7 = str7_2; // error: assignment of read-only variable 'p_ch7'
//      p_ch8 = str8_2;

        *p_ch1 = '@';
//      *p_ch2 = '@';   // error: assignment of read-only location '* p_ch2'
//      *p_ch3 = '@';   // error: assignment of read-only location '* p_ch3'
        *p_ch4 = '@';
//      *p_ch5 = '@';
//      *p_ch6 = '@';   // error: assignment of read-only location '*(const char*)p_ch6'
//      *p_ch7 = '@';   // error: assignment of read-only location '*(const char*)p_ch7'
//      *p_ch8 = '@';

        std::cout << p_ch1 << std::endl;
        std::cout << p_ch2 << std::endl;
        std::cout << p_ch3 << std::endl;
        std::cout << p_ch4 << std::endl;
//      std::cout << p_ch5 << std::endl;
        std::cout << p_ch6 << std::endl;
        std::cout << p_ch7 << std::endl;
//      std::cout << p_ch8 << std::endl;
}
```



実行結果：

```
$ g++ -std=c++20 -Wall -pedantic-errors ./ptr_cnst.cpp -o ./ptr_cnst.exe
./ptr_cnst.cpp: In function 'int main()':
./ptr_cnst.cpp:19:7: warning: unused variable 'str4_2' [-Wunused-variable]
   19 |  char str4_2[] = "char[] test4-2.";
      |       ^~~~~~
./ptr_cnst.cpp:21:7: warning: unused variable 'str6_2' [-Wunused-variable]
   21 |  char str6_2[] = "char[] test6-2.";
      |       ^~~~~~
./ptr_cnst.cpp:22:7: warning: unused variable 'str7_2' [-Wunused-variable]
   22 |  char str7_2[] = "char[] test7-2.";
      |       ^~~~~~

$ ./ptr_cnst.exe
@har[] test1-2.
char[] test2-2.
char[] test3-2.
@har[] test4-1.
char[] test6-1.
char[] test7-1.
```



## 実行結果まとめ

先ほどの実行結果を表にまとめると下記のようになる。

| constの付け方                   | 変数の定義 | ポインタの指す先<br>("アドレス")を変更 | ポインタの指す先の<br>"値"を変更 |
| ------------------------------- | ---------- | ---------------------------------- | ---------------------------- |
| `char *p_ch1`                   | 可能       | 可能                               | 可能                         |
| `const char *p_ch2`             | 可能       | 可能                               | **不可能**                   |
| `char const*p_ch3`              | 可能       | 可能                               | **不可能**                   |
| `char *const p_ch4`             | 可能       | **不可能**                         | 可能                         |
| `const char const * p_ch5`      | **不可能** | ―                                  | ―                            |
| `char const *const p_ch6`       | 可能       | **不可能**                         | **不可能**                   |
| `const char *const p_ch7`       | 可能       | **不可能**                         | **不可能**                   |
| `const char const *const p_ch8` | **不可能** | ―                                  | ―                            |



## constとポインタの関係【結論】

| constの位置 | 動作                                           |
| ----------- | ---------------------------------------------- |
| *の前       | ポインタの指す先の**"値"**の変更が不可         |
| *の後       | ポインタの指す先（**"アドレス"**）の変更が不可 |

つまりconstが*の後にあるか、前にあるかで動作が変わるということである。