こんにちは､ そしてお帰りなさい｡
この講義では､ ポインタと構造体の使い方､ その関連性についてお話したいと思います｡
構造体の中の要素をポインタにし､ さらに構造体そのものをポインタにすることができます｡
非常に有用な2つのコンセプト｡
そこで､ 構造体が重要になるのですが､ cでは2つの構造体へのポインタが可能です｡
そのため､ 構造体型の変数を作るときは､ ポインタとして作ることができます｡
構造体へのポインタは､ 構造体そのものよりも操作しやすい｡
このように､ 表記上の工夫をすることで､ より簡単に､
より効率的に作業を進めることができるのです｡
c言語の古い実装では､ 構造体を関数の引数として渡すことができないものもありますが､
ポインタはできることが分かっています｡
構造体を宣言するときにポインタを指定すると､
古いバージョンのC言語では関数の引数として構造体を渡すことができるからです｡
構造体を引数として渡すことができる場合でも､ ポインタを渡した方が効率的です｡
その方が効率的であることは分かっています｡ 重要なパスをいかに効率的に行うかは､
これまでも話してきたとおりです｡
この場合､ 構造体全体を渡すと､ 構造体が大きく､
多くの要素を含んでいる場合､ 構造体内のすべての要素をそのパラメータにコピーすることになるので､
さらに効率的です｡
ポインターを渡す場合は､ アドレスをコピーするだけです｡
ですから､ 構造体が大きくなる可能性があるポインタを引数にすると､
なおさら重要なのです｡
そのため､ 多くのデータ表現では､ 他の構造体へのポインタを含む構造体を使用しています｡
c プログラムの構造体がポインタであることはよく見かけますが､
その理由は次のとおりです｡
古いバージョンではパラメータとして渡すことができ､
また､ 値で渡すことによってすべてを行うので､
この大きな構造体のすべての要素をコピーするよりもアドレスをコピーする方がはるかに効率的です｡
そのため､ 構造体をポインタとして宣言するのは非常に簡単です｡
打たれたポインタである変数を定義するときは､ データの型の後にそのアスタリスクを使うだけです｡
そして､ 構造体は本当に新しいタイプであると言いました｡
今回は､ 私たちにとって重要な日付構造です｡
struct dateと言って､ アスタリスクの日付ポインタを使うだけですが､
おそらくこれをnullに初期化したいのでしょう｡
なぜなら､ 構造体日付ポインタ日付構造体日付ポインタと言ったとき､
それは何のメモリも割り当てず､ 実際のポインタを格納するためのメモリを作成することを思い出してください｡
中のデータのためのメモリーを作っているわけではないのです｡
そのため､ この日付ポイントや別の日付構造体のアドレスに代入するか､
どこか別の場所にメモリを確保するか､ そのようにいくつかのデータで初期化することができます｡
つまり､ 変数dateポインタは他のポインタと同様に代入が可能で､
代入文によって今日の日付を指すように設定できますので､
すでに今日の日付の宣言があった場合は､ このようになります｡
そのアドレスを取得するためにアンパサンドを使うだけなのです｡
そこで､ 通常の日付構造体を作成し､ それは今日の日付です｡
そうすると､ 3つの要素のメモリができるんですね｡
そして､ ポインターを作成し､ もう一方の日付のアドレスを代入するだけです｡
かなりわかりやすい｡ 普通のデータ型でもそうしてきたのだから､
構造体でやっても同じことだ｡
これにより､ 日付ポインタが指すデータ構造の任意のメンバに間接的にアクセスすることができます｡
そこで､ 次のようなことが必要になります｡
もし､ 日ポインタ構造体の要素にアクセスしたい場合は､
まず参照を解除する必要があります｡
そのため､ 括弧でくくることで非常にわかりやすくなるのではないでしょうか｡
特定の値を設定するためには､ 日付ポインタの参照を解除して､
今日の日付が指しているアドレスの値を取得する必要があるのです｡
そこで､ 参照を解除して､ dot
day equals 21とすれば､ dayポインタに21が代入されます｡
さて､ 上記は今日の日付が指すものが何であれ､ その値にアクセスし､
日を21に設定したいというだけの話です｡
この場合､ ドットはアスタリスクより優先順位が高いので､
括弧が必要である｡
もし括弧を入れなかった場合､ 実際にポインターを取得する前に､ まずドットを実行してデータを指し示そうとするので､
めちゃくちゃになります｡
そのため､ 基本的にヌルポインタでデータにアクセスしようとします｡
それで､ 非参照にすることで､ 値を得ることができるのです｡
さて､ 構造体やポインターを使用する場合｡
もし､ 日付ポインタが指す日付構造体に格納されている月の値をテストしたい場合は､
次のようにします｡
ポインタの参照を解除し､ 括弧を付けてドットより優先させ､ ドット月が12に等しいとする｡
完全に有効な構文です｡
でも､ なんだかちょっと面倒くさい｡
cは独自の構文を持っているので､ 構造体のためにすべてを非参照にして括弧の中に入れる必要はないわけです｡
構造体が指しているものにアクセスしたいとき､ このように参照を解除する必要はない｡
cにはポインタの構造体のために存在する特別な演算子があり､ それはダッシュのgreater
thanです｡
ダッシュの後に大なり記号をつけたものです｡
そして､ これによって､ ポインタの参照を解除する必要がなくなります｡
つまり､ 星形xを括弧で囲んでから点yを打つのではなく､
xがyより大きいダッシュをすればいいのです｡
xはポインタなので､ 参照する必要はなく､ 何を指しているにせよ､
その構造体の中の要素にアクセスしたいのです｡
だから､ この構文をあちこちで目にすることになる｡ 括弧とdの参照は見ない方がいい｡
全く問題ないのですが､ 文字数が多く入力されていますね｡
xがyより大きいというだけの方がよっぽど効率的です｡
つまり､ 先ほどのif文では､ 括弧の中の参照を解除する代わりに､ 「日付ポインタのdashが月より大きい場合」と記述すればよいのです｡
そして､ ダッシュのgreater thanが何かを指し示しているようなものなので､ なんとなく直感的に理解できるのです｡
そして､ 月より大きい日付のダッシュをするわけです｡
で､ 日付ポインターはポインターだから､ ダッシュのgreater thanを使うわけです｡
もし､ これが入れ子構造になっていたら､
構造体に対してダッシュの大を付けて､ その後にドットを付けて､
入れ子構造の要素にアクセスすることになりますね｡
しかし､ デイポインターはポインターなので､ ダッシュイコールまたはダッシュグレイターザンを使用しなければなりません｡
ここでは､ 3つの要素を持つ構造体dateを作成し､ ポインタdateポインタを作成し､
通常の変数todayを作成する例を示します｡
ポインタに今日のアドレスを代入し､
構造体の実際の要素を代入するには､ ダッシュの greater than
を使用するだけで､ それをプリントアウトできます｡
では､ これがどのように機能するのか､ コードブロックで見てみましょう｡
そこで､ ここにこの例を載せておきます｡
そして､ 通常､ 構造体を定義するときは､ 関数の外側で定義します｡
そうすれば､ どの関数でも構造体を使うことができるからです｡
なんだか､ グローバルみたいですね｡ しかし､ 構造を定義したところで､
実際には何のメモリも作っていないことを忘れないでください｡
つまり､ ローカル変数やポインタとして変数を作成することに変わりはありませんが､
実際の定義は関数の外部にあるのです｡
だから､ 使おうと思えば､ どんな機能でも使える｡
つまり､ 一番上に date 型または name to date 型の新しい構造体を作成し､
3つの整数を格納して､ セミコロンで終端しているのです｡
次に､ mainの内部にいくつかの変数を作成します｡
一つは通常の変数 struct date today
で､ もう一つはポインタです｡ 前にアスタリスクが付いているので､ ポインタであることがわかります｡
ポインターを作成すると､ そのポインターの値のアドレスを格納するためのメモリが割り当てられるだけであることが分かっています｡
そのため､ 実際にアドレスを割り当てる必要があります｡ 今日はアンパサンドを使っていますが､
これは実際にメモリレポートを作成することになるからです｡
つまり､ 日付ポインターは､ 今日のアドレスが何であれ､
それを指すようになるわけです｡
そして､ この「より大きい」というダッシュを使えば､
データを代入することができます｡
そして､ それをプリントアウトすると､ プリントアウトのために参照を解除する必要がないことに気づくでしょう｡
プリントアウトするには､
フォーマット指定子のデータ型として､ パーセント（%）とパーセント（%）､
整数（d）を指定し､ ダッシュ採点器を使用します｡
今､ これを変えても有効です｡
括弧を付けて､ ドット月と言わなければならないのです｡
どちらでも良いですが､ dash then greater の方が構文として優れているので､ 慣れてください｡
そして､ これをビルドすると､
日付ポインタの値がプリントアウトされ､ すべて9 25と2015に設定されているのがわかると思います｡
だから､ 今日の日付は9 25 15と書いてある｡ そのように印刷できるようにフォーマットされていたのです｡
かなりわかりやすい内容です｡
ダッシュのgreater thanは､ 構造体へのポインタの新しい構文であることを覚えておいてください｡
でも､ ここ以外は普通なんですよ｡
ポインタはストアのアドレスを持っていなければならないので､ 動的メモリを割り当てるか､
既存の構造体を割り当てる必要があることは分かっています｡
もう一度､ 構造体とポインタについて詳しく説明しましょう｡
では､ ポインターを含む構造体について説明します｡
構造体のポインタを作る方法は知っています｡
既存のポインタのアドレスを割り当てたくない場合は､ そのポインタのためにメモリを割り当てることができました｡
しかし､ ポインターそのものである構造体の中に要素を持つこともできます｡
つまり､ ポインタは構造体のメンバにもなり得るのです｡
その文字配列は使わなくていいんです｡
つまり､ 構造体をポインターで指定して､
この例では2つの整数､ 2つの整数ポインターp1とp2を使っています｡
そこで､ in pointersという構造体は､ 2つの整数ポインタを含むように定義されています｡
1枚目をp1､ 2枚目をp2と呼びます｡
ポインタには､ 通常の､ struct､ int ポインタ､ ポインタと同様に､ struct
型の変数を定義することができます｡
変数ポインタは､ 他の構造体と同様に使用できるようになりました｡
ポインターは､ ポインターではありません｡
それはint型ポインターです｡ そのstrikeの中にポインタである2つの要素を含んでいますが､
ポインタ自体は実際のポインタではありません｡
だから､ そこに注目してください｡ ポインターを2つメンバーにしている構造体変数が違うんです｡
つまり､ ポインターの中にあるポインター変数の中の要素にアクセスしたい場合､
ポインターはポインターではなく､ 構造体なので､
ダッシュの greater than は使いません｡
ドットを使ってp1にアクセスし､
p2にアクセスすることになる｡
この例では､ 構造体を作成し､ 変数を作成します｡
ポインターというポインタではありません｡
そして､ 100に等しいi1と初期化されていないi2という2つの整数を作成します｡
ここで, p1 と p2 はポインタである｡
そのため､ アドレスがメモリ割り当てされている有効なメモリアドレスが含まれていなければならない｡
それが､ i1やi2の中に入っているのです｡
整数の変数を宣言すると､ その整数のためにメモリが確保されます｡
i1とi2は整数なので､ その分のメモリが確保されています｡
さて､ もし整数ポインタではなく､ 文字ポインタだった場合､ ダイナミックメモリを確保する必要があります｡
つまり､ 今は整数を使うだけなので､ 2つの整数のアドレスをp1､ p2に割り当てる｡
スターポインターを言えるようになった｡ p2は97に等しく､ マイナス97｡
なぜそこで非参照にするかというと､
ポインターだからです｡ p2が参照するものです｡
ポイント pt p2 はポインタである｡
構造体の要素にアクセスしているわけではありません｡
ポインターの全てをデリファレンスしています｡ p2.
そして､ その指し示すものの値をマイナス97に代入するのです｡
そこで､ その違いに注目してください｡
構造体ポインターを作っているわけではありません｡
通常の変数ポインタを作成していますが､ ポインタである2つの要素を含んでいます｡
ですから､ 構造体の中の2つの要素に代入したい場合は､
インダイレクト演算子を使って参照を解除する必要があります｡
そして､ 2つのprintステートメントで
ここでも､ p1が指している値を出力したいので､
p2と同じように参照を解除して､
ポインタが指している値を再び出力します｡
つまり､ 最初のものでは､ i1がパーセントIのコンマ・ポインタに等しいと言っているのです｡
p1はパーセントIに等しい｡
つまり､ 二重引用符で囲まれた最初の部分は実際のテキストを出力しているだけで､
カンマの後の部分は何も参照解除していないのです｡
まず100であるi1をプリントアウトし､ 次に100となるポインターp1をプリントアウトしています｡
I2は､ p2に割り当てた後､
実際にはマイナス97になる｡
では､ 文字配列や文字ポインタはどうでしょう｡ 前回の講義では､ ポインタの中の要素として文字配列を作成する方法についてお話しました｡
しかし､
文字ポインタの場合はどうでしょうか｡
first と last が文字配列を持つようにするのか､
それとも文字ポインタを要素とする first と last のようにするのか｡
何が良いのか｡
まあ､ 見てのお楽しみです｡ これも表記上のことですが､ ポインターを使うことには利点があります｡
ですから､ 要素の中に文字へのポインターを作ると､ 大抵は少しはマシになります｡
しかし､ ここで理解する必要があるのは､ 配列の要素を作成するときに char array
を要素とする場合とポインタを要素とする場合､ どちらも有効であることです｡
でも､ 大きな違いがあるんです｡
つまり､ 構造体名veepやvpと言い､
TaliaやSummersと言い､ 構造体ポインタ名と言うのであれば｡
ですから､ 構造体名には文字配列が含まれていると覚えておいてください｡
構造体ポインタ名にはポインタが含まれる｡
そして､ ここではBradとFallingjawに等しいポインタを初期化しています｡
そして､ それらをプリントアウトします｡ 最初の要素である文字aとvp､
そしてtreasの最初の要素であるfirstへのポインタをプリントアウトするのです｡
つまり､ 構造体は変数veepを名乗る｡
ここで起こっているのは､ 構造体の中に文字列が格納されていることです｡
この構造体では､ 2つの名前を保持するために合計40バイトが割り当てられており､ それらは文字配列になっています｡
ポインターの中に文字配列を作ると､ そういうことになるのです｡
その2つの名前､ 20と20を保持するために40バイトを割り当てているのです｡
ポインタ名変数treasについて､ 最初と最後の2つのポインタを持っているかどうか､ これらは文字列であり､
コンパイラが文字列定数を格納する場所であればどこにでも格納されます｡
構造体はポインタなので､ 2つのアドレスを保持しています｡
ポインターは､ 何､ アドレスである値を含んでいます｡
つまり､ その構造体の中に2つのポインタを割り当てるだけなのです｡
ポインターは通常8バイトか4バイトです｡
struct p の名前構造体は､ ポインタ自体のためにスペースを確保するだけなので､
文字列を格納するためのスペースは確保されません｡
ポインターはアドレスとなる値を持っています｡
このアドレスにはメモリが割り当てられていません｡
そのため､ この2つのポインターを作成しても､ その2つのポインターのためにメモリが割り当てられることはないのです｡
領域を確保しない｡
そのため､ 他の場所にスペースが確保されている文字列に対してのみ使用することができます｡
構造体の中にポインタがある場合､ 必ずmallocかcallocを使わなければならない｡
そうでなければ､ 文字列定数が格納されている場所であれば､ どこにでも格納することができる｡
そして､ そこが大きな違いです｡ ですから､ ポインタとp名構造体は､ プログラムの他の場所で作成され､
割り当てられた文字列を管理するためにのみ使用する必要があります｡
データを動的に割り当てるか､
既存の構造体など他の場所に割り当てたのでない限り､ そこにデータを割り当てたくはないでしょう｡
しかし､ 通常は､ これらのポインタをmallocしたいはずです｡
今回も､ かなり違いますね｡
名前構造体の中に､ この2つの文字配列のために40バイトを割り当てているのです｡
p名構造体には､ 2つのポインタがあります｡
その型の変数を作るときに､ そのためのメモリは割り当てられていない｡
2つのキャラクタ・ポインタのために､ それぞれ8バイトのメモリを作成しているだけなのです｡
実際にfirstとlastが指しているものに対してメモリを確保するには､ mallocを実行する必要があります｡
つまり､ 文字列を扱うのにポインタと構造体を使うことが理にかなっているのは､
メモリを動的に割り当てる場合です｡
他の許可されたデータ型が自動的にメモリを確保してくれるから､
普通はそういう時に使うんだ｡
そこで､ ポインタを用いてアドレスを格納するようにしたい｡
これは､ mallocに文字列に必要な量だけ領域を確保させることができるという利点があります｡
動的メモリ割り当ての利点についてお話しました｡
コンパイル時に静的にものを作る場合､ 特定のバイト数しか作らないことになります｡
さらにバイト数が必要になったらどうするんだ､ プログラムが動かなくなるじゃないか｡
戻って再コンパイルする必要があります｡ 動的割り当て文字列では､ 再調整が可能なので､
必要な分だけメモリを作成することができ､ プログラムの効率が格段に良くなります｡
つまり､ 構造体の中に作られたポインタにはmallocを使うということですね｡
この場合､ name countという名前の構造体があるわけですね｡
文字ポインタfnameとlast nameの2つのポインタを持っています｡
そして､ int､ letterがありますね｡
ここの中の2つの文字列は構造体に格納されていない｡
アドレスを含むポインターのためのメモリしか作らない｡
fnameとlnameには､ ポインタが指し示すもののためのメモリを作成したいと思います｡
そこで､ それを動的メモリロケーションで管理されたチャンクに格納したいのですが､ それにはmallocが使われています｡
2つの文字列のアドレスが格納されるのであって､ 何を指しているのかではありません｡
そのため､ 文字列を扱う関数では通常アドレスが使われます｡
ポインタを使うのは良いことです｡
そこで､ 一例をご紹介します｡ get infoという関数があります｡
ポインターを取ります｡
そして､ そのポインタを簡単に受け渡すことができるのです｡
ここで､ もうひとつのポインタ配列を作成し､
それにメモリを割り当てることにします｡
そこで､ tempという文字を作成し､ そのtempにデータを保存しています｡
ユーザーに何かを得てもらう｡ sgetsはあまり馴染みがないですが､ gets saved scanfと少し同じです｡
そして､ そのポインタの中にfname用のメモリを確保するのですが､ これは他の文字用のメモリを確保するのと同じです｡
mallocを呼び出して､
バイト数を渡す｡
この場合､ strの長さ10にヌルターミネータ分の1を加えた長さになります｡
そして､ mallocはvoid starを回すので､ それをcharacter starにキャストすることを思い出してください｡
でも､ ここが重要なんです｡ このmallocコールは､ ポインターを含む構造体に対して必要である｡
さて､ このポインタを渡しましたが､ ポインタにメモリが割り当てられたことはありません｡
つまり､ この内部でメモリを確保しているわけです｡ そうすると､ 非常に簡単にコピーができるようになるんです｡
パラメータとして渡された構造体pstのfnameにtempをコピーすることができます｡
そして､ 姓を入力させることができます｡ 姓の分だけメモリをmallocして､
姓構造体変数に格納することができます｡
これは､ 構造体の中に文字へのポインタがある場合､
mallocをする必要があるんだ｡
なぜなら､ 繰り返しになりますが､ これらのポインタはアドレスを保存するだけだからです｡
アドレスを格納するのに十分なメモリが割り当てられ､ 文字列を格納するのに十分なメモリは割り当てられない｡
このアドレスが指しているものが何であれ､ そのためにメモリを確保しなければなりませんが､
これは通常のmallocと同じように行います｡ 唯一の違いは､
構造体内の実際の要素（文字ポインタ）にアクセスするために大なり記号を使用している点です｡
だから､ こんなことで遊んでいたくなるんですね｡ これは､ 構造体内部の文字ポインタでは非常によくあることです｡
これをやらなきゃいけないというチャレンジをするんです｡ しかし､ このコードで遊んでみてください｡
コードブロックの中に入って､ 構造体の中に文字列や文字ポインタのためのメモリを確保し､
str copyを使って実際にデータをその中に入れる方法を見てみてください｡
前の例のように定数を使っている場合､ そのメモリがどこに割り当てられているかは誰にもわかりません｡
このように､ この講義では多くの重要なコンセプトが提示されています｡
この新しい構文では､ ポインタである構造体のアイデアは､
ダッシュを使えば､ インダイレクト演算子とは対照的に､
より大きなものを使うことができ､ 非常に素晴らしいものです｡
そして､ 実際に要素をポインタとして持ち､ 文字ポインタのためにどのようにメモリを確保しなければならないか｡
ありがとうございます｡