文字列操作
情報処理演習（テキスト：第4章4.3節，第6章6.2節）

今日の内容
1.
2.
3.

文字列
文字列の長さ練習問題

1. 文字列（テキスト34ページ）

 文字列データを扱うときは，文字の配列を使う– 配列には，名前とサイズがある– 配列を使うために，配列の使用をコンピュータに伝えること（宣言）が必要

宣言

char x[100];
文字データ

名前はx

配列のサイズは 100

文字列用の配列のサイズ
 配列の添字は0から（サイズー１）例） char x[100]; と宣言したら，サイズは 100，添字は 0 から 99
 文字列を格納する場所として実際に使えるのは，0 から（サイズー 2）まで
例） char x[100]; と宣言したら，実際に文字列の格納のために使えるのは，添字 0 から添字 98 まで（最大 99 文字まで）「文字列の末尾」を表す特別な文字定数'\0'（ヌル文字，値は 0）を入れるのに，配列要素が１つ使われる

添字0
1299

文字列はダブルクォートで囲む

文字列の代入Hchar s[]= "Hi!";i!

\0

初期化 このように書くことはできない

char s1[100],s2[100];
strcpy(s1, "Hi!");× s1= "Hi!";s2[0]= 'H';s2[1]= 'I'; 文字列用のライブラリ関数を使う
s2[2]= '!'; （string.hのインクルードが必要）
s2[3]= '\0';
文字列の終わりを示す文字定
数 （ヌル(NULL)文字）

文字列の入出力文
 「書式」と読み込むべき変数名を書く

char x[100];
scanf("%s", x);
書式

配列名はポインタを表す「&x[0]」と同じ読み込むべき変数名
配列名の前には「&」を付けない

printf("strlen(%s) = %d\n", x, len);書式

表示すべき変数名

2. 文字列の長さ
 文字列を読み込んで，長さを表示するプログラムを作る例） “Computer” の長さは 8“プログラミング演習Ⅰ” の長さは 20– ここでの文字列は，半角の「空白文字」を含まないものとする– 半角文字は１，全角文字は２として数える 半角文字は1バイト 全角文字は2バイト文字列の長さを表示するプログラム#include <stdio.h>#include <string.h>
int main(){char x[100];
int len;

文字列を格納するための配列の宣言（「char」とあるのは「文字」という意味）

文字列データの読み込み• 「%s」は文字列の意味• 配列名「x」は，配列の先頭要素のメモリアドレスの意味

printf("string=");
文字列の長さを求める標準関数
scanf("%s", x);（string.h をincludeしておく）
len = strlen(x);printf("strlen(%s) = %d\n", x, len);return 0;}

文字列データと長さの表示• 「%s」は文字列の意味

文字列用のライブラリ関数（テキスト59ページ）

 文字列用のライブラリ関数を使うときには，次の１行をプログラムに含めること#include <string.h>
コピー：
 長さ取得：
 連結：
 比較：
 検索：
など

strcpy(s, ct)
strlen(cs)strcat(s, ct)
strcmp(cs, ct)
strstr(cs, ct)

バッファオーバフローに注意
バッファオーバフローに注意(cs, ct は const char *, s は char *)文字列が変わらない

文字列が変わる例題：文字列の連結#include <stdio.h>#include <string.h>
int main(){char s1[80], s2[80];標準入力から文字列を受け取るprintf("s1=");
scanf("%s", s1);
printf("s2=");2つの文字列を連結する関数
scanf("%s", s2);
strcat(s1, s2);
printf("s1=%s, s2=%s\n", s1, s2);
return 0;}例題：文字列の比較#include <stdio.h>#include <string.h>
int main(){char s1[80], s2[80];int n;

標準入力から文字列を受け取る

文字列を比較する関数
printf("s1=");
scanf("%s", s1);
printf("s2=");
scanf("%s", s2);n = strcmp(s1, s2);
if (n < 0) printf("%s < %s\n", s1, s2 );else if (n == 0) printf("%s == %s\n", s1, s2 );else if (n > 0) printf("%s > %s\n", s1, s2 );return 0;}

入力文字列を受け取る時の注意「文字列の連結」のプログラムに下の部分があるが，これは良いのだろうか？
char s1[80];printf("s1=");
scanf("%s", s1);

バッファオーバーフロー• 入力される文字列が80文字以上の場合は，確保した配列の領域を超えて書き込まれる• 関数 scanf は s1 の添字の範
囲をチェックしないchar s1[80];printf("s1=");
fgets(s1, 80, stdin);• 入力文字数の上限を指定できる入力関数を使用する

3. 練習問題
Level問題C

文字列を入力し，それを表示するプログラムを作成せよ．

B

文字列を入力し，その文字列の長さ（文字数）を表示する
プログラムを作成せよ．文字列用のライブラリ関数を使用しないこと．

B

文字列を入力し，その文字列を逆順に出力するプログラ
ムを作成せよ．

B+半角英字からなる文字列を入力し，小文字はすべて大文字に置き換えて表示するプログラムを作成せよ．

配列
情報処理演習（テキスト：第4章4.3節，第8章8.6, 8.7節）

今日の内容配列の必要性2. 配列の宣言
3. 配列変数のイメージ
4. 配列変数を使用した例
5. 範囲を超えた添字を使うと?
6. データのソーティング
7. 今日の練習問題
第２回C言語レポート課題の出題
1.

1. 配列の必要性（テキスト31ページ）

多数のデータ処理(要求)

10,000人分の成績データをもらって，合計点の大きい順に並べ替えて出力したい．

(方法)

ひとまず，全部のデータを変数に入れておく．

(問題)

しかし，int d1,d2,d3,..,d10000;のように，10,000個も変数を書けるか?

(解決策) 配列を使おう!!
int d[10000];と書くだけで，d[0]〜d[9999] の 10,000 個の変数を使うことができる．

2. 配列の宣言（テキスト32ページ）

 一般の変数を宣言する場所で配列も宣言する．
 宣言の一般形

型 配列名[配列の要素数];
 配列名は変数名と同様，アルファベット・数字・記号からなる．
 最初の文字はアルファベットであること．
 記号は一部しか使用できない．

 型は構成要素の型を表す．

3. 配列変数のイメージ
int LastDay[13];13個の箱が並んでいると考える．
先頭から順番に 0，1，2，0

1

12 と添字が付く．
122

LastDay

LastDay[1] = 31;
添字

LastDay131← 代入式の文

配列変数へのデータの代入と参照
int main(int argc, const char * argv[]) {int LastDay[13];int month;
LastDay[1]=31;
LastDay[4]=30;
LastDay[7]=31;
LastDay[10]=31;

LastDay[2]=28;
LastDay[5]=31;
LastDay[8]=31;
LastDay[11]=30;

LastDay[3]=31;
LastDay[6]=30;
LastDay[9]=30;
LastDay[12]=31;

printf("月の日数を答えます．何月を知りたい？");scanf("%d", &month);
printf("%d月は%d日です\n", month, LastDay[month]);}LastDay0123128311２303130

3131303130

314. 配列を使用した例（テキスト101ページ）

 ある会社に2つの支店（A支店とB支店）があるとする．
 各支店で1月〜3月の各月の売上額が下の表のようなデータとしてある場合，この会社の各月の売上額を計算する．
0Branch̲A

12100,000

70,00080,00001250,000

60,00090,000Branch̲B

1月のデータCompany

2月のデータ0

3月のデータ12150,000 130,000 170,000

Company[0]= Branch̲A[0] + Branch̲B[0];プログラム例
int main(int argc, const char * argv[]) {int Branch̲A[3], Branch̲B[3],Company[3];int month;
Branch̲A[0] = 100000;
Branch̲A[2] = 80000;
Branch̲B[1] = 60000;

Branch̲A[1] = 70000;
Branch̲B[0] = 50000;
Branch̲B[2] = 90000;

for(month = 0; month < 3; month++){// 各月で計算
Company[month] = Branch̲A[month] + Branch̲B[month];}
printf("この会社の売上額");for(month = 0; month < 3; month++){// 各月で売上額を表示
printf("%d月は%d円", month + 1, Company[month]);}}配列を使用した例（２） （テキスト103ページ）
 テストの成績データがあったとして各学生の偏差値を計算するプログラムを作成してみよう．
学生番号成績

1 2 3 4 5 6 7 8 9 1068 94 77 52 73 85 79 71 86 63
上の表は下のような配列
変数に格納されたデータと考えることができる．

出力例

学生番号
123

成績
68
94
77

偏差値??
??
??
mark

1063??

68

1294

77

31063

偏差値の計算

偏差値は下に示す式で求まる．
学生iの偏差値 =(学生iの得点 − 平均) ×10+ 50
標準偏差平均と標準偏差を求める．
個々の得点の総和 d1 + d 2 + d 3 +  + d10 1 10
平均 === ∑ di
学生数1010 i =1(個々の得点 − 平均) 2 の総和
1 10
標準偏差 ==( d i − m) 2∑
学生数
10 i =1



総和を求める計算
 繰り返し制御構造を使う．
 右のプログラム片で計算できる．

ループ終了後，変数waの値が総和

プログラム片wa = 0;for(i = 1; i <= 10; i++){wa = wa + d[i];}偏差値計算のプログラム例#include <math.h>int main(int argc, const char * argv[]) {const int N = 10;
int mark[] = {0, 68, 94, 77, 52, 73, 85, 79, 71, 86, 63};
int wa, i;
double m, sigma, wa2, y;
wa = 0;for(i = 1; i <= N; i++){wa = wa + mark[i];}m = wa / N;
wa2 = 0;for(i = 1; i <= N; i++){wa2 = wa2 + pow((mark[i] - m), 2);}sigma = sqrt(wa2);
printf("学生番号 成績 偏差値\n");for(i = 1; i <= N; i++){y = (10 * (mark[i] - m) / sigma + 50);
printf("%4d%2d%2.1f\n", i, mark[i], y);}}5. 範囲を超えた添字を使うと？
 配列のサイズは宣言のときに決定する．

 宣言時にメモリ領域を確保する．
 サイズを超えて使用したら?

int x, a[3];int 型の配列，3つの箱を作る．
012

x = a[3];
この箱の値?
そもそもここに箱はあるの?

配列のオーバフロー
 C言語では，コンパイラは配列の添字の範囲のチェックは行わない．
 配列の添字のチェックはプログラマの責任
int x, a[3];x = a[3];a[10] = 100;

この代入式では，何らかの値が変数 x に代入される．
確保されていないメモリ領域に値 100 が書き込まれる．

これらはエラーにならない

添字の範囲チェックを怠ると，おかしな動作をするプログラムになる．

6. データのソーティング （テキスト110ページ）
並べ替えイメージ(その1)
小さい順

01

2345

並べ替え前 4 89 6 2 23 2189 1〜6の最大
23 89 1〜5の最大
21 23 89 1〜4の最大
6 21 23 89 1〜3の最大並べ替え後 2 4 6 21 23 89

並べ替えイメージ（その2）隣り合った数を比べ，前の数が大きければ入れ替える．
01234

54 > 89 ? No

Step 1
Step 289 > 6 ? Yes!!
(1)

(2)

(3)

W

6
Step 3, 4, 5

8989

46

入れ換え

2223232121

Max89 1〜6の最大入れ換え

プログラム例（main関数の中）
const int N = 6;
int d[]={4,89,6,2,23,25}, sd[N];int i,j,w;// 配列dを配列sdにコピー
for(i = 0; i < N; i++){sd[i] = d[i];}for(i = 0; i < N; i++){for(j = 0; j < N - i - 1; j++){if(sd[j] > sd[j+1]){
w=sd[j];sd[j] = sd[j+1];
sd[j+1] = w;}}}
printf("Sorted data=\n");for(i = 0; i < N; i++){printf("sd[%d]=%d\n", i, sd[i]);}9. 今日の練習問題
Level問題C

月と日を入力すると，その年の1月1日からの経過日数を表示するプログラムを作成せよ（閏年は考えない）(例8.7.1参照)．

B

配列内の変化の様子を観察できるようにソートの例題
プログラムを変更せよ(例8.7.3参照)．

B+n個のデータをもらって，各データの値だけ「*」を横に並べる棒グラフを描くプログラムを作成せよ(例8.7.2参照).

A

棒グラフにおいて，平均値の「*」を「+」に換えて表示せよ．

第２回C言語レポート課題
課題： 繰り返しと配列を利用したプログラムを作成する．
形式： プログラムだけを提出するのではなく，プログラムの説明を書くこと．実行結果をキャプチャして貼付けること．
問題： テキスト pp.117~120の練習問題から１つ以上を選んで答えなさい．
〆切： クラスによって案内がある．