前回はプロジェクト「0030_moji」を新しく作って、char型の変数の宣言や初期化の方法、足し算を計算して、ASCIIコード表をベースに変換した結果を表示するプログラムを書きました。
今回は、char 型を使って文章(文字列)の初期化と表示の方法をお話しようと思います。
ではでは 文字列を取り扱うサンプルプログラムを作成してみましょう。
プロジェクト「0040_mojiretsu」を新しく用意し、 プロジェクトに空の「Mojiretsu.c」を追加し、下のソースコードをコピペして動かしてみましょう。
#include <stdio.h>
int main( void )
{
char player[] = "ぷらんく";
char* lunchA = "小麦スープ";
char waiterName[6] = { 'O', 'y', 'a', 'j', 'i','\0'};
printf("%s:お金がないのでお昼は格安 A ランチにします\n", player );
printf("%s:ほらよっ、A ランチの「%s」だぜ!\n"
, waiterName, lunchA);
printf("%s:(うわぁ、まずそうです・・。)\n", player);
}
実行すると、下のような結果になったと思います。
ぷらんく:お金がないのでお昼は格安 A ランチにします
Oyaji:ほらよっ、A ランチの「小麦スープ」だぜ!
ぷらんく:(うわぁ、まずそうです・・。)
4行目~6行目で文字列を扱う変数の宣言と初期化(代入)を行っています。
それぞれ宣言と初期化の方法をわけて書いています。いろんな宣言と初期化の方法があるということでご紹介したかったので。
char player[] = "ぷらんく";
char* lunchA = "小麦スープ";
char waiterName[6] = { 'O', 'y', 'a', 'j', 'i', '\0'};
4行目ですが、 変数の宣言と初期化を同時に行って います。
のように2つのコードに分解することができます。
char 変数名[]; で以降、変数名に文字列を代入することができます。
ここには日本語も入れることができます。また、記号 [ ] は配列を意味しています。
配列は簡単にいうと「char 型変数をたくさん並べて、たくさんの文字(日本語含む)を保存できるようにしている」 となんとなく理解してもらえればと思います。
配列についてのお話はまた後ほどお話するとして、こんな風に文字列を初期化することができるということを理解してもらえればとおもいます。 5行目ですが、こちらも変数の宣言と初期化を同時にやっています。
char* 変数名;
で以降、「変数名」という変数に文字列を代入することができます。 6行目も文字列 "Oyaji" を変数 waiterName に入れる、という処理をしています。
char 変数名[使う文字のサイズ] = {1文字 , 1文字 , 1文字 … 1文字, '\0' };
という書き方をしており、ソースコードには書きづらいですが、こんな書き方もある、という例として挙げています。
'\0'はNULL終端文字と呼ばれる制御文字です。 使い方は後ほどお話しますね。
あと、この書き方だと日本語は書けません。理由は前回お話しましたが、日本語は1文字のサイズ(バイト)ではなく、2文字以上のサイズをとるからです。
7行目から変数に設定した文字列を表示するプログラムとなっています。
printf("%s:お金がないのでお昼は格安 A ランチにします\n", player );
printf("%s:ほらよっ、A ランチの「%s」だぜ!\n"
, waiterName, lunchA);
printf("%s:(うわぁ、まずそうです・・。)\n", player);
すでにprintf のフォーマット識別子については、int型とchar型(一文字表示)を読んでいただいていれば、なんとなく読めるかとおもいます。
%s は文字列を表示するために使う識別子です。 player の変数や、lunchA の変数に設定した文字列を表示できているかと思います。
%s は英語のスペルの String(文字列)の頭文字
と見ると覚えやすいと思います。
8行目と9行目は1つの printf 関数呼び出しの命令を2行にわけて書いています。
関数も横に長いときは行を分けて見やすくしたいと思いますよね。 今回は、一つ目の引数(文字列)と2つ目以降の引数(変数名を記載)で別々の行に分割しました。
関数名を書いてる途中や、変数名などの途中に改行やスペースを入れることはできませんが、それ以外の場所には見やすくするためにいくら書いても大丈夫です。
NULL終端文字の使われ方と、使い方(ASCIIコード表をご参考)でみると、数値の 0 (制御コードNULL) は NULL終端文字 と呼ばれ、文字列の最後は、自動的にNULL終端文字が入るようになっています。 NULL終端文字と文字列の関係は、後の 配列 についてのページでもお話しますので、今は理解できなくても大丈夫です!
char name[] = "usagi";
であれば、char型の 'u'、's'、'a'、'g'、'i' の次にNULL終端文字が自動的に入っていることになります。
printf関数などで文字列を表示するときは、この NULL終端文字 を printf 関数のプログラムがチェックして、NULL終端文字が出現するまで、順番に一文字づつ表示しているという仕組みになっています。
NULL終端文字は意図的に 「\0」 で書くことができます。
たとえば、
と文字列を定義して表示すると、前半の「かめ」だけが表示されます。 これは、\0 で文字列が終わっていると printf が判断するためです。
ソースコードの6行目の
char 変数名[使う文字のサイズ] = {1文字 , 1文字 , 1文字 … 1文字, '\0' };
だと、文字の束に一文字づつ代入しているという書き方になるので、 自動的に '\0' が代入されないため、手動で '\0' を書いてあげるという意味になります。
もし'\0'(NULL終端文字)を最後に書かないで「char 変数名[使う文字のサイズ] = {1文字 , 1文字 , 1文字 … 1文字};」で定義した文字列を printf で表示しようとした場合、どのような文字が表示されるかは保証されません。もし実行して文字の表示が延々とされて終わらない場合は、
+ などをコンソールからキーボードで入力して、プログラムを中断しましょう。このページの中で、3種類の変数の宣言と初期化の方法をご紹介しました。どれも同じ結果が得られるように見えますが、Visual Studio 2017 でビルドして動かしてみた時、実際にはそれぞれ PC が内部で行っている処理はそれぞれ異なったものになります。
今回のソースコードを アセンブラ という、より機械(CPU)が理解する命令に近い形に変換すると細かな違いが見えてきます。
|[[info | 細かい話なので前置き ]] | ここは細かい話で、私がお勉強&メモとして書いている項目なので、ふーん…程度に読んでいただければ。あとポインタとか定数の知識が必要なので、今読んでも意味不明と思われるかもしれません。このパートは飛ばしていただいてまったく問題ありませんので、後々このシリーズを読んだ後にもういちど来てもらえばなんとなく意味がわかるように書いていきたいです。お役に立てたら嬉しいのですが。
文字列の宣言と初期化の方法(で内部でやってること)
書き方 | 処理の方法 |
---|---|
char player[] = "ぷらんく"; | あらかじめメモリ上に "ぷらんく" という文字列定数の領域を用意し、そこから、4バイトづつ player ポインタ位置に読み取っていく処理を順次行って初期化します。(文字列をコピーする処理をしています) |
char* lunchA = "小麦スープ"; | あらかじめメモリ上に "小麦スープ" という文字列定数の領域を確保し、そこへのポインタ(アドレス)を設定しています。(文字列のコピーは発生せず、main関数のスタック・セグメントにもポインタのための領域しか確保されません。) |
char waiterName[6] = { 'O', 'y', 'a', 'j', 'i', '\0'}; | あらかじめメモリ上に "Oyaji" という文字列は確保せず、waiterName へのポインタを作成したら、ポインタ先の領域に対して 1 バイトづつ文字をポインタ位置をずらしながら、コピーする処理をしています。 |
今回はここまで。お疲れさまでした。
更新日 | 更新内容 |
---|---|
更新なし |
コメント、ありがとうございます。
ごめんなさい。エラーでうまく送信できませんでした。ご迷惑をおかけします。しばらくおいてから再度送信を試していただくか、以下から DM などでご連絡頂ければと思います。
Twitter:@NodachiSoft_jpお名前:以下の内容でコメントを送信します。よろしければ、「送信」を押してください。修正する場合は「戻る」を押してください
お名前: