6254


[How to initialize const members of structs on the
heap](https://stackoverflow.com/questions/2219001/how-to-initialize-const-
members-of-structs-on-the-heap) を参考に以下の様なプログラムを書きました。

[code]

    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    
    const int* make_immutable_point(int x){
      void *p = malloc(sizeof(const int));
      if (p == NULL) abort();
    
      const int temp = x;
      memcpy(p, &temp, sizeof(temp));
    
      return (const int*)p;
    }
    
    int main() {
        const int *f = make_immutable_point(10);
        *f=50; // エラー
        printf("%d,",*f);
    }
    
[/code]

メイン関数内で `*f=50;` のように記述するとエラーとなります。  
これは `const` として値が確保されているからなのでしょうが、

なぜ、このように記述すると動的に `const` が確保できているのかがわかりません。


CC BY-SA 3.0
2015-02-10T07:31:08.163
6245
2015-02-12T02:49:37.007
2017-05-23T12:38:55.307
-1
question
c
const に値が代入できるのはなぜですか？


malloc() の戻り値は void* で、そこに値を memcpy でセットしてるから。  
関数から戻す時に const int* にキャストして返しているので、受け取った方は変更不能。  
変更不可能な値を動的に作る場合に使われるテクニックですね。


CC BY-SA 3.0
2015-02-10T07:40:14.813
6247
2015-02-10T07:40:14.813
5005
6245
answer
~~`代入`ではなく`初期化`なので const int *f = make_immutable_point(10);
はOKです。実行する度に値を変えれるので`定数`と呼ぶのは抵抗があるかもしれませんが、この変数`f`が生存している間は値が変更できないので、`定数`と呼べそうです。~~

質問を読み違えてたので修正します。そもそも変数fは`定数`じゃなかった。。。

[code]

    const int *f;
    
[/code]

変数`f`はポインタです。`f`は`const int`な変数へのポインタ。なので`f`そのものは定数ではない。

つまり、次のコードはOKです。

[code]

    #include <stdio.h>
    
    int main() {
        const int v = 3;
        const int v2 = 4;
    
        const int *f = &v;
        printf("%d\n", *f);
    
        // 定数じゃないので代入はできる
        f = &v2;
        printf("%d\n", *f);
    
        // ポインタの先はconstなので変更できない
        // *f = 10;
        return 0;
    }
    
[/code]

`f`そのものを`const`にするには、次のように*のあとにもう一度`const`を付けます。

[code]

    #include <stdio.h>
    
    int main() {
        const int v = 3;
        const int v2 = 4;
    
        // 変数fはconst
        const int * const f = &v;
        printf("%d\n", *f);
    
        // 変数fはconstなので、代入しようとするとコンパイルエラー
        // f = &v2;
        return 0;
    }
    
[/code]


CC BY-SA 3.0
2015-02-10T07:41:08.633
6248
2015-02-10T08:01:34.023
2015-02-10T08:01:34.023
845
845
6245
answer


ポインタ変数そのものの変更を禁止する場合と、ポインタの指し示す先の変更を禁止する場合の２通りがあります。

const の指定を、*（アスタリスク）の前で指定する場合と、後で指定する場合の２つを書いて、以下２行の結果を比較してみてください。  
違いがわかると思います。

[code]

    *f=50; // 指し示す変数に代入
    f=0; // ポインタに代入
    
[/code]

Effective C++ に詳細な解説があったと思いますので、気になる場合は合わせて参照してください。  
「正確にはアスタリスクの前後とは言い切れないのだが、あまりにも複雑なのでそう理解して問題ない」とか面白い記載があったような記憶があります。


CC BY-SA 3.0
2015-02-10T08:08:03.550
6251
2015-02-10T08:08:03.550
4391
6245
answer


`const` は型への修飾子 (type qualifier) であり、`const`
を付けた変数の値が本当に変更不可能でなければならないわけではありません。示しているだけです。また、 `const`
キーワードは、キャストを利用することで、実質付けたり外したりすることができます。

[code]

    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    
    int const * make_immutable_point(int x){
      void *p = malloc(sizeof(const int));
      if (p == NULL) abort();
    
      const int temp = x;
      memcpy(p, &temp, sizeof(temp));
    
      // キャストして const を付け、返却
      return (int const *)p;
    }
    
    int main() {
    
        // 参照先の保護された変数
        int const *f = make_immutable_point(10);
        f = make_immutable_point(10); // 変数は保護されない。再度代入できる
        *f = 50; // ERROR!!!
        printf("f = make_...;   // => %d\n",*f);
    
        // 保護された変数
        int * const pf = (int * const)f;
        pf = make_immutable_point(10); // ERROR!!!
        *pf = 100;  // const で保護されない参照先を、値変更する
        printf("*pf = 100; // => %d\n",*f);
    
        // どちらも保護された変数
        int const * const ppf = (int const * const)f;
        ppf = make_immutable_point(10); // ERROR!!!
        *ppf = 200; // ERROR!!!
    
        // キャストで const 型を外し、値変更できる (危険！)
        int *vf = (int *)f;
        *vf = 400;
        printf("*vf = 400; // => %d\n",*f);
    
    }
    
[/code]

コンパイルすると以下のようなエラーが出力されます。

_Output_ : (Compiler)

[code]

    main.c: In function 'main':
    main.c:21:5: error: assignment of read-only location '*f'
         *f = 50; // ERROR!!!
         ^
    main.c:26:5: error: assignment of read-only variable 'pf'
         pf = make_immutable_point(10); // ERROR!!!
         ^
    main.c:32:5: error: assignment of read-only variable 'ppf'
         ppf = make_immutable_point(10); // ERROR!!!
         ^
    main.c:33:5: error: assignment of read-only location '*ppf'
         *ppf = 100; // ERROR!!!
         ^
    
[/code]

`// ERROR!!!` の行を削除して、コンパイル、実行すると、

_Output_ :

[code]

    f = make_...;   // => 10
    *pf = 100; // => 100
    *vf = 400 // => 400
    
[/code]

* * *

上記のコードの内で、気をつけるべき部分があります。

[code]

    int *vf = (int *)f;
    
[/code]

の行です。 `const` キーワードを外すことは、 [Undefined
Behaviour](https://www.securecoding.cert.org/confluence/display/seccode/BB.+Definitions#BB.Definitions-
undefinedbehavior) を引き起こす可能性をもたらし危険です。 ( 今回は、実体が `malloc(sizeof(const int))`
の結果であることが明白なため、問題が起こさないだけです。 )

  * [EXP05-C. Do not cast away a const qualification](https://www.securecoding.cert.org/confluence/display/seccode/EXP05-%20C.+Do+not+cast+away+a+const+qualification;jsessionid=EC0EE084CD0587B9623EDB3524E6C1C9) ([日本語訳](https://www.jpcert.or.jp/sc-rules/c-exp05-c.html))


CC BY-SA 3.0
2015-02-10T09:18:38.423
6254
2015-02-10T09:18:38.423
4978
6245
answer


C言語における`const`キーワードの働きについて、複数の話題が混在しているようですから、順番に解説してみます。

* * *

`const`修飾には「定数値」と「読み取り専用」という2つの側面があります。両者はよく似ていますが、明確に異なる意味をもっています。前者は"
**値/データそれ自身** "に対する修飾であり、後者が" **式の評価結果** "に対する修飾となることに注意してください。

  * 初期値をともなう変数定義で`const`修飾を用いた場合、「定数値」の定義となります。例：`const int x = 5;` とすると、名前 `x` は定数値 `5` となります。
  * ポインタが指す先の型（詳細後述）で`const`修飾を用いた場合、「読み取り専用」宣言になります。例：`int y = 10; const int *p = &y;` とすると、式 `*p` の評価結果は `const int` 型つまり「読み取り専用」となります。

「定数値」では、実行時にその値を変更することはできません。キャストにより`const`を外してコンパイラを騙しても、続く代入操作が動作する保証はまったくありません。未定義動作つまり"何が起きても知りませんよ"状態となるため、運がよければ実行時にプログラムがクラッシュします。

[code]

    const int x = 5;
    x = 0;              // NG: コンパイルエラー！
    
    int *q = (int*)&x;  // 強引にキャスト...
    *q = -1;            // NG: 未定義動作！！
    
[/code]

「読み取り専用」は名前の通り、その式を経由した変更操作を許可しないだけです。指しているデータが通常変数であれば、キャストにより`const`を外すこともできます。

[code]

    int y = 10;
    const int *py = &y;  // pyは変数yと同じintデータを指す
    
    *py = 0;             // NG: コンパイルエラー/'*py'は読み取り専用
    *((int*)py) = -1;    // OK: yの値が-1に書き変わる
    y = 42;              // OK: yの値が42に書き変わる
    assert(*py == 42);   // OK: *pyの値は42になっている
    
    const int x = 5;
    const int *px = &x;  // pxは定数xを指す
    
    *((int*)px) = -1;    // NG: 定数値は書換不可（実行時クラッシュなど）
    
[/code]

* * *

ポインタ型に対する`const`修飾では、"ポインタ値そのもの"と"ポインタが指す先のデータ"を区別して考える必要があります。下記 `p0` ～ `p3`
のポインタ型変数は、全て異なる型です。

[code]

          int *       p0;
    const int *       p1;
          int * const p2;
    const int * const p3;
    
[/code]

簡単には `*` の左側 `const` は"ポインタが指す先のデータ"を、`*` の右側 `const`
は"ポインタ値そのもの"を「読み取り専用」と宣言しています。例えばデータ領域 `*p1` は読み取り専用ですが、ポインタ値 `p1`
は自由に書き替えができます。逆にポインタ値 `p2` は読み取り専用ですが、データ領域 `*p2` は自由に書き換え可能です。

* * *

`const`修飾は、型のサイズに影響を与えません。必ず `sizeof(const int) == sizeof(int)` となります。

>
[code]

>     const int* make_immutable_point(int x){
>       void *p = malloc(sizeof(const int));
>  
[/code]

関数 `make_immutable_point()` でのメモリ確保処理は、次のように書き変えても全く同じ動きとなります。`malloc()`
では要求サイズのメモリ領域を確保するだけで、`malloc(sizeof(const int))` と書いてもメモリ領域（≒データ）が定数値とはなりません。

[code]

    void *p = malloc(sizeof(int));
    
[/code]

* * *

質問への直接回答は次の通りです。

> メイン関数内で `*f=50;` のように記述するとエラーとなります。

変数 `f` は `const int*` 型として宣言されており、`*f` の評価結果が `const int`
型つまり「読み取り専用」となるためです。読み取り専用への代入操作はコンパイルエラーです。

> これは `const` として値が確保されているからなのでしょうが、

いいえ。データ（値が格納されるメモリ領域）は `malloc()`
で実行時に確保された領域ですから、書き換えることができます。単に受け取った変数が「読み取り専用」となっているだけです。

> なぜ、このように記述すると動的に `const` が確保できているのかがわかりません。

「動的に `const`
が確保」はされていません。データは通常の非`const`領域にあり（定数ではない）、その領域を指す変数が`const`修飾されている（読み取り専用マークがある）だけです。

> コメント：`int t; scanf("%d",&t); const int *f = make_immutable_point(t);`
> のように記述すると今までの `const` の扱いとは異なり後から値が自由に代入できる

`t` は通常の `int` 型なので自由に代入可能です。`make_immutable_point(t)` の呼び出しにより、`t`
のデータ（int型の値）が **コピー** されて関数に渡されるので、変数 `t` のconst性と変数 `f` のconst性には何の関連もありません。


CC BY-SA 3.0
2015-02-10T14:04:30.460
6270
2015-02-12T02:49:37.007
2015-02-12T02:49:37.007
49
49
6245
answer