C言語では, 単語や文章などの複数の文字(文字列)は文字の配列として扱う. この章では, 文字列の扱いを学ぶが, それ以上にコンピュータのメモリ上にどのように文字というデータが配置されているかを理解することに重点を置いて欲しい.
%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; }
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の書式 |
文字 | 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); |
// 文字の復習 #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; }
// 文字列 #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; }
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; }
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; }
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; }
自由課題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; }
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