【C言語】自己参照構造体について解説

自己参照構造体の解説ページアイキャッチ

このページにはプロモーションが含まれています

このページでは、C言語における自己参照構造体について解説していきます!

構造体とはデータの型の1つで、下記ページで解説しているとおり「複数の配列や値をまとめて1つの型として管理する」型になります。

構造体解説ページのアイキャッチ 【C言語】構造体について初心者向けに分かりやすく解説

例えば下記のような構造体は、char 型の配列、float 型の値2つをまとめて1つの型として管理を行う型となります。また、構造体の持つ各変数をメンバと呼びます。

構造体の例
struct PERSON {
    char name[256];
    float height;
    float weight;
};

このように、複数の値をまとめて管理することで、データを単なる数値や文字ではなく、何かしらの物体や概念をとして扱うことができるようになります。要は、オブジェクトとして扱うことができます。上記の struct PERSON 型の変数の場合、name (名前) ・ height (身長) ・weight (体重) を持っている型なので「人間」のようにして扱うことができます。この例に限らず、扱いたいものに合わせて構造体を定義してやれば、プログラム内で好きなものを扱うことができるようになります。

構造体に関しては上記ページで解説している通りなのですが、今回は構造体の中でも少し特別的な存在である「自己参照構造体」について解説していきます。具体的には、自己参照構造体の意味、自己参照構造体の定義の仕方、自己参照構造体を利用するメリットについて解説していきます。

自己参照構造体

まずは、自己参照構造体の意味合いについて解説していきます。

自己参照構造体とは、自分自身の型を参照する構造体のことになります。

例えば、前述で示した struct PERSON であれば、struct PERSON 型のデータ(変数や配列の要素等)が他の struct PERSON 型のデータを参照できるように定義したものが自己参照構造体となります。

同じ型の変数を参照する様子

ここでいう「参照」とは、要は特定の変数が他の変数を指すことを言います。C言語で考えれば、特定の変数が他の変数を指すポインタのメンバを持つことと言い換えることができます。

今回説明するのは「自己参照」ですので、特定の変数が「同じ型の他の変数」を指すことを言います。

例えば前述で示した struct PERSON は、他の struct PERSON 型の変数を指すためのメンバを持っていないため、自己参照構造体ではありません。

自己参照構造体の定義の仕方

ここまでの説明の通り、自己参照構造体は同じ型の他の変数をポインタで指すことができるように定義してやれば良いことになります。

スポンサーリンク

自己参照構造体の定義

つまり、自己参照構造体は、その構造体のメンバに「自身の型のポインタ」を持たせることで実現できます。

したがって、自己参照構造体は下記のように定義することができます。このように定義を行えば、メンバ名 のメンバによって、自分自身の型と同じ型の変数を指すことができるようになります。

自己参照構造体の定義
struct 構造体名 {
    // 他のメンバ
    struct 構造体名 *メンバ名;
};

例えば下記のように struct PERSON を変更すれば、この struct PERSON は自己参照構造体となります。

自己参照構造体の定義例
struct PERSON {
    char name[256];
    float height;
    float weight;
    struct PERSON *person;
};

この struct PERSON は確かに自己参照構造体ではあるのですが、メンバ名が person になっているところはイマイチです。後述でも解説しますが、このメンバは同じ型の他のデータとの関係性を示すためののになります。したがって、このメンバには、名前からその関係性が理解できるようなメンバを付けてやるのが良いです。

間違った自己参照構造体の定義

自己参照構造体を定義する際によく起こる間違いが下記のような例になります。先ほどとの違いは person がポインタではなく、単なる struct PERSON になっている点になります。

自己参照構造体の間違った定義
struct PERSON {
    char name[256];
    float height;
    float weight;
    struct PERSON person;
};

このように、構造体の中で自分自身の型そのもののメンバを宣言するとコンパイル時にエラーが発生することになります。発生するエラーは下記のようなものになります。

error: field has incomplete type 'struct PERSON'

構造体の中で利用可能なメンバの型は、サイズが確定している型のみになります。

構造体の型のサイズが確定するのは最後の閉じ括弧 '}' が記述された部分になります。上記のような struct PERSON の定義では、最後の閉じ括弧前に struct PERSON 型が利用されており、サイズが確定していない型のメンバが宣言されているため上記のエラーが発生することになります。

自己参照構造体の間違った定義

ですが、ポインタはアドレスを格納する変数であるため、ポインタに必要なサイズはアドレス分であることが確定しています。そのため、struct PERSON * に変更してやればエラーは発生しなくなります(図示しているバイト数はコンパイラや環境によって異なる可能性があるので注意してください)。

自己参照構造体の正しい定義の型のサイズ

自己参照構造体で考えると少し複雑ですが、上記のような問題に関しては、別々の構造体で考えると何が起こっているかをシンプルに理解できます。

例えば下記のような構造体の定義をした場合も、先ほどと同様に field has incomplete type というエラーが発生します。これに関しては、定義前の struct TESTstruct PERSON で利用されているのでエラーが発生することに納得していただけると思います。struct TEST のサイズが確定していないため、struct PERSON では struct TEST のメンバ変数は利用できません。もちろん、構造体の定義の順番を逆にしてやれば上手くコンパイルできるようになります。

サイズ確定前の型の利用
struct PERSON {
    char name[256];
    float height;
    float weight;
    struct TEST test;
};

struct TEST {
    char name[256];
    float height;
    float weight;  
};

また、上記の struct PERSONtest メンバの型を struct TEST から struct TEST * に変更してやれば、先ほどと同様にエラーが解決できることも確認することができます。

サイズ確定前の型の利用
struct PERSON {
    char name[256];
    float height;
    float weight;
    struct TEST *test;
};

struct TEST {
    char name[256];
    float height;
    float weight;  
};

もちろん、struct TEST の定義そのものがなければポインタに変更したとしても違うエラーが発生することになりますが、定義されるのであれば、ポインタの場合は構造体自体の型のサイズが確定する前に利用することが可能となります。前述で示した自己参照構造体の場合は少し現象が複雑にも思えますが、結局起きているのはここで説明した内容と一緒になります。

少し話が逸れましたが、要は自己参照構造体を定義する際には、その構造体に自分自身の型そのもののメンバを持たせるのではなく、そのポインタをメンバとして持たせる必要があります。

typedef での型名の定義

また、通常の構造体の時同様に、自己参照構造体においても typedef で新たな型名を定義することが可能です。例えば下記のように typedef を利用すれば、struct PERSONperson_t という型名で扱うことができるようになります。

自己参照構造体の型名の定義
typedef struct PERSON {
    char name[256];
    float height;
    float weight;
    struct PERSON *person;
} person_t;

ただし、下記のように person_t が定義される前に person_t を利用するとコンパイル時にエラーが発生することになるので注意してください。

定義する前の型名が利用される例
typedef struct PERSON {
    char name[256];
    float height;
    float weight;
    person_t *person;
} person_t;

自己参照構造体を定義する際に、メンバの型として typedef で定義した型を利用したいのであれば、自己参照構造体を定義する前に typedef を記述しておく必要があります。

エラーの解決例
typedef struct PERSON person_t;
struct PERSON {
    char name[256];
    float height;
    float weight;
    person_t *person;
};

スポンサーリンク

自己参照構造体の使い道・メリット

ここまでの説明で、自己参照構造体の定義の仕方については理解していただけたのではないでしょうか?

ですが、この自己参照構造体の使い道や、自己参照構造体を利用するメリットに関してはイマイチ理解できていない方が多いのではないかと思います。

ここからは、これらの自己参照構造体の使い道やメリットについて解説していきます。

他のオブジェクト(変数)との関係性を示すことができる

結論としては、この自己参照構造体を利用するメリットは他のオブジェクト(変数)との関係性を示すことができる点にあります。ですので、自己参照構造体は、他のオブジェクトとの関係性を示したい場合に利用します。

他のオブジェクトとの関係性

関係性という言葉が非常に曖昧なので、具体例で解説していきます。

例えば、ここまでも何回も登場してきた struct PERSON の変数は「人間」というオブジェクトを表すことになります。皆さんもご存知の通り、人と人の間には様々な関係性があります。関係性の例としては、例えば親子もありますし、恋愛関係もあると思います。また、友達という関係もありますね!

こんな感じで、人と人との間には様々な関係があります。そして、こういった関係性は「人間」同士だけでなく、様々な「物」同士や「事」同士にも存在することになります。こういった「人間」「物」「事」をひっくるめてオブジェクトと呼んでいます。

で、こういったオブジェクト自体を表すのにC言語では良く構造体を利用します。そして、同じ種類のオブジェクト間の関係性を示す際に、自己参照構造体が利用されます。

関係性を示すためにポインタを利用する

また、こういった関係性を示す図によく用いられるのが「矢印」になります。下の図のように、どのオブジェクト同士が関係性を持っているかを示すために矢印がよく用いられます。

関係性を矢印で表す図

そして、C言語におけるポインタとは、アドレスを格納する変数であり、これは何かを指すための変数となります。イメージするなら、ポインタとは矢印です。この辺りは下記ページでも解説している通りです。

ポインタの解説ページアイキャッチ 【C言語】ポインタを初心者向けに分かりやすく解説

つまり、C言語では同じ種類のオブジェクト間の関係性を示すためにポインタを利用してやれば良いことになります。こういったイメージからも、自己参照構造体にはポインタのメンバが必要であることを理解していただけるのではないかと思います。

同じ種類のオブジェクト間の関係性をポインタで示す様子

例えば、下記のように自己参照でない構造体を定義したとしても、struct PERSON の各変数の間に関係性を持たせることはできません。

各変数が独立している様子
#include <string.h>

struct PERSON {
    char name[256];
    //struct PERSON *person;
};

int main(void) {
    struct PERSON person1, person2, person3;

    strcpy(person1.name, "YamadaTaro");
    strcpy(person2.name, "YamadaHanako");
    strcpy(person3.name, "YamadaSaburo");
}

つまり、struct PERSON のそれぞれの変数、すなわち、それぞれの人間は独立していることになります。

それぞれのオブジェクトが独立している様子

ですが、下記のように自己参照構造体を定義した場合、各変数の間に関係性を持たせることができます。

各変数の間に関係性が存在する様子
#include <string.h>

struct PERSON {
    char name[256];
    struct PERSON *love;
};

int main(void) {
    struct PERSON person1, person2, person3;

    strcpy(person1.name, "YamadaTaro");
    strcpy(person2.name, "YamadaHanako");
    strcpy(person3.name, "YamadaSaburo");

    person1.love = &person2;
    person2.love = &person3;
    person3.love = NULL;
}

この場合は、YamadaTaro という人が YamadaHanako という人を好きであり、さらに、YamadaHanakoYamadaSaburo という人が好きであるという関係性を持たせていることになります。

自己参照構造体により各オブジェクトの間に関係性が持たされた様子

リスト構造や木構造における自己参照

また、今までに自己参照構造体を利用した経験がある人もおられるかもしれません。そういった方の中にはリスト構造や木構造の実装を行う際に利用したという人が多いのではないかと思います。

リスト構造や木構造については下記のページで解説していますので、ここでの各データ構造の詳細な説明は省略し、自己参照構造体の観点にのみ絞って説明を行います。

リスト構造の解説ページアイキャッチ 【C言語】リスト構造について分かりやすく解説【図解】 二分探索木解説ページのアイキャッチ C言語で二分探索木(木構造・ツリー構造)をプログラミング

こういったリスト構造や木構造を実装する際には自己参照構造体を利用することが多いです。そして、ここで自己参照構造体を利用するのも、今までの解説と同じで、同じ種類のオブジェクト間の関係性を示すことが目的となります。

例えば、上記のリスト構造の解説ページにおいては、リスト構造におけるノードを扱うために次のような struct NODE を定義していました。

NODE構造体
/* 会員情報を登録する構造体 */
struct NODE {
    int number; /* 会員番号 */
    char name[256]; /* 会員名 */
    struct NODE *next; /* 次のノード */
};

そして、この NODE 構造体では number (会員番号) と name (会員名) と next のメンバを持たせており、nextstruct NODE * 型なので、struct NODE は自己参照構造体となります。

もし、この struct NODEnext がなかった場合、各ノードは単に会員番号と会員名を管理するだけのオブジェクトとなります。そして、各ノード間には関係性がなく、各ノードは独立して存在することになります。

各ノードが独立して存在する様子

それに対し、struct NODEnext があれば、単に会員番号と会員名を管理するだけでなく、特定のノードの次のノードも管理することができるようになります。例えば node1.next = &node2; とすれば、node1 の次のノードが node2 であることを示すことができます。このようにして、各ノードを順序の関係性を含めて管理することができます。

各ノードの間に順序という関係性が存在する様子

そして、これによって、各ノードを特定の順序に並べるようなことが可能となります。例えば、各ノードを会員名のアルファベット順にソートすることもできます。また、新規会員が追加された場合に、特定のノードの next を変更してやることで、新規会員追加後も会員名のアルファベット順にソートされた状態を維持するようなこともできます。

nextの設定によってソートした状態を保ちつつノードを追加する様子

ノードの場合、人間などに比べてさらに抽象度の高いオブジェクトとなりますが、結局は自己参照構造体を定義する理由はオブジェクト間の関係性を示すためとなります。そして、この関係性を示すようにするのは、その関係性を示すことで新たな機能を実現するためとなります。

例えばリスト構造の場合はノードをソートすることができるようになって線形探索を実現することができますし、2分木の場合は各ノード間の親子関係を示すことで2分探索を実現することができることになります。

自己参照構造体の利用例

最後に、簡単なプログラムで自己参照構造体の利用例を示しておきたいと思います。

スポンサーリンク

利用例のソースコード

そのプログラムのソースコードが下記となります。

自己参照構造体の利用例
#include <stdlib.h>
#include <string.h>
#include <stdio.h>

#define PERSON_NUM 5 // 人の最大人数

// 人を表す構造体
struct PERSON {
    char name[256]; // 名前
    struct PERSON *love; // 好きな人
};

// 人を配列で管理
static struct PERSON persons[PERSON_NUM];

// nameの好きな人を表示する
void showLoveTo(char *name) {
    printf("%sの好きな人:\n", name);
    for (int i = 0; i < PERSON_NUM; i++) {

        // nameを探索して好きな人を表示
        if (strcmp(persons[i].name, name) == 0) {

            if (persons[i].love == NULL) {
                printf("   いません!\n");
            } else {
                printf("   %s\n", persons[i].love->name);
            }
            break;
        }
    }
    printf("\n");
}

// nameのことが好きな人を全員表示する
void showLovedFrom(char *name) {
    int loved_from_num = 0;
    struct PERSON *person = NULL;

    for (int i = 0; i < PERSON_NUM; i++) {
        // nameを持つ要素を探索
        if (strcmp(persons[i].name, name) == 0) {
            person = &persons[i];
        }
    }

    // personのことが好きな人を探索して表示
    printf("%sのことが好きな人:\n", name);
    for (int i = 0; i < PERSON_NUM; i++) {
        // loveがpersonを指している要素を探索
        if (persons[i].love == person) {
            // 見つかったら表示
            loved_from_num++;
            printf("   %d : %s\n", loved_from_num, persons[i].name);
        }
    }

    if (loved_from_num == 0) {
        // nameのことが好きな人がいなかった場合
        printf("   いません!\n");
    }

    printf("\n");
}

int main(void) {

    char name[256];

    // 各struct PERSONの情報を最初に設定
    strcpy(persons[0].name, "Taro");
    strcpy(persons[1].name, "Hanako");
    strcpy(persons[2].name, "Jiro");
    strcpy(persons[3].name, "Mika");
    strcpy(persons[4].name, "Saburo");

    persons[0].love = &persons[1];
    persons[1].love = NULL;
    persons[2].love = &persons[3];
    persons[3].love = &persons[0];
    persons[4].love = &persons[3];

    while (1) {
        // 名前の入力受付
        printf("名前:");
        scanf("%s", name);

        int i;
        for (i = 0; i < PERSON_NUM; i++) {
            if (strcmp(name, persons[i].name) == 0) {

                // 入力されたnameの好きな人・nameのことが好きな人を表示
                showLoveTo(name);
                showLovedFrom(name);
                break;
            }
        }

        // 入力されたnameが存在しない場合は終了
        if (i == PERSON_NUM) break;
    }
}

ソースコードの解説

簡単にソースコードの解説を行なっておきます。

自己参照構造体

まず、下記で「人」を表す構造体 struct PERSON の定義を行っています。名前と好きな人を管理するため namelove メンバを持たせており、lovestruct PERSON * 型であるため、この構造体は自己参照構造体となります。

構造体の定義と配列の宣言
#define PERSON_NUM 5 // 人の最大人数

// 人を表す構造体
struct PERSON {
    char name[256]; // 名前
    struct PERSON *love; // 好きな人
};

// 人を配列で管理
static struct PERSON persons[PERSON_NUM];

また、この struct PERSON 型の配列を定義しており、この配列で5人の人を管理できるようにしています。

struct PERSON の設定

そして、main 関数の前半部分で各配列の struct PERSON の要素に対して namelove の設定を行なっています。

persons[0] から persons[4]name メンバに対し、strcpy 関数で文字列をコピーすることで名前の設定を行なっています。これにより、5人それぞれに名前が設定されることになりますが、それぞれ無関係・独立した状態になっています。

persons配列の各要素が独立して存在している様子

続いて、persons[0] から persons[4]love メンバを設定することで、それぞれの人の間に関係性が生まれます。イメージとしては、人同士の間に矢印が描かれることになります。そして、この矢印こそがポインタとなります。人は struct PERSON によって表現されているのですから、このポインタは struct PERSON * である必要があります(もしくは void *)。

persons配列の各要素に関係性が生じた様子

そして、このポインタの名前は love なので、この矢印は、誰が誰を好きであるかの関係性を示すものとなります。矢印の根元側の人が、矢印の指す方向の人を好きであることを表しています。上記のソースコードの場合、例えば TaroHanako のことが好きですが、Hanako は好きな人がいない、さらに TaroMika から好かれていることになります。

あくまでも love はポインタですので、love に代入する値はアドレスである必要があります。そして、このアドレスを取得するためにはアドレス演算子 & が必要になるので注意してください。

自己参照構造体の利用例
persons[0].love = &persons[1];
persons[1].love = NULL;
persons[2].love = &persons[3];
persons[3].love = &persons[0];
persons[4].love = &persons[3];

アドレス演算子 & については下記ページで詳しく説明していますので、詳細が知りたい方や & の意味合いを知りたい方は是非下記ページを読んでみてください。

アドレス演算子と間接演算子の解説ページアイキャッチ 【C言語/ポインタ】アドレス演算子「&」と間接演算子「*」について解説

また、上記の2行目のように、関係性を持つ相手がいない場合は NULL を指定しておいたほうが良いです。これにより、関係性を持つ相手がいないことを明示的に示すことができますし、不定値が格納されることを回避することができます。

このように、自己参照構造体における「自分自身の型のポインタ」で他の変数や配列の他の要素を指すことで、各変数や要素間、オブジェクト間に関係性を持たせることができます。

「特定の人の好きな人」の表示

そして、main 関数の後半部分で、各「人」の間の関係性を表示するようにしています。

scanf 関数で人の名前の入力受付を行い、その入力された名前の人を探索し、「その人の好きな人」と「その人のことが好きな人」の2種類の表示を行なっています。

main関数の後半で行っている処理の説明図

前者を表示するのが showLoveTo 関数で、引数で指定された名前の人を表す要素を persons 配列から探索し、「その人が好きな人の “名前”」を表示しています。

この「引数で指定された名前の人を表す要素」を persons[i] とすれば、presons[i] の好きな人は persons[i].love によって指されていることになります。なので、persons[i].love によって指されている先の構造体のデータの name メンバを表示してやることで、persons[i] の好きな相手の名前を表示することができます。

showLoveTo関数の説明図

ただ、この persons[i].love はポインタであり格納されているのはアドレスになるため、*persons[i].lovepresons[i] の好きな人(struct PERSON 型の実体)ということになります。

さらに、ここで表示したいのは presons[i] の好きな人の “名前” ですので、printf の引数に指定する表示する文字列としては (*persons[i].love).name を指定する必要があります。

自己参照構造体におけるポインタと実体の説明図

ただし、persons[i] に好きな人がいない場合は persons[i].loveNULL となるようにしているため、この場合は *persons[i].love を実行すると NULL へのアクセスが発生してプログラムが異常終了することになります。そのため、persons[i].love == NULL が成立する場合は *persons[i].love を実行しないようにする必要があります。

また、(*persons[i].love).name のようなポインタの指す構造体のメンバにアクセスする際はアロー演算子を利用することができ、(*persons[i].love).namepersons[i].love->name に書き換えることで簡潔に表現することが可能です。

要は、メンバの前側の変数が構造体を指すポインタである場合、アロー演算子 -> を利用することができます。メンバの前側の変数が構造体の実体である場合は . を利用してアクセスする必要があります。

.と->の使い分けの説明図

自己参照構造体では構造体を指すポインタのメンバが必ず存在しますので、アロー演算子 -> を利用する機会が多いです。下記ページでアロー演算子について解説していますので、この機会にアロー演算子についても理解を深めておいてください!

C言語のアロー演算子(->)を分かりやすく、そして深く解説

アロー演算子などが使われていて showLoveTo 関数の特に for ループの中身の意味合いがパッと理解できないかもしれませんが、下記のように変数を分けて考えると理解しやすいのではないかと思います。

showLoveToのfor内の書き換え
// nameを探索して好きな人を表示
if (strcmp(persons[i].name, name) == 0) {
    struct PERSON src = persons[i]; // nameを名前にもつ人
    struct PERSON *p_dst = persons[i].love; // srcの好きな相手
    if (p_dst == NULL) {
        printf("   いません!\n");
    } else {
        printf("   %s\n", p_dst->name);
    }
    break;
}

スポンサーリンク

「特定の人のことが好きな人」の表示

また、前述の通り、main 関数の後半部分では scanf 関数で人の名前の入力受付を行い、その入力された名前の人を探索し、「その人の好きな人」と「その人のことが好きな人」の2種類の表示を行なっています。

main関数の後半で行っている処理の説明図

この後者側の表示を行なっているのが showLovedFrom 関数となります。この関数では「引数で指定された名前の人を表す要素を persons 配列から探索し、「その人のことが好きな人の “名前”」を表示しています。

この「引数で指定された名前の人を表す要素」を persons[i] とすれば、presons[i] のことが好きな人は、love メンバによって presons[i] のことを指していることになります。つまり、love メンバが presons[i] のアドレスと一致している構造体のデータの name メンバを表示してやることで、persons[i] のことを好きな人の名前を表示することができます。

showLovedFrom関数の説明図

そのため、showLovedFrom 関数では最初に「引数で指定された名前の人を表す要素」を特定し、そのアドレスを person に格納するようにしています。これを行っているのが showLovedFrom 関数の前半の for ループになります。

personがpersons[i]を指す様子

そして、後半のループでは、各要素 persons[i] に対し、persons[i].love == person が成立するかどうかを判断し、成立した場合に persons[i].name を表示することで「引数で指定された名前を持つ人のことが好きな人の “名前”」を表示することを実現しています。

まとめ

このページではC言語における自己参照構造体について解説しました!

自己参照構造体とは、自分自身の型のポインタを持つ構造体のことになります。そして、この自己参照構造体のメリットは、同じ種類のオブジェクトの関係性を示すことができる点にあります。

今回紹介した人と人との恋愛関係を示すこともできますし、各ノード間の関係性を示すことでリスト構造や木構造などのデータ構造を実装することも可能です。

とにかく使い道は多いですし、こういった自分自身の型を参照する仕組みは、C言語の自己参照構造体だけでなく、クラス等でもよく利用されるので、是非この機会に自己参照のメリットについて理解しておいてください!

このサイトでは、自己参照構造体を利用する例としてリスト構造や木構造についても解説していますので、復習のために是非こちらも読んでみてください!

リスト構造の解説ページアイキャッチ 【C言語】リスト構造について分かりやすく解説【図解】 二分探索木解説ページのアイキャッチ C言語で二分探索木(木構造・ツリー構造)をプログラミング

オススメの参考書(PR)

C言語学習中だけど分からないことが多くて挫折しそう...という方には、下記の「スッキリわかるC言語入門」がオススメです!

まず学習を進める上で、参考書は2冊持っておくことをオススメします。この理由は下記の2つです。

  • 参考書によって、解説の仕方は異なる
  • 読み手によって、理解しやすい解説の仕方は異なる

ある人の説明聞いても理解できなかったけど、他の人からちょっと違った観点での説明を聞いて「あー、そういうことね!」って簡単に理解できた経験をお持ちの方も多いのではないでしょうか?

それと同じで、1冊の参考書を読んで理解できない事も、他の参考書とは異なる内容の解説を読むことで理解できる可能性があります。

なので、参考書は2冊持っておいた方が学習時に挫折しにくいというのが私の考えです。

特に上記の「スッキリわかるC言語入門」は、他の参考書とは違った切り口での解説が豊富で、他の参考書で理解できなかった内容に対して違った観点での解説を読むことができ、オススメです。題名の通り「なぜそうなるのか?」がスッキリ理解できるような解説内容にもなっており、C言語入門書としてもかなり分かりやすい参考書だと思います。

もちろんネット等でも色んな観点からの解説を読むことが出来ますので、分からない点は別の人・別の参考書の解説を読んで解決していきましょう!もちろん私のサイトも参考にしていただけると嬉しいです!

入門用のオススメ参考書は下記ページでも紹介していますので、こちらも是非参考にしていただければと思います。

https://daeudaeu.com/c_reference_book/

同じカテゴリのページ一覧を表示

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です