見出し画像

C言語勉強ノート;【C言語】ポインタ変数に「const」を付けよう!

ポインタ変数についてのまとめです。
kznさんの記事を契機に、姉弟子のAyumiさん、いつも天の声を授けてくださる遊月さんから、多くのことを学ぶことができました。

話の発端

発端となった記事はこちら。

この記事に対して、Ayumiさんと以下のようなやりとりをしました。

Akio van der Meer
(中略)初歩的な質問で恐縮です。get_count()の中のループ処理で、const Node* p;と定義しておられますが、これがよくわかりません。constなので値の変更は禁止かと思いきや、p = p->nextで値を変更してますよね?宜しければこの辺りの考え方のヒントをください。宜しくお願いします。

AyumiKatayama
(中略)ポインタ変数は間接参照なので「const」の対象が2種類あるんです。「const Node* p;」と書いた場合、「p->next = 1;」はダメだけど「p = p->next;」はOK。
という記事を書きました!
https://note.com/ayumi_kat/n/n26301e372a1d
ネタの提供もありがとうございます(笑)

https://note.com/ayumi_kat/n/nf5c2cc8acae5

このやりとりがきっかけでAyumiさんが発表された記事がこちら。

以下、(私的に)ディープなやりとりが続きます。

遊月
(中略)constとポインタの掛け合わせは悩めば悩むほどハマりますよねw
const char * だけ知ってるほうが幸せなくらい。
わたくしの解釈としては「constは直前の型に作用する」ですが、* で理解するのもありですねw
でも先頭のconstだけは例外で右に作用とかは「え〜っ」て思いますが…
 char const * const
が正しいけど分かりやすいのか分かりにくいのか…
 const char * const
の方が見慣れてますよねw
constも意外と厄介ですね💦

https://note.com/ayumi_kat/n/n26301e372a1d

さっぱりわからない、、、

上記の遊月さんからのコメントを受けて、Ayumiさんが追記されました。

[2023/4/16 追記]
遊月さんの次のコメントを受けて考えてみました。
「constは直前の型に作用する」
ダブルポインタになると更にややこしい。
「直前の型に作用する」と考えるとすっきりするだろうか。
ということで、書いてみた。

https://note.com/ayumi_kat/n/n26301e372a1d?from=notice
int main()
{
    char x;
    char * px = &x;
    char const** px1 = &px;
    char *const* px2 = &px;
    char **const px3 = &px;

      px1 = 0;    // char const** px1
     *px1 = 0;    // char const** px1
    **px1 = 0;    // char const** px1 --- NG

      px2 = 0;    // char *const* px2
     *px2 = 0;    // char *const* px2 --- NG
    **px2 = 0;    // char *const* px2

      px3 = 0;    // char **const px3 --- NG
     *px3 = 0;    // char **const px3
    **px3 = 0;    // char **const px3

    return 0;
}

どうでしょう。

https://note.com/ayumi_kat/n/n26301e372a1d?from=notice


どうって言われても、、、

、、、さっぱりわかりません!


ということで、実際に動かしてみた

まずは、最初にchar xに'a'をセットして、他の変数への影響を確認します。

#include <stdio.h>
#include <stdlib.h>

int main() {
    char x = 'a';
    char * px = &x;
    char const** px1 = &px;
    char *const* px2 = &px;
    char **const px3 = &px;
    char **px4 = &px;

    printf("x     = %c\n", x);
    printf("&x    = %p\n\n", &x);

    printf("px    = %p\n", px);
    printf("&px   = %p\n", &px);
    printf("*px   = %c\n\n", *px);

    printf("px1   = %p\n", px1);
    printf("&px1  = %p\n", &px1);
    printf("*px1  = %p\n", *px1);
    printf("**px1 = %c\n\n", **px1);

    printf("px2   = %p\n", px2);
    printf("&px2  = %p\n", &px2);
    printf("*px2  = %p\n", *px2);
    printf("**px2 = %c\n\n", **px2);

    printf("px3   = %p\n", px3);
    printf("&px3  = %p\n", &px3);
    printf("*px3  = %p\n", *px3);
    printf("**px3 = %c\n\n", **px3);

    return 0;
}
x     = a
&x    = 0x16fd3f72b

px    = 0x16fd3f72b
&px   = 0x16fd3f720
*px   = a

px1   = 0x16fd3f720
&px1  = 0x16fd3f718
*px1  = 0x16fd3f72b
**px1 = a

px2   = 0x16fd3f720
&px2  = 0x16fd3f710
*px2  = 0x16fd3f72b
**px2 = a

px3   = 0x16fd3f720
&px3  = 0x16fd3f708
*px3  = 0x16fd3f72b
**px3 = a


エクセルでまとめてみた

これで各変数の相関関係がわかりました。

つまり、

「 char const **px1 = &px;」と定義したとき、  **px1に値をセットしようとするとコンパイルエラーとなる。

「 char *const *px2 = &px;」と定義したとき、*px2に値をセットしようとするとコンパイルエラーとなる。

同様に、「char **const px3 = &px;」と定義したとき、px3に値をセットしようとするとコンパイルエラーとなる。


即ち、

「constは直前の型(今回の場合は、char型)の(**px1, *px2, px3)に作用する」ってことになります。

(追記:22:43 2023/4/16)下記のAyumiさんのコメントを参照ください。


、、、多分、あってると思う(苦笑


一つの質問からいろんな話につながっていく、、、だからこのC言語教室、楽しい!

最初の課題提出のときは、Macのキーボードから'\'(バックスラッシュ)を入力する方法すらわからず、kznさんの記事からコピペしてコーディングするところから始まりました。あの時は、いつもコメントをくださるMOHさんから、Macでのキー入力方法をわざわざご連絡いただきました。いろんな方に助けられながらここまでやってこれました。有り難うございます!これからも宜しくお願いします!


ここまで読んでいただき、有り難うございました。


これまでの収益は全て、それを必要としておられる方々へ、支援機関を通して寄付させていただきました。この活動は今後も継続したいと思っています。引き続きよろしくお願いいたします。