2001年後期 情報処理演習I (火曜1限)
■ 情報処理演習I
■ 講義(7)
■ 講義(8)
■ 講義(9)
■ 講義(10)
■ 講義(11)
■ 講義(12)
■ C言語入門(1)
■ C言語入門(2)
C言語入門

参考書として,98年度の大阪市立大学インターネット講座「UNIXで学ぶC言語」 をあげておく.

簡単なC言語のプログラム
main()
{
        int i;
        for (i = 0 ; i < 10 ; i = i + 1) {
                printf("count %d\n", i);
        }
}

Cのプログラムは,main関数から実行される.とりあえず,最初はmain関数しか使わない.

main()
{
        関数の中身
}

疑似言語との主な違い
  • 正確に書かないと,コンパイルできない
  • 思ったように動くのではなく,書いてある通りに動く :-)
疑似言語 C言語
変数 型がない 型がある.使う前に宣言が必要
配列 型がない 型がある.使う前に要素の数を決めておく必要がある.宣言が必要
定数 型がない 型がある
変数への代入 = (イコール1つ)
演算 +, -, *, /, % 等 整数型で演算すると,結果は切り捨てられる
比較 = == (イコール2つ)
比較 !=
論理演算 A かつ B A && B
論理演算 A または B A || B (縦棒2つ)
関数 input() scanf(...) 等
関数 output(...) printf(...) 等
制御構造 if, while 同じ
制御構造 for (i ← A ... B) for (i = A ; i <= B ; i++)
文末 特になし 文の終わりには,; (セミコロン)が必要

コメント
  • /* 〜 */ で囲まれた部分は,コメントとして扱われる.
  • コンパイラは,コメントを無視する.
  • "文字列"の中以外では,どこにでも書ける.
  • 積極的にコメントを書いて,プログラムを読みやすくするべき.
  • 教室の環境では,コメントとして日本語も書ける.
/* This is sample program */
main()
{
        /* main関数の中身 */
}

変数

変数は宣言が必要.

基本的な型
整数型 int 4バイト -2,147,483,648〜2,147,483,647
long 4バイト -2,147,483,648〜2,147,483,647
short 2バイト -32,768〜32,767
char 1バイト -128〜127
実数型 float 4バイト -3.40282*1038 〜 3.40282*1038
double 8バイト 省略(倍精度)
(注意:実数といっても,無限の精度があるわけではない)
  • 変数の宣言は,関数の最初で行う.(本当は { の後ならどこでも良い)
  • 変数の方は,変数がとりうる値の種類に応じて選ぶ.基本的に,今のところは int と float がメイン
  • 変数の宣言は,型名の後に変数名を書く.
  • 変数名は,アルファベットと数字, _ (アンダースコア)が使える.ただし,先頭に数字は使えない.できるだけ分かりやすい変数名をつけること.
  • 幾つでも続けて宣言できる.
main()
{
        int i;          /* int 型の変数 i の宣言 */
        int j;          /* int 型の変数 j の宣言 */
        long x, y, z;   /* long 型の変数3つ x, y, z の宣言 */
        ....
}

printf関数

C言語では,全ての入力(キーボードやファイルから)や出力(画面やファイルへ)を,関数を用いて行う.

printf関数は,画面に出力するための関数で,疑似言語の output にほぼ対応する.

/* output("i = ", i, "j = ", j) に対応 */
printf("i = %d j = %d\n", i, j);
  • 文字列は "ダブルクオート" で囲む.
  • C言語では,文字列中の \n は,改行を意味する.
  • 最初の引数 "count %d\n" の部分が表示される.
  • %d は,その次の引数をここに埋め込んで表示するという意味.
よく使われる特殊文字.C言語では,文字列中の \ は特別扱いされる.
\n 改行
\t タブ
\" ダブルクオート(文字列中に"を入れるために使う)
\\ バックスラッシュ(文字列中に\を入れるために使う)

(注意) \ はもともとバックスラッシュであるが,日本の計算機では円記号で表示される場合がある.

printf での書式指定文字(当面使うもののみ)
%d 10進数で表示
%f 実数として表示
%3d 3桁の10進数
%6.2f 6桁の実数(小数点以下2桁)
printf("count %d %d\n", i, i * 10)

演習
i, j の二つの整数型変数に,適当な値を代入し,それらの和,差,積,商を表示するプログラムを書け. また,i, j を実数に変更して実行してみよ.

scanf関数

scanf関数は,キーボードから入力を行う関数,疑似言語の input にほぼ対応する.

int i;
float f;
double d;

/* i ← input() に対応 */
scanf("%d", &i)     /* i に整数値を入力する */
/* f ← input() に対応 */
scanf("%f", &f)     /* f に実数値を入力する */
/* d ← input() に対応 */
scanf("%lf", &d)    /* d に実数値を入力する */
  • &を忘れないように.& の意味は今は気にしない.
  • printf と異なり,書式指定文字列(%?)以外は書かない.
main()
{
        int i;
        print("Input an integer: ");
        scanf("%d", &i);
        if (i > 100) {
                printf("Too large\n");
        } else if (i > 50) {
                printf("Large\n");
        } else {
                printf("Small\n");
        }
        printf("End\n");
}

演習
  キーボードから2つの整数を入力し,それらの和,差,積,商を表示するプログラムを書け.
また,i, j を実数に変更して実行してみよ.

if文
  • 条件の回りの括弧 () は必須
  • 文の本体の中括弧 {} も必須(本当は,つけなくて良い場合もあるが..)
if (a == 0) {
        printf("a は 0 です\n");
} else {
        printf("a は 0 ではありません\n");
}

a = 10;
while (a > 0) {
        printf("a = %d\n", a);
        a = a - 1;
}

for文

C言語の for 文は,いろいろと複雑なことができるが,今は

/* for (i ← 0 ... 20) と同じ意味 */
for (i = 0 ; i <= 20 ; i++) {
        ...
}

の形だけ使う.間の ; (セミコロン) を忘れないように.

break文

疑似言語の「ループからの脱出」は break 文になる.

for (i = 0 ; i <= 20 ; i++) {
        if (i == 10) {
                break;  /* for 文を抜ける(XXX に飛ぶ) */
        }
}
/* XXX */

問題
九九の表を出力するプログラムを書け
2つの数を入力させ,それらの和,差,積,商を出力せよ. 2数として0,0が入力されるまで,この処理を続けよ.

配列
型名 配列名[要素数];
で宣言する.
  • int ryokin[10]; だと ryokin[0] 〜 ryokin[9]までの10個のint型の変数ができる.
  • 要素数には変数は使えない.定数のみ.
  • 変数と同様,配列の要素の初期値は不定.
  • C言語では,添字(インデックス)の範囲のチェックは行われない. int ryokin[10]; に対して,ryokin[100] = 0; としてもコンパイラは文句をいわない. 以下のプログラムを,実行する前にどのような結果が出るかを考えてから,実行してみよ.
    main()
    {
            int a;
            int ryokin[10];
    
            a = 1234;
            ryokin[10] = 2345;	/* ryokin[10]は配列の範囲外! */
            printf("a = %d\n", a);
    }

問題

数列

  • A[1] = 1
  • A[k] = 2 * A[k-1] + 3

の第100項を求めよ.

random関数

0 〜 231-1 までの間で乱数を発生させる関数.この関数を呼ぶ度に,乱数を返す.(a = random();)

ある範囲 (例えば0〜5) の乱数が欲しいときは,6 で割った余りを使えば良い.randomが返す値は,実際は乱数のように見える数列.以下のプログラムを何回か実行してみよ.

main()
{
        int i;
        for (i = 1 ; i < 10 ; i++) {
                printf("%d\n", random());
        }
}

乱数の「種」を初期化するために,srandom関数がある.srandomの引数が,乱数の種になる.プログラムの実行の度に,なるべく異なった値を入れる.簡単に済ますには,time関数を使う.

time 関数は,1970年1月1日からの秒数を返す関数.time(0) の形で使うこと.int i; と for の間に,srandom(time(0)); を追加してみよ.

問題
  • 0から9までの整数の乱数を100個発生させ,それぞれの出現頻度を表示せよ.乱数を1000個,10000個にしたときはどうなるか?
    main()
    {
            int i;
            int a;
            int hindo[10];
            /*・配列 hindo を御破算にする */
            for (i = 0 ; i <= 9 ; i++) {
                    hindo[i] = 0;
            }
            /*・以下の処理を100回繰り返す(for) */
            for (i = 1 ; i <= 100 ; i++) {
                    /* 0-9までの乱数を発生させる(aとする) */
                    a = random() % 10;
                    /* 配列 hindo を a に応じて +1 する
                    (つまり,hindo[a] = hindo[a] + 1) */
                    hindo[a] = hindo[a] + 1;
            }
            /*・hindoの中身を出力する */
            for (i = 0 ; i <= 9 ; i++) {
                    printf("%d の頻度 : %d\n", i, hindo[i]);
            }
    }

否定演算子(!)

「!(a == 0)」と書くと,「a == 0 でない」という意味になる(つまり,a != 0 と同じ).

主に,if文やwhile文の条件で使う.

例: !(a == 0 && b == 0) (これは,論理的に a != 0 || b != 0 と同じ)

一般に,
  • !(A && B) ←→ !A || !B
  • !(A || B) ←→ !A && !B
が成り立つ.

while(永遠に)

疑似言語で無限にループするために while (永遠に) と書いたが,C言語では,while (1) あるいはfor (;;) と書けば良い.

main()
{
        while (1) {
                ...
        }
        /* こっちでも良い */
        for (;;) {
                ...
        }
}

#define

#define XXX YYY と書くと,以後の XXX が YYY に置き換えられてからコンパイルされる.

  • ある数値(例えば10)が,プログラム中で複数の個所で使われるような場合, 例えば #define NUM 10 と書いておきと,後から変更するときに #define の行だけを変更すれば良いので便利.
  • プログラム中に突然数値が出てくると,他人には意味が分かりにくい.
    #define 分かりやすい名前 数値
    としておき,「分かりやすい名前」を使えば,他人から読みやすいプログラムになる(重要).
  • #define にはセミコロン(;)はつけない.
  • #define は通常行頭から書く.
  • XXX の部分が"文字列の中"に現れても,置換されない.
  • XXX を「マクロ」と呼ぶ.#define することを,「マクロを定義する」と呼ぶ.
#define NUM_OF_RANDOM 10

main()
{
        int i;
        for (i = 1 ; i < NUM_OF_RANDOM ; i++) {
                printf("%d\n", random());
        }
        printf("全部で %d 個の乱数を発生しました\n", NUM_OF_RANDOM);
}

文字の扱い方

コンピュータで扱うそれぞれの文字には,文字コードが割り当てられている.多くのコンピュータでは ASCIIコードと呼ばれる文字コードを使っている.

コンピュータは,内部的に文字コードという数値を使うことで文字を扱っている.

ASCIIコード表
0 NUL1 SOH2 STX3 ETX4 EOT5 ENQ6 ACK7 BEL
8 BS 9 HT(タブ) 10 NL(改行) 11 VT 12 NP 13 CR 14 SO 15 SI
16 DLE17 DC118 DC219 DC320 DC421 NAK22 SYN23 ETB
24 CAN25 EM 26 SUB27 ESC28 FS 29 GS 30 RS 31 US
32 SP 33 ! 34 " 35 # 36 $ 37 % 38 & 39 '
40 ( 41 ) 42 * 43 + 44 , 45 - 46 . 47 /
48 0 49 1 50 2 51 3 52 4 53 5 54 6 55 7
56 8 57 9 58 : 59 ; 60 < 61 = 62 > 63 ?
64 @ 65 A 66 B 67 C 68 D 69 E 70 F 71 G
72 H 73 I 74 J 75 K 76 L 77 M 78 N 79 O
80 P 81 Q 82 R 83 S 84 T 85 U 86 V 87 W
88 X 89 Y 90 Z 91 [ 92 \ 93 ] 94 ^ 95 _
96 ` 97 a 98 b 99 c 100 d 101 e 102 f 103 g
104 h 105 i 106 j 107 k 108 l 109 m 110 n 111 o
112 p 113 q 114 r 115 s 116 t 117 u 118 v 119 w
120 x 121 y 122 z 123 { 124 | 125 } 126 ~ 127 DEL

改行(NL, New Line)等にもコード(10)が割り当てられている.

日本語の文字コードには日本語EUCコード,JIS漢字コード,シフトJISコード等が使われている.

C言語で文字を扱うためには char 型の変数を使うことが多い.char 型は,ASCII文字1文字を入れておくことができるサイズ.日本語の文字は入らない.

C言語では,'シングルクオート' で文字を囲むと,その文字のコードを表す.

char c;         /* 変数の宣言 */

c = 65;         /* A という文字を記憶 */
c = 'A';        /* シングルクオートして書いても良い(こちらのほうがベター) */
c = '\n';       /* 改行のコード10 */

c = 'AB';       /* これは許されない */
c = 'あ';       /* 日本語もダメ */

printf で文字コードを引数に与えて文字として表示するためには,%c を使う.

char c;         /* 変数の宣言 */

c = 'A';
printf("a の文字は %c, 文字コードだと %d\n", c, c);

標準入力と標準出力

多くのプログラムはキーボードから入力して画面に出力するようにできている.

UNIXでは,プログラムに変更を加えなくても入力装置(通常はキーボード)や出力装置(通常は画面)を簡単に切り替えることができる.

  % prog < file1               (キーボードの代わりに file1 から入力)
  % prog > file2               (画面の代わりに file2 へ出力)
  % prog < file1 > file2    (file1 から入力して file2 へ出力)

このため,プログラムの入力装置(普通はキーボード)を標準入力,出力装置(普通は画面)を標準出力と呼んでいる.多くのプログラムは,標準入力から入力を受取り,標準出力へ出力を行う.

例えばprintfは標準出力に書き出す関数.scanfは標準入力からフォーマットに従って入力を受け付ける関数である.

getchar関数とへッダファイル

標準入力から1文字入力するためにはgetchar関数を使う.入力した文字のコードが返り値になる.(ただしリターンキーを押さないと入力した文字がプログラムに渡されないことに注意).

int a;          /* int 型の変数を使っていることに注意 */
a = getchar();  /* 1文字入力 */
  • 標準入力には,「終わり」という概念がある.キーボードから「終わり」を入力するに Control-D を入力する(UNIX の場合).
  • 「終わり」になると,getchar 関数は EOF という値を返す.(実際は EOF はマクロで,-1 という値).EOF は End Of File の意味.
  • getchar は -1 〜 255 の値を返す.-1 が char の範囲に入りきらないので,int 型の変数に代入している.
  • Cのプログラムで,#include <stdio.h>と書くと,そこに stdio.h を取り込むという指示になる.stdio.h は UNIX では /usr/include/stdio.h にある.
  • 〜.h のファイルをへッダファイルあるいはインクルードファイルと呼ぶ.
  • stdio は STandard I/O (Input/Output) (標準入出力) という意味.C言語の標準的な入出力のための関数やマクロが宣言されている.
  • EOF は stdio.h で定義(#define)されているマクロなので,#include <stdio.h> がないとコンパイルできない.
/*
 * 標準入力から受け取った文字のコードを表示するプログラム
 * 入力が「終わり」になるまで続ける
 */
#include <stdio.h>        /* へッダファイルの取り込み */

main()
{
        int code;
        for (;;) {             /* break 文が実行されるまで繰り返す */
                code = getchar();
                if (code == EOF) { /* 「終わり」になったらループから脱出 */
                        break;
                }
                printf("%c のコードは %d です\n", code, code);
        }
}
/*
 * 標準入力から受け取った文字から空白の数を数えるプログラム
 * 入力が「終わり」になるまで続ける
 */
#include <stdio.h>        /* へッダファイルの取り込み */

main()
{
        int code;
        int count = 0;
        for (;;) {             /* break 文が実行されるまで繰り返す */
                code = getchar();
                if (code == EOF) { /* 「終わり」になったらループから脱出 */
                        break;
                }
                if (code == ' ') { /* 空白を見つけたらカウンタを増やす */
                        count++;
                }
        }
  	printf("Number of blanks = %d\n", count);
}

演習(スペースと行数を数えるプログラム)

標準入力から入力した文字のうち,スペース ' ' の数と行数を数えるプログラムを書け.

This is a pen(リターン)
That is a book(リターン)
(ここでControl-Dを入力)
スペースの数 = 6, 行数 = 2

行数を数えるには,改行('\n')の数を数えれば良い.

演習(行番号を付与するプログラム)

標準入力から入力した各行に行番号を付与するプログラムを書け.

This is a pen(リターン)
1: This is a pen
That is a book(リターン)
2: This is a book
(ここでControl-Dを入力)
k-abe@media.osaka-cu.ac.jp