Subsections

9章 H.24.12.12 文字列の基本

C言語では, 単語や文章などの複数の文字(文字列)は文字の配列として扱う. この章では, 文字列の扱いを学ぶが, それ以上にコンピュータのメモリ上にどのように文字というデータが配置されているかを理解することに重点を置いて欲しい.

9.1 新しい文法事項

文字と文字列
文字は1文字のみ. 文字列は文字の配列により実現する. 文字の初期化は ' (シングルコーテーション), 文字列の初期化は"(ダブルコーテーション). printfで文字列を出力するときは%s (教科書p.212), 文字を出力するときは %cを書式に指定する.

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
	 char c = 'a';
	 char str[] = "ABCDEFG";

	 printf("c=%c\n", c);
	 printf("str=%s\n", str);

	 return EXIT_SUCCESS;
}

リテラル
直接値を表す字句単位. プログラムのソースコード中に直接記述された値. リテラルは定数のうちの1つ. const char c; のように const で修飾して宣言されたcは定数であるが, このままでは, 値は不明でリテラルではない.

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
	 char c = 'a';              // 'a' は文字リテラル. c は変数.
	 char strA[] = "ABC";       // "ABC" は文字列リテラル. strA は変数.
	 const char strB[] = "ABC"; // "ABC" は文字列リテラル. strB は定数.
	 int a;                     // a は変数.
	 int b = 1;                 // 1 は整数リテラル. bは変数.
	 double x;                  // x は変数.
	 double y = 5.0e3;          // 5.0e3 は倍精度浮動小数点数リテラル. y は変数.
	 const double z = 1.0e2;    // 1.0e2 は倍精度浮動小数点数リテラル. z は定数.

	 a = 3;                     // 3 は整数リテラル. a は変数.
	 y = 8.0e-4;                // 8.0e-4 は倍精度浮動小数点数リテラル.

	 printf("a=%d, y=%f\n", a, y); // "a=%d, y=%f\n" は文字列リテラル.
	 return EXIT_SUCCESS;
}

printf, scanf の書式のまとめ
宣言 printfの書式 scanfの書式
文字 char c; printf("c = %c", c); scanf("%c", &c);
文字列 char str[10]; printf("str = %s", str); scanf("%s", str);
整数 int n; printf("n = %d", n); scanf("%d", &n);
実数 double x; printf("x = %f", x); scanf("%lf", &x);
アドレス double x; printf("xのアドレス = %p", &x);  
アドレス char str[10]; printf("strの先頭のアドレス = %p", str);  

9.2 プログラム

9.2.1 文字

program 1
// 文字の復習

#include <stdio.h>

int main(void)
{
	// 文字の宣言
	char a;

	// 文字の代入
	a = 'A';  // 特殊な文字の例 '\t' '\n'  '\a' '\0'
	a = 0x62; // 16進数で表記した文字

	// printf の書式
	printf("c=%c\n", a); // 文字の出力は %c で指定する.

	// scanf の書式
	scanf("%c", &a); //  文字の出力は %c で指定する. 引数には & をつける.

	printf("c=%c\n", a);
	
	 return 0;
}

9.2.2 文字列の宣言, 初期化, 入出力

program 2
// 文字列

#include <stdio.h>

int main(void)
{
	// 文字列の宣言 と初期化
	char a[8] = "ABC";

	// printf の書式
	printf("a=%s\n", a); // 文字の出力は %s で指定する. 

	 return 0;
}

program 3

// 文字列

#include <stdio.h>

int main(void)
{
	// 文字列の宣言
	char a[8];

	// scanf の書式
	scanf("%s", a); //  文字の入力は %s で指定する.
	// 引数には配列名を与える.
	// 配列でない変数 -> & をつけて 渡す.
	// 配列           -> &a[0] を渡す. 毎回 &a[0] と書くのは面倒.
	//                  配列名 a が &a[0] の意味で使えるようになっている.
	
	// 入力した文字の確認.
	printf("a=%s\n", a);
	
	 return 0;
}

program 4

// 初期化

#include <stdio.h>

int main(void)
{
	// 文字列の初期化
	char a[8] = {'A', 'B', 'C', '\0'};
	char b[8] = "pqr";

	// ここでは, "pqr" は {'p', 'q', 'r', '\0'} の意味

	// 初期化された内容の確認.
	printf("a=%s\n", a);
	printf("b=%s\n", b);
	
	 return 0;
}

program 5

// 注意 !!! 結果を予想できるようになりましょう.

#include <stdio.h>

int main(void)
{
	char a[8];
	char b[8];
	char c[8];

	printf("a = ");
	scanf("%s", a);
	printf("b = ");
	scanf("%s", b);
	printf("b = ");
	scanf("%s", c);
	
	printf("a=%s\n", a);
	printf("b=%s\n", b);
	printf("b=%s\n", c);
	
	 return 0;
}

9.2.3 代入, コピー

program 6

// 代入

#include <stdio.h>

int main(void)
{
	// 文字列の宣言
	char a[8];

	// 文字列への代入
	a[0] = 'A';
	a[1] = 'B';
	a[2] = 'C';
	a[3] = 0x62; // 16進数で表記した文字
	a[4] = '\0'; // 最後は 0 で終端する.
	
	// 入力した文字列の確認.
	printf("a=%s\n", a);
	
	 return 0;
}

program 7

// 代入

#include <stdio.h>

int main(void)
{
	// 文字列の宣言
	char a[8] = "ABC";
	char x[8];

	// 文字列 a から 文字列 x へのコピー(代入)

	x[0] = a[0];
	x[1] = a[1];
	x[2] = a[2];
	x[3] = a[3]; // a[3] には '\0' が代入されていることに注意.
	
	
	// コピーした文字列の確認.
	printf("x=%s\n", x);
	
	 return 0;
}

program 8

// 代入

#include <stdio.h>

int main(void)
{
	int i;
	// 文字列の宣言
	char a[8] = "ABC";
	char x[8];

	// 文字列 a から 文字列 x へのコピー(代入)
	// 繰り返しを使うと
	for(i=0; i<8; i++){
		x[i] = a[i];
		if(x[i] == '\0')
			break;
	}
	
	// コピーした文字列の確認.
	printf("x=%s\n", x);
	
	 return 0;
}

program 9

// 代入 のための関数 string_copy を作ってみましょう.

#include <stdio.h>

// 文字列 x を文字列 y にコピーする関数.
void string_copy(char y[8], char x[8])
{
	int i;
	for(i=0; i<8; i++){
		y[i] = x[i];
		if(x[i] == '\0')
			break;
	}
}

int main(void)
{
	// 文字列の宣言
	char a[8] = "ABC";
	char b[8];

	string_copy(b, a);
	
	// コピーした文字列の確認.
	printf("b=%s\n", b);
	
	 return 0;
}

program 10

#include <stdio.h>
#include <string.h> // 文字列に関係した関数を使うときは string.h をインクルードする.

int main(void)
{
	char str[100];

	strcpy(str, "ABC"); // 本当は, 文字列のコピーのための関数は標準で備わっている.
	printf("str=%s\n", str);
	
	 return 0;
}

9.2.4 長さとサイズ

program 11

// 文字列の長さを調べるプログラム.
// str[10] に代入されている文字を前から1つずつ調べ,
// '\0' でない文字の数を length により数える.

#include <stdio.h>
#include <strings.h>

int main(void)
{
	char str[10] = "ABC";
	int i;
	int length;

	length = 0;
	for(i=0; i<10; i++){
		if(str[i] != '\0'){
			printf("%d: %c\n", i, str[i]);
			length++;
		}else{ // str[i] == '\0' のとき
			break; // for のループの繰り返しを終了する.
		}
	}

	printf("length = %d\n", length);
	 return 0;
}

program 12

// 文字列の長さを調べる関数

#include <stdio.h>
#include <strings.h>

// 配列を受けとる関数. [] 内の要素数は省略できる.
int string_length(char s[])
{
	int len = 0;

	while ( s[len] != '\0' )
		len++;

	return len;
}

int main(void)
{
	char str[10] = "ABC";
	int length;

	length = string_length(str); // 配列名を渡す

	printf("length = %d\n", length);

	return 0;
}

program 13

// 文字列の長さを調べる.

#include <stdio.h>
#include <string.h> // 文字列に関係する関数を使用するときインクルードする.

int main(void)
{
	char str[10] = "ABC";
	int length;

	length = strlen(str); // 文字列の長さを調べる関数は標準で用意されている.

	printf("length = %d\n", length);

	return 0;
}

program 14

// 文字列の長さとサイズ

#include <stdio.h>
#include <string.h>

int main(void)
{
	char str[10] = "ABC";

	printf("length = %d\n", strlen(str));
	printf("sizeof(str) = %d\n", sizeof(str));

	return 0;
}

program 15

// 配列に対する sizeof 演算の復習

#include <stdio.h>
#include <string.h>

int main(void)
{
	double x[10];

	printf("sizeof(x) = %d\n", sizeof(x)); // 配列のための領域の大きさ (単位はバイト)
	printf("sizeof(x[0]) = %d\n", sizeof(x[0])); // 各要素の大きさ (単位はバイト)
	printf("sizeof(x) / sizeof(x[0]) = %d\n", sizeof(x)/sizeof(x[0])); // 要素の数

	return 0;
}

9.2.5 文字列の1次元配列 = 文字の2次元配列

program 16

// 文字列の配列

#include <stdio.h>
#include <string.h>

int main(void)
{
	char a[3][8]; // 3つの文字列の領域を確保. 各文字列は最大7文字まで.

	strcpy(a[0], "abc"); // a[0] に "abc" をコピーする.
	strcpy(a[1], "pqr");
	strcpy(a[2], "uvw");

	// 値の確認.
	printf("a[0] = %s\n", a[0]);
	printf("a[1] = %s\n", a[1]);
	printf("a[2] = %s\n", a[2]);
	
	 return 0;
}

program 17

// 文字列の配列の初期化
#include <stdio.h>
#include <string.h>

int main(void)
{
	char a[3][8] = {"abc", "pqr", "uvw"};

	// 値の確認.
	printf("a[0] = %s\n", a[0]);
	printf("a[1] = %s\n", a[1]);
	printf("a[2] = %s\n", a[2]);
	
	 return 0;
}

program 18

// 文字列の配列を関数に渡す.
#include <stdio.h>
#include <string.h>

// 各文字列に定められた文字列をコピーする関数
// 各要素の大きさ (ここでは8) は省略できない.
void strings_copy(char destination[3][8])
{
	int i;
	char source[3][8] = {"00", "111", "222"};

	for(i=0; i<3; i++)
		strcpy( destination[i], source[i] );
}

int main(void)
{
	int i;
	char a[3][8];

	strings_copy(a);

	// 値の確認
	for(i=0; i<3; i++)
		printf("%d: %s\n", i, a[i]);

	 return 0;
}

program 19

// 文字列の配列を関数に渡す.
#include <stdio.h>
#include <string.h>

// 各文字列の内容を出力する関数
// 各要素の大きさ (ここでは8) は省略できない.
void print_strings(char str[3][8])
{
	int i;
	for(i=0; i<3; i++)
		printf("%d: %s\n", i, str[i]);
}

int main(void)
{
	char a[3][8] = {"abc", "pqr", "uvw"};

	print_strings(a);
	
	 return 0;
}

program 20

// 文字列の配列を関数に渡す.
#include <stdio.h>
#include <string.h>

// 各文字列の長さを出力する関数
// 各要素の大きさ (ここでは8) は省略できない.
void print_lengths(char str[3][8])
{
	int i;
	for(i=0; i<3; i++)
		printf("%d: %d\n", i, strlen(str[i]));
}

int main(void)
{
	char a[3][8] = {"123", "12345", "1234567"};

	print_lengths(a); // 配列名を渡す.
	
	 return 0;
}


9.3 自由課題

自由課題1

/*
  (a) 教科書p.208の「文字列リテラルの大きさ」およびp.211「文字列配列の初期化」を
  理解し, 以下のプログラム中の 配列 string の要素数を答えよ.
  これは, 文字列 "12345" の長さ5とは異なることに注意せよ.
  (b) また, その理由を答えよ.
*/

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
	char string[] = "12345";

	printf("string = %s\n", string);

	printf("sizeof(char)=%d, sizeof(string)=%d\n", sizeof(char), sizeof(string));
	return EXIT_SUCCESS;
}

自由課題2

/*
  以下のプログラムについて
  (a) 文字の配列 strA, strB, strC のサイズを答えよ.
  (b) (*)で行っていることを説明せよ.
  (c) (**)で行っている代入は誤りである. 理由を答えよ.
 */

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
	// 初期化
	char strA[5] = "acem";
	char strB[5] = "nors";
	char strC[5] = "tuvw";

	printf("strA = %s\nstrB = %s\nstrC = %s\n\n", strA, strB, strC);
	strB[2] = 'X'; // (*) 正
	printf("strA = %s\nstrB = %s\nstrC = %s\n\n", strA, strB, strC);
	strB[7] = 'Y'; // (**) 誤
	printf("strA = %s\nstrB = %s\nstrC = %s\n\n", strA, strB, strC);

	
	return EXIT_SUCCESS;
}

自由課題3

/*
 次のプログラムでは, 文字列 string の長さ length を求めている.
 これを実現するためには, 文字列は '\0' で終端することになっているので,
 配列中から 文字 '\0' を探せばよい.
 とくに, 配列の大きさとは異なることに注意すること.

 このプログラムでは, while を用いて長さを求めているが,
 for を用いたプログラムに変更せよ.
*/


#include <stdio.h>
#include <stdlib.h>

int main(void)
{
	char string[20] = "ABCD";
	int length;

	length = 0;
	while(string[length] != '\0')
		length++;

	printf("length = %d\n", length);
	
     return EXIT_SUCCESS;
}

自由課題4

/*
  以下のプログラムでは, まず文字列 string の長さ length を求め,
  その値を用いて, 指定した文字 c が, 文字列 string の中で
  最初に現れる場所 position を求めている.

  position を求めている部分は for を用いて記述されているが,
  これを while を用いて表せ.
*/


#include <stdio.h>
#include <stdlib.h>

int main(void)
{
	char c, string[20] = "ABCD";
	int length, position;

	// 文字列の長さを求める.
	length = 0;
	while(string[length] != '\0')
		length++;
	printf("length = %d\n", length);

	// string の中で最初に文字 c が現れる場所 position を求める.
	c = 'B';
	for(position=0; position < length; position++){
		if(string[position] == c){
			printf("'%c' is found at %d\n", c, position);
			break;
		}
	}
	
     return EXIT_SUCCESS;
}

自由課題5

/*
  以下のプログラムでは, 文字列 strAに 別の文字列 strB の内容を
  コピーしている. 代入を行っている部分を for を用いて書き直せ.

  C言語では, strA = strB のような代入を行うことはできない.
  また, 初期化以外の場所では, strA = "abce"; のような代入ができない.
  内容をコピーするときは, 下に示すように各要素の代入を行う.
  または,
  strA[0] = 'P';
  strA[1] = 'Q';
  strA[2] = 'R';
  strA[3] = '\0';
  のようなに代入を行う.

  文字列を対象とした コピー, 結合, 長さ, 検索などの
  頻繁に用いる操作については, 標準関数として用意されているので,
  実用上はこれらの標準関数を用いた方がよい.
  
*/

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
	char strA[100];
	char strB[100] = {'P', 'Q', 'R', '\0'};

	strA[0] = strB[0];
	strA[1] = strB[1];
	strA[2] = strB[2];
	strA[3] = '\0';
	
	printf("s = %s\n", strA);

	return EXIT_SUCCESS;
}

自由課題6

/*
  文字列自体が文字の配列であるので,
  文字列の1次元配列は文字の2次元配列で実現される.

  以下のプログラムでは, まず各文字列を表示し,
  次に各文字列の長さを求めて出力するプログラムである.

  (*)の部分に各文字列 array[i] の長さを length に代入するよう
  プログラムを加筆せよ.
 */

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
	int i, length=0;
	char array[4][100] = {"abcdefg", "1234", "UVWXYZ", "---"};

	for(i=0; i<4; i++)
		printf("array[%d] = %s\n", i, array[i]);

	for(i=0; i<4; i++){
		/*
		  array[i] の長さ length を求める. (*)
		 */
		printf("[%d] %d\n", i, length);
	}
	
	
	return EXIT_SUCCESS;
}

9.4 注意: 仮引数が const str[][6]の時の関数呼出し (List9-11, List9-12)

List 9-11, List 9-12 の constの使い方は, C言語の規格上は誤りである.

これらは gcc でコンパイルすると, 以下ようなメッセージが表示される.

List9_11.c: In function ‘main’:
List9_11.c:14: warning: passing argument 1 of ‘put_strary’ from incompatible pointer type

const を削除して次のように宣言すればよい.

void put_strary(char st[][6], int n)
詳細は, ポインタの知識が必要となるので, ポインタを学んだ後に第6.4節を参照のこと.

これに対し, 仮引数に1次元配列を渡す場合は正しい. 例えば List 9-8 では,

unsigned str_length(const char str[])
のように関数宣言を行っているが, これは正しい.

(c)1999-2013 Tetsuya Makimura