• 検索結果がありません。

C言語入門

N/A
N/A
Protected

Academic year: 2021

シェア "C言語入門"

Copied!
84
0
0

読み込み中.... (全文を見る)

全文

(1)

C言語入門

第15週

プログラミング言語Ⅰ(実習を含む。),

計算機言語Ⅰ・計算機言語演習Ⅰ,

(2)

乱数

(3)

乱数: ランダムな数値を得る

• rand 関数

:

疑似乱数整数の生成

• srand 関数

:

疑似乱数系列の初期化

• time 関数

:

現在時刻の取得

毎回違う値を得るため

乱数系列初期化に用いる

教科書 p.318, 322.

(4)

rand 関数

• int rand(void)

• [0:RAND_MAX]の範囲で整数の疑似乱数を返す

• 戻り値:

• 疑似乱数の整数を返す

• 値の範囲は 0 以上 RAND_MAX 以下

• RAND_MAX は stdlib.h で定義されている

• RAND_MAX は少なくとも 32767 以上である

• RAND_MAX + 1 はオーバーフローするかもしれ

ない

JM /

rand

(3)

教科書 p.322.

(5)

srand 関数

• void srand(unsigned int seed)

• 疑似乱数系列の初期化を行う

• 引数:

• seed: 疑似乱数の新しい系列の種

初期値は1

• 同じ種からは毎回同じ疑似乱数系列が生成

される。

JM /

rand

(3)

教科書 p.322.

(6)

疑似乱数とは?

• 演算で生成する疑似的な乱数

• POSIX 1003.1-2003 で挙げられている実装例

static unsigned long next = 1;

/* RAND_MAX を 32767 と仮定 */

int myrand(void) {

next = next * 1103515245 + 12345;

return((unsigned)(next/65536) % 32768);

}

void mysrand(unsigned int seed) {

next = seed;

}

計算式は決まっているので、

同じseedなら

毎回同じ計算になるため

毎回同じ乱数系列が生成される

(7)

乱数系列の確認

• seed の値で乱数系列がどうなるか確認

rand_ex1.c int seed, i; printf("seed = "); scanf("%d", &seed); srand(seed); printf("RAND_MAX: %d¥n", RAND_MAX); for (i = 0; i < 10; i++) { printf("%d¥n", rand()); }

乱数系列の初期化

6 7 8 9 10 11 12 13 14 15 16

Cygwin64 mintty + bash

$ gcc rand_ex1.c && ./a seed = 1 RAND_MAX: 2147483647 1481765933 1085377743 1270216262 1191391529 812669700 553475508 445349752 1344887256 730417256 1812158119

seed が同じなら

毎回同じ乱数系列が生成される

(8)

乱数の初期化

(9)

実行毎に異なる乱数系列に初期化

• seed の値で乱数系列がどうなるか確認

rand_ex2.c int i; srand(time(NULL)); printf("RAND_MAX: %d¥n", RAND_MAX); for (i = 0; i < 10; i++) { printf("%d¥n", rand()); } 6 7 8 9 10 11 12

13 Cygwin64 mintty + bash

$ gcc rand_ex2.c && ./a RAND_MAX: 2147483647 408068090 654635880 1819541412 1080013827 1356279002 1536746152 352225876 1197042546 1830476305 459739427

毎回 seed が異なるため

毎回違う乱数系列が生成される

time関数は

現在時刻を返す関数

(10)

time 関数

• time_t time(time_t *t)

• 現在時刻を UNIX time で得る

• 引数:

• t:

通常はNULLで良い

NULLでない場合*tにも戻り値を格納する

• 戻り値:

• 現在時刻を UNIX time で返す。

JM /

time

(2)

教科書 p.318.

(11)

UNIX time (UNIX時間、UNIX時刻)

• UNIX epoch (UNIX 紀元)

• 1970-01-01 00:00:00 UTC

• UNIX time

• UNIX epoch からの経過秒数

2038年問題

• 2038-01-19 03:14:07 UTC

= UNIX time: 2,147,483,647秒

= UNIC time: 0x7fffffff秒

• time_tが符号付き32bitの環境でオーバーフロー

EppochConverter

(12)

2038年問題

• 2038-01-19 03:14:07 UTC

= UNIX time: 2,147,483,647秒

= UNIC time: 0x7fffffff秒

• time_t が符号付き 32bit の環境

• time_t がオーバーフロー

• 以降、正しい日時が処理できなくなる!

• 対策

• time_t の 64bit 化等の対応が必要

(13)

未対策の環境はあるのか?

• SOURCEFORGE.JP MAGAZINE / 2014-05-02:

2038年問題に対応した「OpenBSD 5.5」リリース

http://sourceforge.jp/magazine/14/05/02/160000

• OpenBSD はセキュリティ面で非常に定評のある OS

• そんな OS でもつい最近になってようやく対応してい

る状況もある。

(14)

time_t の確認

• 0x7fffffff秒,0x80000000秒,-1秒を確認

time_t_test.c

char buf[1024];

time_t t = 0x7fffffff;

struct tm *tm;

printf("sizeof(time_t): %d¥n", sizeof(time_t));

printf("time_t has sign: %s¥n", (~(time_t) 0) < (time_t) 0 ? "YES" : "NO");

tm = gmtime(&t);

strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S UTC", tm);

printf("%20luUL: %11ldL: %s¥n", (unsigned long) t, (long) t, buf);

t++;

tm = gmtime(&t);

strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S UTC", tm);

printf("%20luUL: %11ldL: %s¥n", (unsigned long) t, (long) t, buf);

t = -1;

tm = gmtime(&t);

strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S UTC", tm);

(15)

strftime 関数

• size_t strftime(char *s, size_t max,

const char *format,

const struct tm *tm)

• 日付と時刻を文字列に変換する

• 引数:

• s:

変換結果の格納先(通常はchar型配列)

• max: sのサイズ

• format: 変換の書式

• tm:

time_t 型の値をlocaltime関数または

gmtime関数を用いて変換した日付と時刻情報

• 戻り値:

• 終端文字列'¥0'を含めた変換結果のサイズ

• 格納先のサイズが不足していた場合は0

JM /

strftime

(3)

JM /

ctime

(3)

(16)

各環境のtime_tの状況

Cygwin64 + GNU C

$ gcc time_t_test.c && ./a

sizeof(time_t): 8

time_t has sign: YES

2147483647UL: 2147483647L: 2038-01-19 03:14:07 UTC

2147483648UL: 2147483648L: 2038-01-19 03:14:08 UTC

18446744073709551615UL: -1L: 1969-12-31 23:59:59 UTC

Cygwin32 + GNU C

$ gcc time_t_test.c && ./a

sizeof(time_t): 4

time_t has sign: YES

2147483647UL: 2147483647L: 2038-01-19 03:14:07 UTC

2147483648UL: -2147483648L: 1901-12-13 20:45:52 UTC

4294967295UL: -1L: 1969-12-31 23:59:59 UTC

(17)

各環境のtime_tの状況

Borland C++ 5.5.1

>bcc32 time_t_test.c && time_t_test

Borland C++ 5.5.1 for Win32 Copyright (c) 1993, 2000 Borland

time_t_test.c:

Turbo Incremental Link 5.00 Copyright (c) 1997, 2000 Borland

sizeof(time_t): 4

time_t has sign: YES

2147483647UL: 2147483647L: 2038-01-19 03:14:07 UTC

2147483648UL: -2147483648L: 2038-01-19 03:14:08 UTC

4294967295UL: -1L: 2106-02-06 06:28:15 UTC

(18)

各環境のtime_tの状況

Visual Studio 2013 Express Desktop Windows 32bit版

>cl time_t_test.c && time_t_test

Microsoft(R) C/C++ Optimizing Compiler Version 18.00.21005.1 for x86

Copyright (C) Microsoft Corporation. All rights reserved.

time_t_test.c

Microsoft (R) Incremental Linker Version 12.00.21005.1

Copyright (C) Microsoft Corporation. All rights reserved.

/out:time_t_test.exe

time_t_test.obj

sizeof(time_t): 8

time_t has sign: YES

2147483647UL: 2147483647L: 2038-01-19 03:14:07 UTC

2147483648UL: -2147483648L: 2038-01-19 03:14:08 UTC

4294967295UL: -1L: 1969-12-31 23:59:59 UTC

VC は long が 32bit だったので

64bit 表示出来てない点には注意

(19)

各環境のtime_tの状況

Visual Studio 2013 Express Desktop Windows 64bit版

>cl time_t_test.c && time_t_test

Microsoft(R) C/C++ Optimizing Compiler Version 18.00.21005.1 for x64

Copyright (C) Microsoft Corporation. All rights reserved.

time_t_test.c

Microsoft (R) Incremental Linker Version 12.00.21005.1

Copyright (C) Microsoft Corporation. All rights reserved.

/out:time_t_test.exe

time_t_test.obj

sizeof(time_t): 8

time_t has sign: YES

2147483647UL: 2147483647L: 2038-01-19 03:14:07 UTC

2147483648UL: -2147483648L: 2038-01-19 03:14:08 UTC

4294967295UL: -1L: 1969-12-31 23:59:59 UTC

VC は long が 32bit だったので

64bit 表示出来てない点には注意

(20)

乱数の範囲調整

(21)

[0:1) の実数の乱数生成法

• rand(): [0:RAND_MAX]の整数の乱数を生成

• [0:1) を得るには?

• 実数にして RAND_MAX + 1 で割れば良い

• RAND_MAX って幾つ?

• RAND_MAX + 1 だとオーバーフローするかも?

• RAND_MAX + 1.0 なら大丈夫

#define frand() (rand() / (RAND_MAX + 1.0))

[1] p.205.

暗黙の算術変換により

全てdoubleに型変換されて

計算される。

(22)

[0:N-1]の整数の乱数生成法

• [0:1) の実数の乱数を生成してNを掛けた後

整数に変換する

int x;

x = frand() * N;

なぜ以下の計算方法では駄目か?

x = rand() / RAMD_MAX * N;

x = rand() / RAMD_MAX * (N – 1);

x = frand() * (N – 1);

ヒント:

• 生成される値の範囲は?

• N が出る確率は?

[0:N-1] の整数の乱数

= [0:N) の整数の乱数

(23)

N面体のサイコロ

• [1:N] の整数が等確率で欲しい

int x;

x = frand() * N + 1;

[1:N] の整数の乱数

= [0:N-1] + 1 の整数の乱数

= [0:N) + 1 の整数の乱数

(24)

教科書の例

• 実は間違っている

test_p322.c

#include <stdio.h>

#include <stdlib.h>

#include <time.h>

#define RANGE_MIN 0

#define RANGE_MAX 10

void main()

{

int rand10; // 0以上未満

srand( (unsigned)time(NULL) );

rand10=(int)(((double) rand() / (double) RAND_MAX)

* RANGE_MAX + RANGE_MIN);

printf("求まった乱数は %d¥n", rand10);

}

0~9 までは

(RAND_MAX / 10) / (RAND_MAX + 1)

の確率で出現するので

0以上10以下の乱数を意図したとしても

出現確率のバランスが悪い

rand() は

0 以上 RAND_MAX 以下

の値を返すので、この実装では

1/(RAND_MAX+1) の確率で

10 が出現してしまう

ここのコメントも

おかしいが、

0以上10未満でも

0以上10以下でも

やってはいけない実装

教科書 p.322.

乱数に関してよく見られる

有名な間違いです。

(25)

もっと質の良い疑似乱数

• random 関数 (POSIX.1-2001.)

• 非線形加法フィードバック

• JM /

random

(3)

• drand48 関数 (POSIX.1-2001.)

• 線形合同法+48bit整数

• JM /

drand48

(3)

• メルセンヌツイスタ

• Wikipedia /

メルセンヌツイスタ

(26)

ファイル操作

(27)

標準入出力と標準エラー出力

• 以下の入出力が利用できる

• stdin: STanDard INput:

標準入力

• stdout: STanDard OUTput:

標準出力

• stderr: STanDard ERRor output: 標準エラー出力

• scanf や getchar 等は stdin から入力している

• printf や putchar 等は stdout へ出力している

• stdin, stdout はパイプやリダイレクトの対象だ

が stderr は標準では対象外

(28)

標準入出力と標準エラー出力

• パイプやリダイレクトで処理されたくない内容

は stderr へ出力する

• fscanf や fprintf を使うと、入出力先を自由に

選択出来る

[1] pp.196, 199, 218.

stdiotest.c

printf("output to stdout with printf¥n");

fprintf(stdout, "output to stdout with fprintf¥n");

fprintf(stderr, "output to stderr with fprintf¥n");

mintty + bash

$ ./stdiotest > redirect.txt

output to stderr with fprintf

$ cat redirect.txt

output to stdout with printf

output to stdout with fprintf

(29)

標準入出力と標準エラー出力

• stdin,stdout,stderrはstdio.hで定義されている

• stdio.h は standard input / output header

(30)

fprintf 関数

• int fprintf(FILE *fp,

const char *FORMAT, ...);

• printfの結果をfpへ書き出す

• 引数:

• fp:

FILE 構造体へのポインタ

• FORMAT:

書式

• ...:

任意の数の引数

• 戻り値:

• 書き出された文字数

• エラーの場合負の数

教科書 pp.61, 64-66, 98, 300.

参考: [1] pp.305-306.

(31)

fscanf 関数

• int fscanf(FILE *fp,

const char *FORMAT, ...);

• fpからデータを読み込む

• 引数:

• fp:

FILE 構造体へのポインタ

• FORMAT:

書式

• ...:

任意の数の引数

値を格納する変数へのポインタ

• 戻り値:

• 変換され代入された入力項目の数

• ファイル終端またはエラーの場合EOF

教科書 pp.80-83, 254.

参考: [1] pp.307-309.

(32)

fopen 関数

• FILE *fopen(const char *filename,

const char *mode);

• ファイルを開き、FILE構造体へのポインタを得る

• 引数:

• filename: ファイル名(パス)の文字列

• mode:

ファイルを開くモード

• 戻り値:

• FILE 構造体へのポインタ

• エラーの場合 NULL

教科書 pp.298-305.

参考: [1] pp.194-198.

(33)

fopen 関数の mode

mode 読み込み

書き込み

動作

"r"

任意の位置 ×

ファイルを開く、存在しない場合エラー

"w"

×

任意の位置

ファイルを作成し、前の内容は消去する

"a"

×

ファイル末尾 ファイルを開く、または作成

"r+"

任意の位置 任意の位置

ファイルを開く、存在しない場合エラー

"w+"

任意の位置 任意の位置

ファイルを作成し、前の内容は消去する

"a+"

任意の位置 ファイル末尾 ファイルを開く、または作成

教科書 pp.298-305.

参考: [1] pp.194-198.

"r", "w", "a", "r+", "w+", "a+" はテキストモードで読み書きする

テキストモードでは改行コード(¥n)の扱いが環境によって異なる

• Windows:

CR LF (0xd 0xa)

• Mac:

CR (0xd)

• UNIX:

LF (0xa)

バイナリモードにするには、"rb", "wb", "ab", "r+b", "w+b", "a+b" のように

"b" を追加する

(34)

fclose 関数

• int fclose(FILE *fp);

• ファイルを閉じます

• 引数:

• fp:

FILE構造体へのポインタ

• 戻り値:

• 0 を返す

• エラーの場合 EOF を返す

教科書 pp.298-305.

参考: [1] pp.194-198.

(35)

ファイル入力の例

fprintf_ex1.c

#include <stdio.h> #include <stdlib.h> int main() { FILE *fp; // ファイル入出力用のポインタ int value = 123; fp = fopen("sample.txt", "w"); // 書き込みモードでファイルを開く if (fp == NULL) { // エラー処理

fprintf(stderr, "Error: fopen: sample.txt¥n"); exit(EXIT_FAILURE);

}

fprintf(fp, "%d¥n", value); // fp に value の値を出力

fclose(fp); // 使い終わったファイルを閉じる return EXIT_SUCCESS; }

(36)

ファイル出力の例

fscanf_ex1.c

#include <stdio.h> #include <stdlib.h> int main() { FILE *fp; // ファイル入出力用のポインタ int value; fp = fopen("sample.txt", "r"); // 読み込みモードでファイルを開く if (fp == NULL) { // エラー処理

fprintf(stderr, "Error: fopen: sample.txt¥n"); exit(EXIT_FAILURE); } fscanf(fp, "%d", &value); // fp から符号付き整数の文字列を読み込む printf("%d¥n", value); // 読み込んだ値を表示 fclose(fp); // 使い終わったファイルを閉じる return EXIT_SUCCESS; }

(37)

テスト

(38)

ユニットテスト(単体テスト)

• 標準ライブラリヘッダ <assert.h>

• assert マクロの利用

• ユニットテストツールの利用

• CUnit

http://cunit.sourceforge.net/

教科書pp.188-189.

CUnit のインストール (cygwin)

(39)

assert マクロ

void assert(int expression)

• expression がゼロの場合以下のメッセージを

stderr に出力し abort する

Assertion failed: expression, file filename, line nnn

• <assert.h>をインクルードする時点で NDEBUG

マクロが定義されていると assert マクロは無

視される

• ユニットテストだけでなくデバッグ時のみ有効に

する不正値のチェック等でも利用される

JM /

assert

(3)

(40)

assert マクロ

• 専用のテストルーチンで使用した例

is_leap_year_assert.c

void test_leap_year() { assert(is_leap_year(-400) == 1); assert(is_leap_year(- 56) == 1); assert(is_leap_year(- 4) == 1); assert(is_leap_year( 0) == 1); assert(is_leap_year( 4) == 1); assert(is_leap_year( 56) == 1); assert(is_leap_year( 400) == 1); assert(is_leap_year(1996) == 1); assert(is_leap_year(2000) == 1); assert(is_leap_year(2004) == 1); }

is_leap_year_assert.c

void test_normal_year() { assert(is_leap_year(-300) == 0); assert(is_leap_year(-200) == 0); assert(is_leap_year(-100) == 0); assert(is_leap_year(- 3) == 0); assert(is_leap_year(- 2) == 0); assert(is_leap_year(- 1) == 0); assert(is_leap_year( 1) == 0); assert(is_leap_year( 2) == 0); assert(is_leap_year( 3) == 0); assert(is_leap_year( 100) == 0); assert(is_leap_year( 200) == 0); assert(is_leap_year( 300) == 0); assert(is_leap_year(1900) == 0); assert(is_leap_year(1997) == 0); assert(is_leap_year(1998) == 0); assert(is_leap_year(1999) == 0); assert(is_leap_year(2001) == 0); assert(is_leap_year(2002) == 0); assert(is_leap_year(2003) == 0); }

is_leap_year_assert.c

int main() { test_leap_year(); test_normal_year(); return EXIT_SUCCESS; }

(41)

assert マクロ

• 専用のテストルーチンで使用した例

• エラーがなければ何も起きない

• エラーがあるとそこで実行が中断する

mintty + bash + GNU C

mintty + bash + GNU C

$ gcc is_leap_year_assert.c is_leap_year_func_ex4_2.c && ./a

$ gcc is_leap_year_assert.c is_leap_year_func_practice1.c && ./a

assertion "is_leap_year(-300) == 0" failed: file "is_leap_year_assert.c", line 21, function: test_normal_year

(42)

CUnit

• 専用のテストルーチンを作成して使用

is_leap_year_cunit.c

void test_leap_year() { CU_ASSERT_EQUAL(is_leap_year(-400), 1); CU_ASSERT_EQUAL(is_leap_year(- 56), 1); CU_ASSERT_EQUAL(is_leap_year(- 4), 1); CU_ASSERT_EQUAL(is_leap_year(1996), 1); CU_ASSERT_EQUAL(is_leap_year(2000), 1); CU_ASSERT_EQUAL(is_leap_year(2004), 1); }

is_leap_year_cunit.c

void test_normal_year() { CU_ASSERT_EQUAL(is_leap_year(-300), 0); CU_ASSERT_EQUAL(is_leap_year(-200), 0); CU_ASSERT_EQUAL(is_leap_year(-100), 0); CU_ASSERT_EQUAL(is_leap_year(- 3), 0); CU_ASSERT_EQUAL(is_leap_year(2002), 0); CU_ASSERT_EQUAL(is_leap_year(2003), 0); }

is_leap_year_cunit.c

static CU_TestInfo test_is_leap_year[] = { {"leap year", test_leap_year},

{"normal year", test_normal_year}, CU_TEST_INFO_NULL,

};

static CU_SuiteInfo suites[] = {

{"is_leap_year test", NULL, NULL, test_is_leap_year}, CU_SUITE_INFO_NULL, }; int main() { CU_initialize_registry(); CU_register_suites(suites); CU_basic_set_mode(CU_BRM_VERBOSE); CU_basic_run_tests(); CU_cleanup_registry(); return EXIT_SUCCESS; }

(43)

CUnit

• 専用のテストルーチンを作成して使用

• ユニットテストの達成状況がレポートされる

mintty + bash + GNU C

$ gcc is_leap_year_cunit.c is_leap_year_func_ex4_2.c -lcunit && ./a CUnit - A unit testing framework for C - Version 2.1-2

http://cunit.sourceforge.net/ Suite: is_leap_year test

Test: leap year ...passed Test: normal year ...passed

Run Summary: Type Total Ran Passed Failed Inactive suites 1 1 n/a 0 0 tests 2 2 2 0 0 asserts 29 29 29 0 n/a Elapsed time = 0.000 seconds

テストの通過状況の

統計が表示される

(44)

CUnit

mintty + bash + GNU C

$ gcc is_leap_year_cunit.c is_leap_year_func_practice1.c -lcunit && ./a CUnit - A unit testing framework for C - Version 2.1-2

http://cunit.sourceforge.net/ Suite: is_leap_year test

Test: leap year ...passed Test: normal year ...FAILED

1. is_leap_year_cunit.c:21 - CU_ASSERT_EQUAL(is_leap_year(-300),0) 2. is_leap_year_cunit.c:22 - CU_ASSERT_EQUAL(is_leap_year(-200),0) 3. is_leap_year_cunit.c:23 - CU_ASSERT_EQUAL(is_leap_year(-100),0) 4. is_leap_year_cunit.c:30 - CU_ASSERT_EQUAL(is_leap_year( 100),0) 5. is_leap_year_cunit.c:31 - CU_ASSERT_EQUAL(is_leap_year( 200),0) 6. is_leap_year_cunit.c:32 - CU_ASSERT_EQUAL(is_leap_year( 300),0) 7. is_leap_year_cunit.c:33 - CU_ASSERT_EQUAL(is_leap_year(1900),0) Run Summary: Type Total Ran Passed Failed Inactive

suites 1 1 n/a 0 0 tests 2 2 1 1 0 asserts 29 29 22 7 n/a Elapsed time = 0.000 seconds

テストの通過状況の

統計が表示される

(45)

変更箇所の管理

(46)

diff のインストール

• mintty+bash から以下のコマンドを実行

mintty + bash

(47)

diff

• UNIX系のファイル比較コマンド

mintty + bash

$ diff is_leap_year_func_practice1.c is_leap_year_func_ex4_2.c

5c5

< return year % 4 == 0;

---

> return (year % 4 == 0 && year % 100 != 0) || year % 400 == 0;

上記の例では

ファイル間の相違点を

一覧として表示している

(48)

diff

• 並列表示

• --side-by-sideオプションによる比較

mintty + bash

$ diff is_leap_year_func_practice1.c is_leap_year_func_ex4_2.c --side-by-side

#include "is_leap_year_func.h" #include "is_leap_year_func.h" int is_leap_year(int year) int is_leap_year(int year) { {

return year % 4 == 0; | return (year % 4 == 0 && year % 100 != 0) || year % 400 == } }

(49)

fc

• Windows 標準添付のファイル比較コマンド

コマンドプロンプト

>fc is_leap_year_func_practice1.c is_leap_year_func_ex4_2.c

ファイル is_leap_year_func_practice1.c と IS_LEAP_YEAR_FUNC_EX4_2.C を比較しています

***** is_leap_year_func_practice1.c

{

return year % 4 == 0;

}

***** IS_LEAP_YEAR_FUNC_EX4_2.C

{

return (year % 4 == 0 && year % 100 != 0) || year % 400 == 0;

}

(50)

WinMerge

• GUIによるテキストファイルの比較ツール

• 相違点の合成も出来る

http://www.forest.impress.co.jp/library/software/winmerge/

UNIX 環境の GUI 版の比較ツールだと meld や tkdiff 等がある

(51)

Rekisa

• GUIによる複数テキストファイルの比較ツール

(52)

差分とパッチ

• diff : 差分比較、パッチ作成ツール

• JM /

diff

(1)

• Wikipedia /

diff

• patch : 差分適用(パッチ適用)ツール

• JM /

patch

(1)

• Wikipedia /

patch

単一ファイルのパッチの作成

$ diff -c myfile.orig myfile > myfile.patch

パッチの適用

$ patch < myfile.patch

ディレクトリ以下のパッチの作成

$ diff -crN mydir.orig mydir > mydir.patch

カレントディレクトリへのパッチの適用

$ patch -p0 -d. < mydir.patch

ファイル間の差異を

パッチ(=絆創膏)として取り出し

(53)

バージョン管理ツール

RCS

• Wikipedia /

Revision Control System

CVS

• Wikipedia /

Concurrent Version System

Subversion

• Wikipedia /

Apache Subversion

Mercurial

• Wikipedia /

Mercurial

Bazaar

• Wikipedia /

Bazaar

git

• Wikipedia /

git

github

人気になっている

の登場で

過去の改変の記録を残したり

複数人で共同で作業する際に

役立つ

(54)

テキスト画面の簡易制御

(55)

getchar 関数

• int getchar(void)

• 入力 stream から1文字読み込む

• stream というのはバッファのようなもの

• 通常はENTERが押されるまで入力streamには値

が入って来ない。入力ストリームに値がない場合

は値が入ってくるまで待機する

• 戻り値:

• 入力された文字の文字コード返す

• ファイル終端やエラーの場合はEOFを返す

JM /

fgetc

(3)

(56)

getchar 関数の動作

• ENTERが押されるまで一気に読み込む

getchartest.c

#include <stdio.h>

#include <stdlib.h>

int main()

{

int c;

while ((c = getchar()) != EOF) {

printf("%#04x¥n", c);

}

return EXIT_SUCCESS;

}

バッファリングと言います。

読み込み処理を

高速化するための仕組みです。

バッファリングに溜めてある

入力文字を1文字ずつ取り出します。

バッファが空になると

ENTERが押されるまで

入力待ちの状態になります。

(57)

tty_getkey

• ENTER待ちなしのキーボード入力

tty_getkey_ex1.c #include "tty_getkey.h" #include "msleep.h" int main() { int c; tty_begin(); // 開始処理 while(tty_iskeyhit() == 0) { // 打鍵待ちループ

msleep(1); // CPU に負荷をかけずに 1 msec 待つ

// tty_ishitkey() は即座に値を返すので空ループだと CPU に負荷がかかる

}

c = tty_getkey(); // 打鍵キーの取得

tty_printf("%#x key was hit.¥n", c); // tty 用の printf

tty_printf("Hit ESC key to exit.¥n");

while(KEY_ESC != tty_getkey()) { // 打鍵待ちループ ; // tty_ketkey() はキー入力があるまで待機するため空ループでも CPU に負荷をかけない } tty_end(); // 終了処理 return EXIT_SUCCESS; }

(58)

tty_getkey

• ENTER待ちなしのキーボード入力

• Windows 系の環境

• conio.h ライブラリを利用

• embarcadero / RAD Studio /

conio.h

• MSDN /

Console and Port I/O

• UNIX 系の環境

• curses ライブラリを利用

(59)

tty_getkey を利用したプログラムの

コンパイル

• サンプルプログラム

• tty_getkey_ex1.c : サンプルプログラム本体

• 必要なファイル

• msleep.h

: ミリ秒 sleep 用ヘッダ

• tty_getkey.h

: tty_getkey ヘッダファイル

• tty_getkey.c

: tty_getkey 本体

mintty + bash + GNU C

$ gcc tty_getkey_ex1.c tty_getkey.c -lcurses

コマンドプロンプト + Borland C++

>bcc32 tty_getkey_ex1.c tty_getkey.c

gcc では

-lcurses オプションが必要

(60)

tty_getkey 利用前の準備

Cygwin の場合

• ncurses の開発用ライブラリが必要

• 以下のコマンドを入力してインストール

• Borland C++ では、標準添付の conio というラ

イブラリを使っているので前準備は不要

Cygwin64 mintty + bash

apt-cyg install libncursesw-devel

Cygwin32 mintty + bash

(61)

Cygwinが何bit版か確認する方法

• uname コマンドに -a オプションを付けて実行

Cygwin64 mintty + bash

$ uname -a

CYGWIN_NT-6.1 EX58EXTREME 1.7.27(0.271/5/3) 2013-12-09 11:54 x86_64 Cygwin

Cygwin32 mintty + bash

$ uname -a

CYGWIN_NT-6.1-WOW64 EX58EXTREME 1.7.27(0.271/5/3) 2013-12-09 11:57 i686 Cygwin

i686 なら 32bit 版

x86_64 なら 64bit 版

(62)

tty_getkey 初期化関数

• int tty_begin(void)

• tty_getkey の初期化処理を行います

• int tty_end(void)

(63)

tty_getkey キー待ち受け関数

• int tty_iskeyhit(void)

• キー入力の有無を調べます。

• キー入力があれば 1 なければ 0 を返します。

• int tty_getkey(void)

• キー入力を取得します。キー入力がない場合、キー

入力が発生するまで待機します。

• 通常のキーは'a'や'A'等の文字コードを返します。

• 特殊キーの場合はKEY_UPやKEY_DOWN等のマクロで

定義されたキーコードを返します。

(64)

tty_getkey() が返すキーコード

• KEY_INSERT

• KEY_DELETE

• KEY_HOME

• KEY_END

• KEY_PAGEUP

• KEY_PAGEDOWN

• KEY_UP

• KEY_DOWN

• KEY_LEFT

• KEY_RIGHT

• KEY_ESC

• KEY_TAB

• KEY_SPACE

• KEY_BS

• KEY_ENTER

• KEY_F1 ~ KEY_F48

通常のキーは 'a', 'A' 等の

文字定数リテラルが対応

(65)

tty_getkey 出力関数

• int tty_printf(char *fmt, ....)

• 書式付の出力を行います。

• 画面制御を伴うためtty_begin()~tty_end()

の間では、通常のprintfは使わないでください。

• int tty_setxy(int x, int y)

(66)

tty_getkey 画面情報関数

• int tty_getx(void)

• カーソルの x 座標を返します。

• int tty_gety(void)

• カーソルの y 座標を返します。

• int tty_getw(void)

• カーソルが移動可能な画面の幅を返します。

• int tty_geth(void)

• カーソルが移動可能な画面の高さを返します。

(67)

tty_getkey_ex2.c

• カーソルキーで移動、ESC キーで終了

• 移動した場所に * を表示する

(68)

tty_getkey_ex2.c

tty_getkey_ex2.c

while (KEY_ESC != (c = tty_getkey())) {

switch (c) {

case KEY_UP: y--; break;

case KEY_DOWN: y++; break;

case KEY_RIGHT: x++; break;

case KEY_LEFT: x--; break;

}

x = x < 1 ? 1 : w - 2 < x ? w - 2 : x;

y = y < 1 ? 1 : h - 2 < y ? h - 2 : y;

tty_setxy(0, 0);

tty_printf("(%2d,%2d) : %#06x", x, y, c);

tty_setxy(x, y);

tty_printf("*");

}

入力された

カーソルキーの方向に応じて

座標を上下左右に移動

画面から

はみ出さないように

移動範囲を制限

(69)

tty_getkey_ex3.c

• 6面体サイコロの例

• 開始するとサイコロが転がり始める

• 何かキーを押すと3秒待って終了する

tty_getkey_ex3.c

while(tty_iskeyhit() == 0) {

d = frand() * 6 + 1;

tty_setxy(0, 0);

tty_printf("%d", d);

msleep(1);

}

msleep(3000);

値域 [1:6] の乱数生成

= 6面体サイコロ

(70)

総合実習

(71)

tetris.c

• テトリスの簡易版

• 操作方法

• 移動: ← →

• 落下: ↓

• 回転: z x SPACE

• 修了: ESC

mintty + bash + GNU C

$ gcc tetris.c tty_getkey.c -lcurses && ./a

コマンドプロンプト + Borland C++

>bcc32 tetris.c tty_getkey.c && tetris

mintty + bash + GNU C ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## #### ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## #### ## ## ## #### ## #### #### ## ############## ## ############## ####################

(72)

tetris.c

• フィールド

描画用フィールド

field[0][y][x]

固定ブロック用フィールド

field[1][y][x]

浮動ブロック

b[y][x]

ブロック形状

block[spec][y][x]

仮り配置

選択して回転

落下したら固定

描画用フィールドにコピー

(73)

tetris.c の改造

• 以下の改造を考えてみよう

• 「p」でポーズ/解除する

• 点数を表示する

• 次のブロックをランダムに決める

• 次のブロックを表示する

• 上まで積み上がったら

ゲームオーバーにする

• ハイスコアを記録・表示してみる

mintty + bash + GNU C

## ## HISCORE: 001000 ## ## Score : 001000 ## ## ## ## NEXT ## ## ++---++ ## ## ## || || ## ## ## || ## || ## #### ## || #### || ## ## || ## || ## ## ++---++ ## ## ## ## ## ## ## ## ## ## #### ## ## ## #### ## #### #### ## ############## ## ############## ####################

(74)

tetris.c の改造

ヒント

• 「p」でポーズ/解除する

• ポーズフラグ用の変数が必要

• 「p」キーでポーズフラグを反転する

• ポーズフラグがONなら continue してループを先

頭からやり直す

フラグの反転

// 以下の処理はいずれも

// pause == 0 なら 1

// pause != 0 なら 0

// となる

pause = pause ? 0 : 1;

pause = !pause;

pause = pause == 0;

(75)

tetris.c の改造

ヒント

• 点数を表示する

• 点数用の変数が必要

• ブロックの落下や

1列消した場合に

スコアを加算する

• 適当な位置にスコアを表示する

• 位置調整は tty_setxy()

• 表示は tty_printf()

mintty + bash + GNU C

## ## HISCORE: 001000 ## ## Score : 001000 ## ## ## ## NEXT ## ## ++---++ ## ## ## || || ## ## ## || ## || ## #### ## || #### || ## ## || ## || ## ## ++---++ ## ## ## ## ## ## ## ## ## ## #### ## ## ## #### ## #### #### ## ############## ## ############## ####################

例えばこの位置に表示するなら

tty_printf() の前に

tty_setxy(W*2+2,1);

を入れる

(76)

tetris.c の改造

ヒント

• 次のブロックをランダムに決める

• spec の値を乱数で決定すれば良い

• 前述の frand() マクロを使うと

[0:7) の乱数は frand()*7 で得られる

(77)

tetris.c の改造

ヒント

• 次のブロックを表示する

• 次のブロックを記憶するための変数が必要(※1)

• ※1を使って spec を更新する

• spec を更新後 ※1 も更新する

• ※1 を適当な位置に表示する

• 表示位置は tty_setxy() で

調整する

• 枠は "++---++" と

"||" 以外でも

好きな文字を使えば良い

• block[※1][y][x] の値に応じて

" " または "##" を表示する

mintty + bash + GNU C

## ## HISCORE: 001000 ## ## Score : 001000 ## ## ## ## NEXT ## ## ++---++ ## ## ## || || ## ## ## || ## || ## #### ## || #### || ## ## || ## || ## ## ++---++ ## ## ## ## ## ## ## ## ## ## #### ## ## ## #### ## #### #### ## ############## ## ############## ####################

(78)

tetris.c の改造

ヒント

• 上まで積み上がったらゲームオーバーにする

• y <= 0 でブロックが固定されたら、上まで積み上

がっている

• ゲームオーバーになるとどうするか?

• GAME OVER と表示する?

• その後、再度ゲームをスタートするか?プログラムを

終了するか?

• 一般に流通しているゲームはどうしているか参考にす

ると良い?

(79)

tetris.c の改造

• ハイスコアを記録・表示してみる

• ハイスコア用の変数が必要

• ハイスコアは画面の適当な場所に表示する

• スコアがハイスコアを超えたらハイスコアを更新

する必要がある

• ハイスコアはファイルに保存し、ゲーム開始時に

読み込み終了時に保存する必要がある

• fopen, fclose でファイルのオープン、クローズが行える

• fprintf, fscanf で値の読み書きが行える

(80)

まとめ

(81)

この授業で扱った事

• C言語の基礎

• 演算、式、変数、制御構造、関数、ポインタ等

• 簡単なプログラムの作成

• 奇数偶数の判定

• 閏年の判定

• 行列の演算

• シーザー暗号

• 等々

(82)

この授業で扱わなかったこと

• 構造体と共用体: struct, union

• 型定義: typedef

• 可変長引数: <stdarg.h>

• va_list, va_start, va_arg, va_end

• 非局所ジャンプ: <setjmp.h>

• シグナル処理: <signal.h>

(83)

参考文献

• [1] B.W.カーニハン/D.M.リッチー著 石田晴久

訳、プログラミング言語C 第2版 ANSI 規格準

拠、共立出版(1989)

(84)

おしまい

おつかれさまでした。

来週は試験です。しっかり復習しておきましょう。

お か だ

参照

関連したドキュメント

シートの入力方法について シート内の【入力例】に基づいて以下の項目について、入力してください。 ・住宅の名称 ・住宅の所在地

(7)

サンプル 入力列 A、B、C、D のいずれかに指定した値「東京」が含まれている場合、「含む判定」フラグに True を

パキロビッドパックを処方入力の上、 F8特殊指示 →「(治)」 の列に 「1:する」 を入力して F9更新 を押下してください。.. 備考欄に「治」と登録されます。

・電源投入直後の MPIO は出力状態に設定されているため全ての S/PDIF 信号を入力する前に MPSEL レジスタで MPIO を入力状態に設定する必要がある。MPSEL

• 熱負荷密度の高い地域において、 開発の早い段階 から、再エネや未利用エネルギーの利活用、高効率設 備の導入を促す。.

・発電設備の連続運転可能周波数は, 48.5Hz を超え 50.5Hz 以下としていただく。なお,周波数低下リレーの整 定値は,原則として,FRT

・発電設備の連続運転可能周波数は, 48.5Hz を超え 50.5Hz 以下としていただく。なお,周波数低下リレーの整 定値は,原則として,FRT