授業/C言語基礎/文字列 のバックアップソース(No.5)

これまで、文字列は、''ダブル・クォーテーション'' ''"'' で囲まれたもので、printf関数またはscanf関数に渡すだけのものでした。

実は、文字列は文字を並べたデータ、つまり、文字の配列です。

そこで、まずは文字について説明し、それから、文字列について説明します。


*文字はchar型の変数 [#if83af1c]

文字を格納するための変数の型は''char型''です。
#geshi(c){{
  char c;
}}


*文字はシングル・クォーテーションで囲む [#if7f171b]

文字列はダブル・クォーテーションで囲みましたが、文字は''シングル・クォーテーション'' ''''' で囲みます。
#geshi(c){{
  char c = 'A';
}}


*文字を出力するときの変換指定子は %c [#u56ed64b]

printf関数で、変数に格納された文字を出力するときは、変換指定子を''%c''とします(プログラム1)。
#geshi(c){{
  char c = 'A';
  
  printf(">> %c\n", c);
}}

**演習1 [#df4b341c]
プログラム1を作成し、実行結果を確認せよ。


*エスケープ・シーケンスは1文字 [#g7b54906]

改行 \n、バックスラッシュ \\、ダブル・クォーテーション \" などのエスケープ・シーケンスは、1文字として扱われます(プログラム2)。
#geshi(c){{
  char c = '\"';
  
  printf(">> %c", c);
}}

**演習2 [#x91abbdc]
プログラム2を作成し、実行結果を確認せよ。


*文字は文字コードで表される [#x3256690]

文字は、2進数の文字コードで表されています。

たとえば、ASCIIコードの場合、''A'' は2進数の ''01000001'' と表されていて、16進数で表すと 41、10進数で表すと ''65'' です(プログラム3)。
#geshi(c){{
  char c = 'A';

  printf(">> %d\n", c);
}}

このプログラムを実行すると、次のようになります。
#geshi(sh){{
luna% a.out
>> 65
}}

このため、文字を整数として考えると、足し算もできます(プログラム4)。
#geshi(c){{
  char c = 'A';

  c++;
  pinrtf(">> %c\n", c);
}}

このプログラムを実行すると、次のようになります。
#geshi(sh){{
luna% a.out
>> B
}}



**演習3 [#x6169bc6]
プログラム3とプログラム4を作成し、実行結果を確認せよ。






*文字列はchar型の配列 [#c810dcf4]

文字列は、複数の文字を並べたものであり、char型の配列として扱われます。
#geshi(c){{
  char s[] = "ABC";
}}



*文字列を出力するときの変換指定子は %s [#xfe1b213]

printf関数で、変数に格納された文字列を出力するときは、変換指定子を''%s''とします(プログラム5)。
#geshi(c){{
  char s[] = "ABC";
  
  printf(">> %s\n", s);
}}


**演習4 [#df23bded]
プログラム5を作成し、実行結果を確認せよ。




*文字列の最後は \0 [#h8fee5c1]

実は、文字列の最後には、''ナル文字'' ''\0'' という文字(エスケープ・シーケンス)があります。

つまり、"ABC" という文字列は、文字 A、文字 B、文字 C、そしてナル文字 \0 から成る長さ4の文字列です。

ナル文字は表示して確認することはできませんが、if文で比較すると確認できます(プログラム6)。
#geshi(c){{
  char s[] = "ABC";
  int len = sizeof(s) / sizeof(s[0]);

  printf(">>  %d\n", len);

  if (s[3] == '\0') {
    printf("ナル文字です\n");
  } else {
    printf("ナル文字ではありません\n");
  }
}}


**演習5 [#yc6c8569]
プログラム6を作成し、実行結果を確認せよ。



*初期化のとき以外は文字列を直接代入できない [#g19b90d4]

上のプログラムのように、文字列を格納する変数を宣言すると同時に初期化するときには、変数に文字列を直接代入できますが、それ以外のときは文字列を直接代入することができません(プログラム7)。
#geshi(c){{
  char s[4];

  s = "ABC";

  printf(">> %s\n", s);
}}


**演習5 [#k118b967]
プログラム6をプログラム7に変更し、コンパイル結果を確認せよ。



*文字を一つずつ代入して文字列を作る [#ka6f7a85]

文字列は文字の配列ですから、配列の要素を一つずつ代入するように、文字を一つずつ代入することができます。
このとき、最後にナル文字を追加するのを忘れないようにしましょう(プログラム8)。
#geshi(c){{
  char s[4];
  
  s[0] = 'A';
  s[1] = 'B';
  s[2] = 'C';
  s[3] = '\0';
  
  printf(">> %s\n", s);
}}


**演習7 [#y6ce54a1]
プログラム8を作成し、実行結果を確認せよ。



*文字列をキーボードから入力する [#i8e95dd0]

scanf関数を使って、文字列をキーボードから入力することができます。
変換指定子にはprintf関数と同じ ''%s'' を使いますが、代入される変数の前にアンパサンド ''&'' をつけません。
#geshi(c){{
  char s[8];
  
  scanf("%s", s);
  printf(">> %s\n", s);
}}

ただし、文字列の長さが入力された文字数より大きくないと(入力された文字数+1以上でないと)、セグメント・エラーになる可能性があります。
そこで、変換指定子に桁数を指定して、入力される文字列の長さを制限します(プログラム9)。
#geshi(c){{
  char s[8];
  
  scanf("%8s", s);
  printf(">> %s\n", s);
}}

このプログラムを実行し、"1234567890"と入力すると、結果は次のようになります。
#geshi(sh){{
luna% a.out
1234567890
>> 1234567
}}
8文字目はナル文字になるので、7文字しかないように見えます。


**演習8 [#h7812f77]
プログラム9を作成し、実行結果を確認せよ。



*文字列を引数とする関数を作る [#rd075a3c]

文字列は文字の配列なので、char型の配列を引数とする関数を作ることで、文字列を引数とする関数を作ることができます。

配列を引数として関数を定義するとその要素数が無視されるので、配列の全ての要素にアクセスする関数を関数を作るときは、配列の長さも引数として受け取る必要があります。

しかし、文字列の場合は、最後がナル文字 \0 であることがわかっているので、先頭からナル文字 \0 が出てくるまで順番にアクセスすれば、要素数を受け取らなくても全ての文字にアクセスすることができます。

たとえば、文字列を引数として受け取るとその文字数を返す関数は、次のように定義できます(プログラム10)。
#geshi(c){{
#include <stdio.h>

int strlen(char s[]) {
  int i = 0;

  while (s[i] != '\0') {  i++; }

  return i;
}

int  main(void) {
  char s[16];
  int len;

  printf("英単語を入力してください");
  scanf("%16s", s);

  len = strlen(s);
  printf("%d文字\n", len);

  return 0;  
}
}}


**演習9 [#m52d9f52]
プログラム10を作成し、実行結果を確認せよ。




*文字を数値に変換する(おまけ) [#i42f8772]

文字を2進数の文字コードとして考えると、計算によって文字を数値に変えることができます。
#geshi(c){{
  char c = '8';
  int i = c - '0';

  printf(">> %d\n", i);
}}



*文字の種類を判別する(おまけ) [#d2f211db]

文字を2進数の文字コードとして考えると、文字コードの比較によって文字の種類を判別することができます。
#geshi(c){{
  char s[16];

  printf("文字を入力してください");
  scanf("%1s", s);

  if (s[0] >= '0' && s[0] <= '9') {
    printf("数字です\n");
  else if (s[0] >= 'A' && s[0] <= 'Z') {
    printf("大文字のアルファベットです\n");
  else if (s[0] >= 'a' && s[0] <= 'z') {
    printf("小文字のアルファベットです\n");
  } else {
    printf("数字またはアルファベットではありません");
  }
}}



*文字列操作関数を使う(おまけ) [#p020e499]

C言語には、文字列を操作するための関数(''文字列操作関数'')がいくつか用意されています。

文字列操作関数を使うには、''string.h'' をインクルードします。
#geshi(c){{
#include <string.h>
}}

文字列操作関数には、次のようなものが用意されています。
-文字列の''長さ''を調べる ''strlen関数''
-文字列を''コピー''(''代入'')する ''strcpy関数''
-文字列を指定された文字数だけ''コピー''(''代入''する) ''strncpy関数''
-文字列を''連結''する ''strcat関数''
-文字列を指定された文字数だけ''連結''する ''strncat関数''
-文字列を''比較''する ''strcmp関数''
-文字列を指定された文字数だけ''比較''する ''strncmp関数''

詳しい使い方は、教科書8.3節に載っていますが、この授業の範囲外なので、ここでは説明を省きます。
#geshi(c){{
#include <stdio.h>
#include <string.h>

int main(void) {
  char s[16] = "ABC";
  char t[16];
  int len;

  len = strlen(s);  // 文字列sの長さを調べる
  printf(">> %d\n", len);

  strcpy(t, s);  // 文字列sを文字列tにコピー(代入)する
  printf(">> %s\n", t);

  strncpy(t, s, 2);  // 文字列sを文字列tに先頭から最大2文字までコピー(代入)する
  printf(">> %s\n", t);

  strcat(s, "DEF");  // 文字列sの後ろに文字列"DEF"を連結する
  printf(">> %s\n", s);

  strncat(s, "GHI", 2);  // 文字列sの後ろに文字列"GHI"のうち先頭から最大2文字まで連結する
  printf(">> %s\n", s);

  if (strcmp(s, "ABCDEFGH") == 0) { // 文字列sと文字列"ABCDEFGH"を比較する
    printf(">> 同じ\n");
  } else {
    printf(">> 同じでない\n");
  }

  if (strcmp(s, "ABCXYZ", 3) == 0) { // 文字列sと文字列"ABCXYZ"を先頭から最大3文字まで比較する
    printf(">> 同じ\n");
  } else {
    printf(">> 同じでない\n");
  }

  return 0;
}
}}
トップ   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS