こんにちは､ そしてお帰りなさい｡ この講義では､
文字列の作成について､ 特に文字配列を使ってcで行う方法についてお話したいと思います｡
私は､ 文字列と文字列という言葉を使い分けることが多い｡
しかし､ 本当の意味での文字列は､ これから説明するchar配列のことです｡
とても大切な考え方です｡
これからは､ プログラムの中で言葉や文章を活用して､ いろいろなことができるようになるんです｡
さて､ c自身は文字列を表すデータ型を持っていない｡
何もないんですよ｡ 多くの言語では､ オブジェクト型か文字列型のデータ型をサポートしています｡
さて､ stringにはデータ型cが存在しないので､ stringにはどの演算子も使えません｡
そこで､ 最終的には標準ライブラリを使って､ いろいろな種類の文字列関数について話をすることになります｡
採用できるさまざまな文字列機能に特化した3つのレクチャーを用意しています｡
そして､ これは文字列を操作したり､ 文字列に関する情報を取得するのに役立ちます｡
ただし､ 文字列の演算子は使えないので､ 文字列のプラスやマイナスもできない｡
c の文字列は char 型の配列に格納される｡
私たちは､ 配列が何であるかを知っています｡ 型チャーが何であるかは分かっている｡
char型は1つの文字､ 配列は同じ名前の要素を複数格納するだけです｡
つまり､ 文字列は何文字もあるので､ 文字配列の中に文字列が入るのは理にかなっているのです｡
つまり､ 文字列の文字は隣接するメモリセルに､ 1セルあたり1文字ずつ格納されているのです｡
同様に配列にアクセスするのと同じようにアクセスすることができます｡
しかし､ 実際に文字列を宣言して見るには､ 単純にchar型を使って､
サイズを示すために括弧をつければいいのです｡
文字配列のサイズを角括弧で囲んで､ Char､
名前､ サイズとします｡
これが､ cで文字列を宣言する方法です｡
さて､ このようにcで文字列を宣言しても､ 実際のデータは関連付けられていません｡
サイズを指定しているだけなんですね｡
それが一番シンプルな方法です｡
そのため､ この変数には最大19文字を含む文字列を収容することができる｡
このヌル文字を覚えておいてください｡
文字列のサイズを20とすると､ 実際には19文字しか格納できないことになる｡
ヌル用に1文字余分に用意しなければならないからだ｡
文字配列を20と宣言すると､ そこに19文字が格納されることになるので､
その点に注意してください｡
文字列の前に使用する予定の配列の次元を指定する場合 - 文字列を格納するために､
格納したい文字列の文字数より少なくとも1つ大きくなければならない｡
コンパイラは､ すべての文字列定数の末尾に自動的にヌルターミネータを付加します｡
ですから､ 文字列を作成する際には､ 文字列を初期化する必要があります｡
ですから､ 配列の初期化と同じように､ 文字列変数も初期化することができます｡
この括弧の中に各文字を入れるのです｡
つまり､ これは整数配列の要素を初期化する場合と非常によく似ています｡
しかし､ char wordと言いながら､
四角い括弧の中に一文字ずつ入れていくと､
文字配列になる｡
つまり､ 他の配列の初期化と同じなのです｡
サイズを書かない場合､ cコンパイラは自動的に配列の要素数を計算し､
ヌルターミネータ用に1つ追加します｡
つまり､ 中括弧の間にあるイニシャライザの数で判断するわけです｡
このステートメントは､ h-e-l-l-o感嘆符とヌルターミネーターというちょうど7文字分のスペースとメモリを確保することになります｡
自動的に､ そのNULLターミネーターを追加する｡ もし､ 文字配列を常にシングルクォートとカンマで初期化しなければならないとしたら､
本当に面倒なことになります｡
そのため､ 省略記法として､ 二重引用符で囲むこともできます｡
ですから､ 最初の例のように文字列を明示的に指定して､
それにデータを適用するために実際に文字列関数を使うか､
コンパイラにそれをさせるかのどちらかです｡
明示的に行う場合の問題は､ ヌル文字を追加することを常に覚えておく必要があることです｡
だから､ 本当はコンパイラに任せておけばいいんです｡
だから､ その括弧の中にサイズを表す数字を入れないこと｡
なぜなら､ 指定されたサイズが小さすぎると､ コンパイラは配列の末尾にヌル文字を収めることができず､
そこにヌル文字を入れず､ ヌル文字を入れなかったことを教えてくれないからです｡
だから､ 自分のプログラムにバグがあっても､ それに気づかないんだ｡
文字列リテラルを使って文字列を初期化する場合､
コンパイラにサイズを計算させるのがベストプラクティスです｡
大きめのサイズとかにして､ そこに入れるだけでサイズを計算してくれるんです｡
だから､ サイズを指定しないでください｡ コンパイラに解決してもらいましょう｡
そうすれば､ 間違いないでしょう｡
そのカッコを空カッコのままにして､ 代入演算子で代入し､
二重引用符の中に入れればいいのです｡
角括弧も必要ない｡
これがベストプラクティスです｡ 例を挙げて説明します｡
また､ char 型の要素の配列の一部だけを文字列で初期化することも可能です｡
ですから､ 実際に作成したサイズが本当に大きく､ 数文字しか割り当てられなかったとしても､
それも問題なく､ 一部の文字だけを初期化することができるのです｡
他のキャラクターは空回りになってしまうんです｡
つまり､ ここで起こることは､ コンパイラが最初の5つの要素を初期化することだ｡
str zeroからstr fourまで､ 文字列定数､
そして文字列sub 5のこれらの文字はすべてヌル文字を含むことになります｡
配列の全40要素に対してスペースが確保されますが､
そのうち使用されるのは4要素とヌル文字の合計5要素だけです｡
つまり､ ベストプラクティスは､ サイズの中に実際に数値を指定するのではなく､
ダブルクォートを使って文字列リテラルを代入することです｡
結局はそういうことなんですね｡
さて､ 初期化せずにサイズだけ指定して文字配列を作成した場合､
どのように日付を入れるのでしょうか｡
まあ､ 配列だからデータを代入するわけにもいかないしね｡
整数配列があったとして､ 整数配列に数字の5を代入しても意味がないことは分かっています｡
実際の要素を指定する必要があります｡
インデックスを指定する必要があります｡
だから､ 100文字の文字配列を宣言して､ sイコールhelloというようなことはできないんです｡
それじゃダメなんです｡
なぜなら､ 繰り返しになりますが､ sは配列だからです｡ 二重引用符で囲まれた文字列であっても､ メモリ上では配列であることに変わりはありません｡
そのため､ 代入操作を行う場合､ 上記のようにある文字の配列を別の文字の配列に代入することはできません｡
文字配列を宣言した後､ 初期化した後にデータを代入したい場合は､
str copyという特殊な関数を使用する必要があります｡
それについては､ また別の講義でお話します｡
つまり､ ここまでは､ 文字列を作成するときは文字列リテラルで､
二重引用符で囲み､ 決して修正することはない｡
だから､ これもまったく問題ない｡ 実際には､ これらの文字や配列の各要素にインデックスでアクセスし､
各文字を指定すればよいのです｡
だから､ s sub 0､ s sub 1と､ 一文字をシングルクォーテーションで囲むのです｡
そのように割り当てることができます｡ しかし､ 100バイトの文字列があった場合を想像してみてください｡
これには多くのコード行が必要です｡ だから､ そんなことはしたくないんですね｡
宣言時にサイズ無しで初期化し､
その後､ 修正する場合はコピーするように指定します｡
出力として文字列を表示することに関しては､ やはりあのprintf関数を使うことになります｡
プログラム中の任意の場所で文字列を参照したい場合は､ 配列名単体で使用すればよい｡
インデックスを指定する必要はありません｡
配列の中のインデックスが1文字を指定することになるからです｡
文字列全体が必要な場合は､ 配列名を使うだけです｡
つまり､ printf関数を使って出力を表示するのであれば､
次のようにすればいいわけです｡ 二重引用符の内側には､ フォーマット指定子としてパーセントsを使用し､
さらに文字配列の名前だけを使用します｡
つまり､ アクセスするときに他の配列と同じように扱わないということですね｡ アクセスするときは､ インデックスやブラケットを使わず､
名前だけでアクセスできます｡
そして､ percent as format指定子は､ NULLで終端する文字列を出力するためのものです｡
整数にはパーセントdを使いましたが､
文字にはパーセントcを使うことができるのを思い出してください｡
文字列の場合は､ パーセントsを使用し､ それらは文字配列です｡
ヌル文字で終端する文字列を想定しています｡
ですから､ どのように文字列を初期化しても､ サイズ無しでやった場合はヌルターミネータが自動的に追加され､ 特定のサイズで作った場合はそれを追加し､
明示的に追加した場合は関係ありません｡
ただ､ Null Terminatedである必要があります｡
printf関数は､ percentの書式指定子に出会ったとき､
対応する引数がNULL文字で終端する文字列であると仮定する｡
それが期待されているのだから､ そうでなければならない｡
文字列を入力するには､ scanfを使ってキーボードから他のデータセットを入力する方法があります｡
文字列の入力も同じようにやっていきます｡
今回はscanf関数を使用します｡ scanf関数では､ フォーマット指定子を変える以外は同じようになります｡
文字列の場合は､ 入力としてパーセントsを使うということですね｡
そして､ それは文字列の入力に使われる｡
ここでもう一つ注目すべきは､ 変数名が文字なのでアンパサンドがないことです｡
文字列の演算子のアドレスはアンパサンドだけでいいと書きましたが､
覚えていますか？
理由は､ ポインタの話をするときに､ より明確になるためです｡
しかし､ char入力は文字配列であってもポインタであり､
scanfの第2引数はポインタを受け取ります｡
というわけで､ やらなくていいんです｡ 整数や倍数を宣言するとき､ それらはポインタとではありません｡
つまり､
キーボードからの入力をscanfで読み込む場合､ 第2引数はポインタを想定しているので､ アドレスを渡さなければならない｡
さて､ scanfで文字列を入力する場合､
scanfはスペースまでしか読みません｡
だから､ scanfをして､ Jasonと入力すると､ インストラクターになります｡
入力に保存されるのは､ ジェイソンだけです｡ なぜなら､ 一度そのスペースにぶつかると､
残りの文字列を読もうとしないからです｡
だから､ 文字列全体を読み取るために使える他の関数があるんだ｡
これから使う例の中には､ おそらくそうなるものもあるはずです｡ キーボードからの入力を読み取る別の方法として､
スペースに関係なく文字列全体を読み取るf get sのようなことも可能です｡
でも､ もうひとつ､ C11のget underscore sというのがあると思うのですが､
それについてはまた詳しく説明します｡ でも､ これは文字列の入力を読むときに覚えておくといいことのひとつです｡
宇宙までしか読めません｡
2つの文字列が等しいかどうかをテストするのはどうだろう｡
さて､ ここまでは､ すべての変数が等しいかどうかをテストしてきましたが､
ここではイコールequalsを使うだけです｡
しかし､ ストリングスの場合､ これはうまくいきません｡
等号演算子は､ 文字列は文字配列だから演算子は使えないと言ったことを思い出してください｡
また､ 等号演算子はintやdouble､ floatやcharacterにしか適用できない｡
構造体や配列には作用しません｡ 2つの配列を比較することはできません｡
だから､ 文字列ではうまくいかないんです｡ 2つの文字列が等しいかどうかを判断したい場合は､
2つの文字列を1文字ずつ明示的に比較する必要があります｡
だから､ 配列の各要素をインデックスごとに見ていくことになる｡
しかし､ それは本当に面倒なので､ 大きな問題です｡
そこで､ 標準ライブラリのstr比較関数を使うと非常に簡単にできることをお話ししますが､
equalsイコールは使えませんので､ その点は覚えておいてください｡
また､ 何度も言いますが､ 二重引用符で囲まれた文字列定数xは､
一重引用符で囲まれた文字定数xと同じではありませんので､
念のため｡
一重引用符の中のXは基本型であり､ charかcareだけです｡
二重引用符の中のXはドライブタイプで､ char型の配列である｡
二重引用符の中のXは､ 実際にはxとヌルの終端文字であるヌル文字の2文字で構成されているので､
その点に注意してください｡
以下は､ 文字列を作成する際に､
サイズを指定して宣言する方法と､
同じ行で初期化する方法の例です｡
今､ これがベストプラクティスです｡ 文字列リテラルを使用する場合は､
ここに書かれているように､
文字配列を作成し､ サイズを指定せず､ 代入演算子の右側に二重引用符で囲みます｡
コンパイラは自動的に正しい文字列を作成し､ ヌル終端記号を付加します｡
サイズを指定しても､ 文字数が足りなければ保存されません｡
そこで､ charデータ型と変数名､
そして空白の括弧を使って文字列リテラルを作成するだけです｡
このプログラムでは､ 配列の中の文字を数えるので､ 基本的には文字列の長さを取得することになると思います｡
ここでやっているのは､ 2つの文字配列にcountという名前の変数を作り､ 2つの異なる文字列､
to be or not to be､ そしてcommaという質問を入れるということです｡
シャアstrの方はbの後にオルタネーター､ strの方は質問の後にオルタネーターが付きそうです｡
つまり､ 最初の配列のカウントが0に等しくなってから､
NULLのターミネータが表示されるまで､ ループを回します｡
そして､ この場合､ 文字を比較しているので､ not equalsを使うことができるのです｡
文字としてのヌルターミネーター｡ キャラクターを比較するのは全く問題ありません｡
つまり､ このwhileループは､ ヌルのターミネーターが見えるまでずっと続くのです｡
ヌルヌルのターミネーターが見えたら､ 飛び出すんだ｡
Countは､ その文字列の文字数と同じになる｡
その文字列の長さなんですね｡
両方の文字列に対して行うのです｡ そして､ 基本的に両方の文字列が正しい長さになることに気づくでしょう｡
なぜなら､ それぞれの文字列を通過しているだけだからです｡
To b or not to bは､ 18文字くらいになりそうですね｡
なぜなら､ 文字列の長さを求めるための素晴らしいヘルパー関数､
str lengthがあるからです｡
しかし､ 繰り返しになりますが､ これが示しているのは､ 配列であるという事実なのです｡
また､ 文字列の長さを比較したり求めたりする場合は､ not equalsを使い､
ヌル文字であるターミネータを見ればよい｡
参考になったでしょうか｡ このように､ 文字列リテラルを定義し､ 初期化することになります｡
ありがとうございます｡