質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

Q&A

解決済

3回答

595閲覧

c言語 構造体に関する疑問

akiyama3284pga

総合スコア186

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

0グッド

0クリップ

投稿2022/04/14 00:53

学習中、以下のような構造体の使い方を学びました。

#include <stdio.h> #include <stdlib.h> #include <string.h> typedef void (*fptrPrint)(); typedef struct _functions{ fptrPrint display; } stFunc; void justPrint(){printf("%s\n", "aaaaaaaaaaa");} typedef struct aa{ int a; int b; int c; int c2; stFunc functions; }aa; typedef struct bb{ aa base; }bb; aa* getaaInstance(); aa* getaaInstance(){ aa *aap = (aa*)malloc(sizeof(aa)); aap->functions.display = justPrint; return aap; } bb* getbbInstance(); bb* getbbInstance(){ bb *bbp = (bb*)malloc(sizeof(bb)); bbp -> base.functions.display = justPrint; // <=この行ないと、Segmentation fault return bbp; } int main(int argc, char** argv){ aa* aaplist[2]; aaplist[0] = getaaInstance(); aaplist[0] -> functions.display(); aaplist[1] = getbbInstance(); aaplist[1] -> functions.display(); return 0; }

自分にはなぜ、
aaplist[1] -> functions.display();
ができてしまうのか理解できません。
bbp -> base.functions.display = justPrint;
という行がないとエラーとなりますので、ここが重要な役割をしているのは理解できるのですが...
aaplist[1] = getbbInstance();
という行でaa構造体ポインタに対してbb構造体を代入しているので、functionsメンバも生きているのでそれに関数ポインタを入れている???

もちろん、aaplist[1] -> base.functions.display();はbaseというメンバはbb構造体にはないのでできないことは理解しております。

なにか手がかりを頂ければ助かります。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

int32_t

2022/04/14 01:02

aaplist[0] -> functions.display(); が動くのは理解できるが aaplist[1] -> functions.display(); が動くのは理解できない、ということですか?
akiyama3284pga

2022/04/14 01:04

ありがとうございます。 その通りでございます。<(_ _)> よろしくお願いいたします。
int32_t

2022/04/14 01:06

もし「aaplist[1] = getbbInstance();」の行が「aaplist[1] = &getbbInstance()->base;」だったら理解できますか?
akiyama3284pga

2022/04/14 01:13

はい。それであれば、 aaplist[1] -> functions.display(); で動くことに対してはガテンがいきます。 しかし、aaplist[1] = getbbInstance();で aaplist[1] -> functions.display();ができてしまうのかはうまく咀嚼できません...汗
guest

回答3

0

ベストアンサー

aaplist[1] = getbbInstance(); このコードに対してコンパイラは「ポインタの型が違う!」と警告を出していただろうと思います。それでも動いてしまうのは、このコードは aaplist[1] = &getbbInstance()->base; と同じ動きをするからです。

aa 型のメモリ上での配置を考えると、aa 型のポインタが指す先から順に int, int, int, strFunc が並んでいるはずですよね。同様に bb 型のメモリ上の配置を考えると、bb の先頭には aa のフィールドがあるので、やはり先頭から順に int, int, int, strFunc が並んでいます。なので、bb へのポインタは aa へのポインタとして扱っても大丈夫なのです。

投稿2022/04/14 01:24

int32_t

総合スコア20845

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

akiyama3284pga

2022/04/14 01:33

ありがとうございます。 完全に理解することができました。 なぜ返り値が&getbbInstance()->base;の動きをするかというと、何も特殊な動きという訳ではなく、 baseメンバを一番上にしておけば当然構造体の性質上先頭アドから順に整列すると... だからbb へのポインタは aa へのポインタとして扱っても大丈夫(ガテン) 試しに、 typedef struct bb{ int ccc; // 追加 aa base; }bb; と変更して実行しますと、見事にセグフォとなりました。 教科書には継承元構造体メンバを必ず一番上に持ってこないといけないと書いていなかったため...汗 ありがとうございました。<(_ _)>
guest

0

長いこと純粋なC言語を書いてないので,

aaplist[1] = getbbInstance();

こんなのがエラーにならず(Warningすら出ずに?)コンパイル通るのか…? というのに驚かされます.

…で,これが通るなら,

aaplist[1] の型は aa* なのであるから,
aaplist[1] -> functions.display(); っていうのは,単に
aaplist[1]が指す場所に aa 型の実態があるのだと「盲目的に信じて」動いてみせている】というだけのことです.

投稿2022/04/14 01:29

fana

総合スコア11654

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

akiyama3284pga

2022/04/14 01:37

ありがとうございます。 まさにその性質というより当たり前なことを利用していたのが今回の構造体の継承の一例なのだと理解できました。 当然、aaplist[1] = getbbInstance(); の際、aaplist[1] = (aa*)getbbInstance();とキャストしてやれば 警告はでません。 今回の例では必要のない部分でしたのでキャストしておくべきでした。申し訳ございません。
fana

2022/04/14 01:51

それが構造体かどうかによらず, X x; //xはX型だが A* pA = (A*)&x; //xの場所を突っ込むぜ! (今回はこのようなキャストも書かずにいけたようだが) なんてことをすれば,pAの型は A* なのだから,以降で pA を用いた処理を書いたら,その動作には X という型は全く無関係,ってだけの話. (そもそもポインタ pA は,自身が今現在指す先に「何型の実態があるか」なんて情報を持ってるわけじゃないので)
akiyama3284pga

2022/04/14 02:05

確かに構造体云々以前にポインタというものの性質を突き詰めれば、すんなりと理解できていたことなのかもしれないと感じます。 なぜコンパイラが警告だけに留めておくのか、というのも致命的なエラーと判断すべきか微妙なものだからなのだと思いました。 構造体を特別扱いし過ぎたせいで今回の混乱が生まれたのかもしれません。 ありがとうございます。
fana

2022/04/14 02:14 編集

> キャストしておくべき 念のため補足しとくけど,その例での aa* へのキャストというのは 単にコンパイラを強制的に黙らせるだけであって,何かしらの他の素敵な効能があるわけじゃないよ. 「いいから.大丈夫だから.aa* だと思ってちょっとやってみようよ.大丈夫だから.俺が責任持つから^^」みたいな感じで,コンパイラに警告を出させないだけ. じゃあ何故キャストを書いて警告をなくすべきか? というと, 「俺は明確な意志を持って aa* 型ということにするぜ!(=ここはうっかりミスとかじゃないよ)」という意思を示すために,という感じ. (→誰に向けて示すのか? というと, コンパイラ と このコードを未来に読んだり使ったりする誰か)
akiyama3284pga

2022/04/14 02:54 編集

おっしゃる通りだと思います。 この場合自分の世界だけで使う分にはキャストの意味は皆無です。 責任を実行者が持つ代わりに、 typedef struct bb{ int ccc; // 追加 aa base; }bb; のようなミスコードをした場合にはその結果に責任はコンパイラにはありません。 いや、厳密にはエラーとせずに警告にしたのだから一部責任はあるのかもしれませんが... その意味をしっかりとどめておきたいと存じます。
jimbe

2022/04/14 03:28

> この場合自分の世界だけで使う分にはキャストの意味は皆無 プログラミングの古い諺には「明日の自分は自分ではない」というのがあります…。
akiyama3284pga

2022/04/14 11:26

ありがとうございます。 重要なことを忘れておりました。(笑) もしこのような場合にはコメントを入れて明日の違う自分に向けてメッセージを残しておきます。<(_ _)>
guest

0

aaplist[1]というのは、aa型のポインタです
aaのメンバには、functionsってやつがいます。
なら、
aaplist[1]->functions ってのは、stFunc 型の変数、ってことはわかるでしょうか。

その変数の中のdisplayという名前の関数ポインタを引っ張り出してきてそれを実行させてます

投稿2022/04/14 01:21

y_waiwai

総合スコア87749

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

akiyama3284pga

2022/04/14 01:38

毎度ありがとうございます。 今回も無事理解することができました。<(_ _)>
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問