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

C言語入門

N/A
N/A
Protected

Academic year: 2021

シェア "C言語入門"

Copied!
105
0
0

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

全文

(1)

C言語入門

第3週

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

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

(2)

先週の復習

(3)

出力のリダイレクト

• 書式: コマンド > ファイル

• コマンドの出力をファイルに繋ぐ

• コマンド実行時の出力をファイルに保存出来る。

• 例: 第1週の hello.c について

mintty + bash

./a > hello_result.txt

コマンドプロンプト

hello > hello_result.txt

hello_result.txt

hello, world

(4)

値の表示

(5)

値の表示

• printf 関数を使う

教科書 pp.61, 64-66, 98. printftest.c

#include <stdio.h>

#include <stdlib.h>

int main()

{

int i = 128;

double d = 123e-3;

char s[] = "hello, world";

printf("i: %d¥n", i);

printf("d: %f¥n", d);

printf("s: %s¥n", s);

return EXIT_SUCCESS;

}

mintty+bash+gcc

$ gcc printftest.c && ./a

i: 128

d: 0.123000

s: hello, world

いろんな値を表示できる。

1

2

3

4

5

6

7

8

9

10

11

12

13

(6)

printf 関数

• int printf(const char *FORMAT, ...);

• 引数:

• FORMAT:

書式

• ...:

任意の数の引数

• 戻り値:

• 書き出された文字数。

• エラーの場合負の数。

教科書 pp.61, 64-66, 98. 参考: [1] pp.305-306.

(7)

printf: 書式

• %~変換文字までをフィールドと呼ぶび、テン

プレート(穴空き定規)ように扱われる

• フィールドは以下の要素から成る

• %[フラグ][最小フィールド幅][.精度][長さ修飾子]変換文字

printf("1 + 2 =

%d

¥n", 1 + 2);

1 + 2 = ¥n

ここに、 int型の整数型データとして解釈した 2つ目の引数の値(上記の例では1+2の計算結果)を 符号付き10進数にして印字する 教科書 pp.61, 64-66, 98. 参考: [1] pp.305-306.

(8)

printf: フラグ

• -: 左揃えで印字

• +: 数を符号付きで印字

• スペース: 最初の文字が符号でない場合スペース

を前に付ける

• 0: フィールド幅いっぱいに左側から0を詰める

• #: 別の出力形式を指定。

• o: 先頭の桁を0にする

• x: 0でない結果の先頭を0xにする

• e,f,g: 出力に必ず小数点を付ける

• g: 末尾の0を削除しない

教科書 pp.61, 64-66, 98. 参考: [1] pp.305-306.

(9)

printf: 最小フィールド幅

• 変換された引数は少なくともこの幅になる。

• 必要ならもっと広い幅のフィールドに印字。

• 変換された引数がフィールド幅よりも短い場

合padding(=詰め物)が行われる。

• paddingは通常はスペース。フラグに0が指

定された場合は0が用いられる。

• *: 次の引数の値を用いる

教科書 pp.61, 64-66, 98. 参考: [1] pp.305-306.

(10)

printf: .精度

• 「.」(ピリオド): フィールド幅と精度の分離子

(separator)

• 文字列に対しては印字する最大文字数

• e,fの対しては小数点以下に印字すべき桁

• gに対しては有効数字の桁数

• 整数に対しては印字すべき最小桁数(頭に0

が付加される)

• *: 次の引数の値を用いる

教科書 pp.61, 64-66, 98. 参考: [1] pp.305-306.

(11)

printf: 長さ修飾子

• h: short または float として扱う

• l: long として扱う

• L: long double として扱う

教科書 pp.61, 64-66, 98. 参考: [1] pp.305-306.

(12)

printf: 変換文字

文字 変換後の引数の型 d, i int; 符号付き10進数 o int; 符号なし8進数 x, X int; 符号なし16進数 u int; 符号なし10進数

c int; unsigned char に変換された後の単一文字

s char *; 文字列を文字列終端('¥0')または指定された桁まで f double; [-]mmm.dddddd 形の10進数。dの桁数は精度で指定 e, E double; [-]m.dddddde±xx型の10進数。dの桁数は精度で指定 g, G double; 指数が-4より小さいか精度以上の場合%e、それ以外は%f扱い p void *; ポインタとして印字(処理系依存) n int *; このprintfでここまでに書き出された文字数を引数に書き込む % %を印字 参考: [1] pp.305-306. 教科書 pp.61, 64-66, 98.

(13)

printf の詳細

• ここでは概略しか示せていないのと一部不正

確な部分もあるので、詳細は bash から man

コマンドを用いて以下の方法で確認すること

• 邦訳は以下のページ

http://linuxjm.sourceforge.jp/html/LDP_man-pages/man3/printf.3.html

mintty+bash

man sprintf

教科書 pp.61, 64-66, 98. mintty+bash

man 3 printf

3 はマニュアルのセクション番号を意味する。 セクション 3 はサブルーチン (つまりライブラリ関数) 関連のマニュアル http://linuxjf.sourceforge.jp/JFdocs/Man-Page-2.html printf はセクション 1 にもあるので sprintf かセクショ ン 3 の printf を引く必要がある。 2015-04-27 修正&追記

(14)

printf のマニュアル導入

• printf のマニュアルが引けない場合、

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

cygwin-doc パッケージのインストール

mintty+bash

apt-cyg install cygwin-doc

(15)

演習: 円の面積を計算せよ(1)

• area_of_a_circle1.c をダウンロードして

/*WYCH1*/

の部分を書き

変えることで以下のプログラムを完成させなさい。

/*WYCH2*/

/*WYCH3*/

の部分は次の演習で変更するので今はまだ書き変

えない事。なお WYCH は Write Your Code Here を略してい

る。

• double 型の変数 r に円の半径を代入する。

• 円の面積𝑆は公式𝑆 = 𝜋𝑟

2

を用いて計算し、結果は小数点以下2桁ま

で出力する。出力には printf と "%f" を用いれば良いが、前述の

精度を設定する必要がある。

• 円の面積を表示する前に、確認のため計算に用いる半径も表示する。

• r に代入する半径はソースコードにリテラル値として直接埋め込む。

異なる半径面積を計算したい時は、ソースコードのリテラル値を書き

変えてコンパイルし直すこととする。

• 半径を 1~10まで 1 刻みで増やして 計10 個の面積を計算せよ。

2015-04-27 追記

(16)

マクロ

• preprocessor のキーワード置換機能

• 書式: #define マクロ名 置換内容

• 定数等に名前を付ける際に使う

macrotest1.c

#include <stdio.h>

#define MSG "world"

void main()

{

printf("hello, %s¥n", MSG);

}

教科書 p.68.

1

2

3

4

5

6

7

8

MSGはコンパイル前に

"world"で置換される

2015-04-25修正 誤:area_of_a_circle1.c 正:macrotest1.c

(17)

マクロ

• コンパイル時のオプション -D で外部から与え

ることも出来る。

macrotest2.c

#include <stdio.h>

void main()

{

printf("hello, %s¥n", MSG);

}

mintty+bash+gcc

$ gcc -DMSG="¥"kou¥"" macrotest2.c && ./a hello, kou

1

2

3

4

5

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

>bcc32 -DMSG="¥"kou¥"" macrotest2.c && macrotest2

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

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

普通はこのままコンパイルしても

MSGがないためエラーになる

2015-04-25修正 誤:extmacrotest2.c 正:macrotest2.c

(18)

マクロ

• PI に関しては実は math.h で提供されている

mintty+bash

$ grep M_PI /usr/include/math.h

#define M_PI 3.14159265358979323846

#define M_PI_2 1.57079632679489661923

#define M_PI_4 0.78539816339744830962

#define M_TWOPI (M_PI * 2.0)

(19)

マクロ

• 先程の EXIT_SUCCESS や SCNd32 もマクロ

mintty+bash

$ grep "#define.EXIT_" /usr/include/stdlib.h

#define EXIT_FAILURE 1

#define EXIT_SUCCESS 0

$ grep SCNd32 /usr/include/inttypes.h

#define SCNd32 "d"

それぞれの環境に

適切な数値や文字列等が設定されている

教科書 p.68.

(20)

ファイルの包含

• preprocessor のファイル取り込み機能

• 別のファイルに記述されたプログラムやマクロ等

を取り込む際に使う

• 書式:

• #include <ファイル名>

//システム提供ファイル用

• /usr/include 等から探して取り込む

• #include "ファイル名"

//ユーザー作成ファイル用

• 作業ディレクトリから探して取り込む

教科書 pp.203-206.

(21)

grep コマンド

• grep [OPTIONS] PATTERN [FILE ...]

• 検索文字列を含むファイルを検索する

• 引数

• PATTERN : 正規表現等による検索文字列

• FILE : 検索対象のファイルやディレクトリ

• OPTIONS

• -R : ディレクトリ下のすべてのファイルを検索

• -n : 行番号を表示

• -A NUM : マッチ位置の後NUM行も表示

• -B NUM : マッチ位置の前NUM行も表示

• -C NUM : マッチ位置の前後NUM行も表示

• マニュアル邦訳

• http://linuxjm.sourceforge.jp/html/GNU_grep/man1/grep.1.html 備考: UNIX コマンド

(22)

正規表現

表記

意味

c

文字c

¥c

文字¥c

.

任意の一文字

[...]

[]内の任意の一文字

[^...]

[]内に含まれない任意の一文字

*

直前のパターンが0回以上反復

+

直前のパターンが1回以上反復

?

直線のパターンが0または1回出現

|

前後の正規表現の何れか

(...)

()内の正規表現をグループ化

^

行頭にマッチ

$

行末にマッチ

備考: UNIX コマンド

(23)

演習: 円の面積を計算せよ(2)

• 先程完成させた area_of_a_circle1.c をコピーして

area_of_a_circle2.c を作成し

/*WYCH1*/

/*WYCH2*/

の部分を書き変えることで以下のプログラムを完成さ

せなさい。

• r に代入する半径としてマクロ R を代入することで、異な

る半径面積を計算したい時は、コンパイル時に -D オプ

ションを用いて 「-DR=1」 のよう外部から値を与えることで、

ソースコードを変更なしに、コンパイルし直すだけで済む

ように変更せよ。

𝜋の値はリテラル値を直接書き込むのではなく math.h で

定義されたマクロ M_PI を用いるように変更せよ。

• 半径を 1~10まで 1 刻みで増やして 計10 個の面積を

計算せよ。

(24)

fprintf 関数

• int fprintf(FILE *fp,

const char *FORMAT, ...);

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

• 引数:

• fp:

FILE 構造体へのポインタ

• FORMAT:

書式

• ...:

任意の数の引数

• 戻り値:

• 書き出された文字数

• エラーの場合負の数

教科書 pp.61, 64-66, 98, 300. 参考: [1] pp.305-306.

(25)

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

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

• stdin :

st

an

d

ard

in

put : 標準入力

• stdout:

st

an

d

ard

out

put : 標準出力

• stderr:

st

an

d

ard

err

or output: 標準エラー出力

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

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

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

が stderr は標準では対象外(指定すれば対

象にすることも可能)

(26)

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

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

は stderr へ出力すると良い

• 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

$ gcc stdiotest.c $ ./a > redirect.txt

output to stderr with fprintf $ cat redirect.txt

output to stdout with printf output to stdout with fprintf

6 7 8 ← 標準出力(stdout)を redirect.txt へリダイレクト ← fprintf で明示的に stderr へ出力した結果(*1) ← redirect.txtに出力された内容を表示 ← printf で暗黙的に stdout へ出力した結果 ← fprintf で明示的に stdout へ出力した結果 以下の例では(*1)がファイルへ出力されず 画面に出力されている事が確認出来る。

(27)

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

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

• stdio.h は

st

an

d

ard

i

nput /

o

utput

h

eader

(28)

値の読み込み

(29)

値の読み込み

• scanf関数を使う

教科書 pp.80-83, 254. mintty+bash+gcc scanftest.c int i; double d; char s[16]; fprintf(stderr, "i = ?¥b"); scanf("%d", &i); fprintf(stderr, "d = ?¥b"); scanf("%lf", &d); fprintf(stderr, "s = ?¥b"); scanf("%s", s); printf("i: %d¥n", i); printf("d: %f¥n", d); printf("s: %s¥n", s);

$ gcc scanftest.c && ./a

i = 1234

d = 1234e-5

s = hello, world

i: 1234

d: 0.012340

s: hello,

キーボードから入力した値を

変数に保存して利用出来る

5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20

(30)

scanf 関数

• int scanf(const char *FORMAT, ...);

• 引数:

• FORMAT:

書式

• ...:

任意の数の引数

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

• 戻り値:

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

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

教科書 pp.80-83, 254. 参考: [1] pp.307-309.

(31)

scanf: 書式

• スペース、タブ: 無視される

• (%でない)普通の文字:

入力の次の空白でない文字とマッチ

• 変換仕様:

• %[*][最大フィールド幅][ターゲット幅]変換文字

int a;

scanf("

%d

", &a);

教科書 pp.80-83, 254. 参考: [1] pp.250, 307-309. 入力文字列を10進数として扱い int型の整数型変数へ代入 スカラ変数の前には & を付ける 配列変数、ポインタ変数には不要 &: アドレス演算子 変数へのポインタを得る

(32)

scanf: 変換仕様

• *: 入力フィールドはスキップされる

代入抑止

• 最大フィールド幅: 読み込む最大文字数

• ターゲット幅:

• h: int を short に

• l: int を long に、float を double に

• L: float を long doubleに

教科書 pp.80-83, 254.

(33)

scanf: 変換文字

文字 入力データ; 引数の型 d 10進数; int * i 整数; int * (頭に0,0xが付くと8,16進数とみなす) o 8進数; int * u 符号なし10進数; unsigned int * x 16進数; int * c 文字; char * (末尾に'¥0'を付加しない) s 非空白文字の文字列; char * (末尾に'¥0'を付加) e,f,g 浮動小数点数; float * p printf("%p") で印字されるポインタ値; void * n これまでに読み込まれた文字数; int * [...] [...]+; char * (末尾に'¥0'を付加) [^...] [^...]+; char * (末尾に'¥0'を付加) % %; 参考: [1] pp.307-309. 教科書 pp.80-83, 254.

(34)

scanf の詳細

• ここでは概略しか示せていないのと一部不正

確な部分もあるので、詳細は bash から man

コマンドを用いて以下の方法で確認すること

• 邦訳は以下のページ

http://linuxjm.sourceforge.jp/html/LDP_man-pages/man3/scanf.3.html

教科書 pp.80-83, 254. mintty+bash

man scanf

(35)

scanf の引数とポインタ

• 値の代入するには変数のアドレスが必要

0x?? 0x~00 0x?? 0x~01 0x?? 0x~02 0x?? 0x~03 : : : : 0x?? 0x~04 0x?? 0x~05 0x?? 0x~06 : : : : 0x?? 0x~07 0x?? 0x~08 32bit

int a;

scanf("%d", &a);

a 0x???????? &a &: アドレス演算子 変数が配置されているメモリ上のアドレスが得られる このアドレスのことをC言語ではポインタと呼ぶ 教科書 pp.80-83, 254. scanf に値の格納先の アドレスを渡す

(36)

scanf: C99 の stdint.h の場合

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

• #include<inttypes.h> してSCN~を使う

• "%hd" → "%"SCNd16

• "%d" → "%"SCNd32

• "%u" → "%"SCNu32

実装依存なので 欲しい桁数が扱えないかも? 使う bit 数を確実に保証出来る

(37)

buffer overflow の脆弱性

• 確保した配列よりも長い文字列を入力

備考

mintty+bash+gcc

$ gcc scanftest.c && ./a

i = 1234

d = 1234e-5

s = 0123456789abcdefg@@@@@@@@@@@@@@@@

i: 1077952576

d: 32.501961

s: 0123456789abcdefg@@@@@@@@@@@@@@@@

他の変数の領域を

侵食してしまう

(38)

buffer overflow の脆弱性の仕組み

• メモリ上の変数の割り当て

0x?? 0x~00 0x?? 0x~0f 0x?? 0x~10 : : : : 0x?? 0x~17 0x?? 0x~1c char s[16]; 0x?? 0x~1f : : : : : : : : double d; int i; 確保したサイズ以上の データを書き込むと 他の変数のデータを 上書きしてしまう。 備考

(39)

buffer overflow の脆弱性の対策

• 最大フィールド幅を明記する!

• "%s" → "%15s"

終端文字列'¥0'も格納する必要があるため、

最大フィールド幅は

確保したバイト数 -1 以下にする必要がある。

char s[16]; なら最大15文字まで

備考

(40)

演習: 円の面積を計算せよ(3)

• 先程完成させた area_of_a_circle2.c をコピーして

area_of_a_circle3.c を作成し

/*WYCH1*/

/*WYCH2*/ /*WYCH3*/

の部分を書き変えること

で以下のプログラムを完成させなさい。

• r に代入する半径を実行時にキーボードから入力す

ることで、コンパイルし直さなくても半径を変更出来る

ように変更せよ。scanf と "%lf" を利用すれば良い。

• r の入力を求める際は "r = ?¥b" を標準エラー出

力に予め表示せよ。なお ¥b はバックスペースを表す

エスケープシーケンスである。

• 半径を 1~10まで 1 刻みで増やして 計10 個の

面積を計算せよ。

(41)
(42)

sizeof 演算子

• コンパイル時に変数やデータ型の割り当てバ

イト数を求める演算子

• sizeof オブジェクト

• sizeof(型名)

教科書 p.78, 84, 195.

(43)

sizeof 演算子の例1

• 例)各データ型の割り当てバイト数

教科書 p.78, 84, 195. sizeof_ex1.c

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

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

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

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

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

#ifndef __BORLANDC__

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

#endif

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

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

printf("sizeof(long double): %2d¥n", sizeof(long double));

7

8

9

10

11

12

13

14

15

16

17

(44)

各データ型のサイズ(1/5)

• sizeof_ex1.c による比較

32 bit 版 Cygwin + GNU C 64 bit 版 Cygwin + GNU C

$ gcc sizeof_ex1.c && ./a sizeof(char) : 1 sizeof(wchar_t) : 2 sizeof(short) : 2 sizeof(int) : 4 sizeof(long) : 4 sizeof(long long) : 8 sizeof(float) : 4 sizeof(double) : 8 sizeof(long double) : 12

$ gcc sizeof_ex1.c && ./a sizeof(char) : 1 sizeof(wchar_t) : 2 sizeof(short) : 2 sizeof(int) : 4 sizeof(long) : 8 sizeof(long long) : 8 sizeof(float) : 4 sizeof(double) : 8 sizeof(long double): 16 コンパイルする環境により 割り当てビット数や 最大値と最小値が異なる可能性がある

(45)

各データ型のサイズ(2/5)

• sizeof_ex1.c による比較

Borland C++ 5.5

>bcc32 sizeof_ex1.c && sizeof_ex1

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

Turbo Incremental Link 5.00 Copyright (c) 1997, 2000 Borland sizeof(char) : 1 sizeof(wchar_t) : 2 sizeof(short) : 2 sizeof(int) : 4 sizeof(long) : 4 sizeof(float) : 4 sizeof(double) : 8 sizeof(long double): 10 コンパイルする環境により 割り当てビット数や 最大値と最小値が異なる可能性がある

(46)

各データ型のサイズ(3/5)

• sizeof_ex1.c による比較

Visual Studio 2013 Express Desktop Windows 32 bit 版

>cl sizeof_ex1.c && sizeof_ex1

Microsoft(R) C/C++ Optimizing Compiler Version 18.00.21005.1 for x86 Copyright (C) Microsoft Corporation. All rights reserved.

sizeof_ex1.c

Microsoft (R) Incremental Linker Version 12.00.21005.1 Copyright (C) Microsoft Corporation. All rights reserved. /out:sizeof_ex1.exe sizeof_ex1.obj sizeof(char) : 1 sizeof(wchar_t) : 2 sizeof(short) : 2 sizeof(int) : 4 sizeof(long) : 4 sizeof(long long) : 8 sizeof(float) : 4 sizeof(double) : 8 sizeof(long double): 8 コンパイルする環境により 割り当てビット数や 最大値と最小値が異なる可能性がある

(47)

各データ型のサイズ(4/5)

• sizeof_ex1.c による比較

Visual Studio 2013 Express Desktop Windows 64 bit 版

>cl sizeof_ex1.c && sizeof_ex1

Microsoft(R) C/C++ Optimizing Compiler Version 18.00.21005.1 for x64 Copyright (C) Microsoft Corporation. All rights reserved.

sizeof_ex1.c

Microsoft (R) Incremental Linker Version 12.00.21005.1 Copyright (C) Microsoft Corporation. All rights reserved. /out:sizeof_ex1.exe sizeof_ex1.obj sizeof(char) : 1 sizeof(wchar_t) : 2 sizeof(short) : 2 sizeof(int) : 4 sizeof(long) : 4 sizeof(long long) : 8 sizeof(float) : 4 sizeof(double) : 8 sizeof(long double): 8 コンパイルする環境により 割り当てビット数や 最大値と最小値が異なる可能性がある

(48)

各データ型のサイズ(5/5)

gcc 32bit gcc 64bit bcc32 cl 32bit cl 64bit

char 1 1 1 1 1 wchar_t 2 2 2 2 2 shor 2 2 2 2 2 int 4 4 4 4 4 long 4 8 4 4 4 long long 8 8 - 8 8 float 4 4 4 4 4 double 8 8 8 8 8 long double 12 16 10 8 8

(49)

データ型のサイズ

C99における解決方法

• stdint.h ヘッダファイルを使う

#include <stdint.h>

// ...

int8_t

i8;

// 符号付き 8bit整数

uint8_t

ui8;

// 符号なし 8bit整数

int16_t

i16;

// 符号付き16bit整数

uint16_t

ui16;

// 符号なし16bit整数

int32_t

i32;

// 符号付き32bit整数

uint32_t

ui32;

// 符号なし32bit整数

int64_t

i64;

// 符号付き64bit整数

uint64_t

ui64;

// 符号なし64bit整数

第1週のサンプルプログラム • wavtest.c • bmptest.c でも使っています。 注: Boarland C++ 5.5 は C99 非対応なので stdint.h が使えない。 補足

(50)

sizeof 演算子の例2

• 例)変数やリテラルの割り当てバイト数

教科書 p.78, 84, 195.

mintty+bash+gcc

$ gcc sizeof_ex2.c && ./a sizeof(i) : 4 sizeof(d) : 8 sizeof(s) : 13 sizeof( 1 ): 4 sizeof( 1.): 8 sizeof("1"): 2 sizeof_ex2.c

int i = 128;

double d = 123e-3;

char s[] = "hello, world";

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

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

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

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

printf("sizeof( 1.): %2d¥n", sizeof( 1.));

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

6

7

8

9

10

11

12

13

14

(51)

sizeof 演算子の例3

• 例)配列変数の割り当てバイト数

教科書 p.78, 84, 195. sizeof_ex3.c

int a[10];

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

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

printf("sizeof(a[0]) : %2d¥n", sizeof(a[0]));

printf("sizeof(a)/sizeof(a[0]) : %2d¥n",

sizeof(a)/sizeof(a[0]));

mintty+bash+gcc

$ gcc sizeof_ex3.c && ./a

sizeof(int) : 4

sizeof(a) : 40

sizeof(a[0]) : 4

sizeof(a)/sizeof(a[0]) : 10

7

8

9

10

11

12

13

← int型の割り当てバイト数

← 配列変数a の割り当てバイト数

← 変数a[0]の割り当てバイト数

← 配列変数a の要素数

(52)

型変換(cast)演算子

• (変換したい型) 値

教科書 p.70, 84. casttest.c

int a = 1;

int b = 2;

double x = a / b;

double y = a / (double) b;

printf("%f¥n", x);

printf("%f¥n", y);

mintty+bash+gcc

$ gcc casttest.c && ./a

0.000000

0.500000

整数同士の割り算だと

1/2 が 0 になっている。

int型の b の値を

double 型に変換

cast 演算子

(type) 値

type: 任意のデータ型

6

7

8

9

10

11

12

(53)

暗黙の算術変換 (概略)

• 二項演算子の両辺が異なる型の場合

以下の手順で型を変換(符号ありの場合)

• 基本的には大きい方へと型をそろえて行く処理

1. 片方がlong double: 他方をlong doubleに変換

2. 片方がdouble: 他方を doubleに変換

3. 片方がfloat: 他方をfloatに変換

4. char, shortをintに変換

5. 片方がlong: 他方をlongに変換

(54)

算術演算子

算術演算子 演算子の機能 書式 単項演算子 + 被演算数の値 + expr - 被演算数の符号反転 - expr 二項演算子 + 加算 expr1 + expr2 - 減算 expr1 – expr2 * 乗算 expr1 * expr2 / 除算 expr1 / expr2 % 剰余算 expr1 % expr2 教科書 p.69, 84.

(55)

代入演算子、複合代入演算子

代入演算子 演算子の機能 書式 代入演算子 = 代入 var = expr 複合代入演算子 += 加算 var += expr -= 減算 var –= expr *= 乗算 var *= expr /= 除算 var /= expr %= 剰余算 var %= expr &= ビット毎のAND var &= expr ^= ビット毎のXOR var ^= expr |= ビット毎のOR var |= expr <<= 左シフト var <<= expr >>= 右シフト var >>= expr 教科書 pp.75-79, 84.

(56)

bit演算子

算術演算子 演算子の機能

単項演算子 ~ 1の補数 ~ expr

二項演算子 << 左シフト expr1 << expr2 >> 右シフト expr1 >> expr2 & ビット毎のAND expr1 & expr2 ^ ビット毎のXOR expr1 ^ expr2 | ビット毎のOR expr1 | expr2 教科書 pp.78-79, 84.

(57)

• 利用可能 bit の外側には 0が充填される

bitシフト(論理シフト)

(符号なし整数の場合)

0 0

1 1 1 0 0 1 0 0

1 0 0 1 0 0 0 0

<< 2

0 0

1 1 1 0 0 1 0 0

0 0 1 1 1 0 0 1

>> 2

0xe8

0x90

0xe8

0x3a

論理シフトであれば

左シフト、右シフト共に

符号付き、符号なしで結果は共通

教科書 pp.78-79, 84.

(58)

• 最上位ビットより上位は符号拡張される

• 符号ビットが0なら0、1なら1が充填される

bitシフト(算術シフト)

(符号付き整数の場合?)

1 1

1 1 1 0 0 1 0 0

1 1 1 1 1 0 0 1

>> 2

0 0

0 0 0 1 1 0 1 1

0 0 0 0 0 1 1 0

>> 2

0x1b

0x06

0xe8

0xfa

右シフトは最上位ビットの値により

符号付き、符号なしで結果が異なる

環境依存なので、環境によっては 論理シフトになる可能性も 考慮しておくこと。

左シフトは

符号付き、符号なしで結果は共通

教科書 pp.78-79, 84.

(59)

• 実際の環境はどうなっているのか?

右シフト

教科書 pp.78-79, 84.

bitshifttest.c

unsigned int uc = 0xe8000000;

// == 0b11101000...

signed int sc = 0xe8000000;

// == 0b11101000...

uc >>= 2 + 8 * 3;

sc >>= 2 + 8 * 3;

printf("%02x¥n", uc & 0xff);

// 0b00111010 == 0x3a

printf("%02x¥n", sc & 0xff);

// 0b11111010 == 0xfa ?

6

7

8

9

10

11

12

13

2015-05-15修正 誤:0b11100100 正:0b11101000 2015-05-15修正 誤:0b00111001 正:0b00111010 2015-05-15修正 誤:0b11111001 正:0b11111010

(60)

• 算術シフトになっている環境が多い?

右シフト

教科書 pp.78-79, 84.

64bit 版 cygwin + GNU C 4.8.2

Borland C++ 5.5

Visual Studio 2013 Express Desktop Windows 64bit 版

$ gcc bitshifttest.c && ./a 3a

fa

>bcc32 bitshifttest.c && bitshifttest ...

3a fa

>cl bitshifttest.c && bitshifttest ...

3a fa

(61)

論理演算

X Y X AND Y X OR Y X XOR Y NOT X

0 0 0 0 0 1 0 1 0 1 1 1 1 0 0 1 1 0 1 1 1 1 0 0

論理演算子 ビット毎の論理演算子 意味 英語表記

&& & 論理積 AND || | 論理和 OR

^ 排他的論理和 XOR (exclusive or) ! ~ 論理反転 NOT

教科書 pp.78-79, 84.

(62)

C言語の論理値(真偽値)

• 数値を論理値として用いている

• 論理演算とビット毎の論理演算に注意

論理値 数値 真偽値判定時 偽 0 0のみが偽として扱われる 真 1 0以外はすべて真として扱われる logictest.c

int x = 1;

// = 0b01

int y = 2;

// = 0b10

printf("x && y = %d¥n", x && y);

printf("x || y = %d¥n", x || y);

printf("x & y = %d¥n", x & y);

printf("x | y = %d¥n", x | y);

mintty+bash+gcc

$ gcc logictest.c && ./a

x && y = 1

x || y = 1

x & y = 0

x | y = 3

教科書 pp.78-79, 84.

6

7

8

9

10

11

(63)

論理演算とビット毎の論理演算

• 論理演算を行う単位が違う

1 1 1 0 0 1 0 0

論理演算子

0 0 0 0 0 0 0 1

1 1 1 0 0 1 0 0

ビット毎の論理演算子

0 0 0 0 0 1 0 0

論理演算では

全体を1つの論理値として扱う

ビット毎の論理演算では

各ビットを個別に扱う

教科書 pp.78-79, 84.

(64)

bit 毎の AND による bit mask

• bit毎にANDを取った結果が得られる

1 1 1 0 0 1 0 0

0 0 0 0

0 0 0 0 0 1 0 0

&

0 0 0 0 1 1 1 1

X & Y で

右辺の値をマスクとして用いた場合

0: 0でクリア

1: 元の値をそのまま通過

教科書 pp.78-79., p.84.

(65)

bit 毎の OR による bit mask

• bit毎にORを取った結果が得られる

1 1 1 0 0 1 0 0

1 1 1 1

1 1 1 1 0 1 0 0

|

1 1 1 1 0 0 0 0

X | Y で

右辺の値をマスクとして用いた場合

0: 元の値をそのまま通過

1: 1でクリア

教科書 pp.78-79., p.84.

(66)

bit 毎の XOR による bit 反転

• bit毎にXORを取った結果が得られる

1 1 1 0 0 1 0 0

@ @ @ @

0 0 0 1 0 1 0 0

^

1 1 1 1 0 0 0 0

X ^ Y で

右辺の値をマスクとして用いた場合

0: 元の値をそのまま通過

1: bit を 0⇔1 反転

同じ値で再度 XOR を取ると元に戻るので

簡易暗号的な使い方も出来る

教科書 pp.78-79., p.84.

(67)

1の補数演算子

• 要は単なるビット毎の論理反転

1 1 1 0 0 1 0 0

ビット毎の論理反転

0 0 0 1 1 0 1 1

教科書 pp.78-79., p.84.

~

(68)

補数とは

• 基数

𝑏(𝑏進数) 𝑛桁で表現可能な整数𝑎に対し

𝑏

𝑛

− 𝑎

: 基数(𝑏)の補数

𝑏

𝑛

− 𝑎 − 1 : 減基数(𝑏 − 1)の補数

• 例: 2進数8桁で表す1について

• 2の補数

• 0b

1

00000000 – 0b00000001 = 0b11111111

• 1の補数(単なるビット毎の論理反転)

• 0b

1

00000000 – 0b00000001 - 1 = 0b11111110

(69)

インクレメント、デクレメントの演算子

算術演算子 演算子の機能 前置演算子 ++ インクレメント ++expr -- デクレメント --expr 後置演算子 ++ インクレメント expr++ -- デクレメント expr-- 前置演算子は演算後に値を取り出す。 後置演算子は演算前に値を取り出す。 incrtest.c

int i = 5;

printf("%d¥n", ++i);

printf("%d¥n", --i);

printf("%d¥n", i++);

printf("%d¥n", i--);

mintty+bash+gcc

$ gcc incrtest.c && ./a

6

5

5

6

教科書 pp.73-74., p.84.

6

7

8

9

10

(70)

比較演算子

(関係演算子、等値演算子)

演算子 比較の意味 関係演算子 < 左辺が小 expr1 < expr2 <= 左辺が小または等しい expr1 <= expr2 > 左辺が大 expr1 > expr2 >= 左辺が大または等しい expr1 >= expr2 等値演算子 == 等しい expr1 == expr2 != 等しくない expr1 != expr2 教科書 pp.117-118, 147.

演算結果は真(=1)または偽(=0)となる

(71)

ポインタ演算子

• アドレス演算子

• 書式 : &オブジェクト

• オブジェクトの配置されたアドレスを得る。

• 間接演算子

• 書式 : *ポインタ

• ポインタが指すアドレスに配置されたオブジェクトを得

る。

教科書 pp.213-218.

(72)

• 書式:

• 条件式 ? 式1 : 式2

条件演算子

三項演算子(?:)

[1] pp.63-66, 256-257. 条件式 真 偽 式1 式2 condexprtest.c #include <stdio.h> void main() { int i; fprintf(stderr, "i = ?¥b"); scanf("%d", &i);

printf("%s¥n", i ? "not zero" : "zero"); } 1 2 3 4 5 6 7 8 9 mintty+bash+gcc

$ gcc condexprtest.c && ./a i = 1 not zero $ ./a i = 0 zero 2015-05-01追加修正

(73)

演算子の優先度

演算子 結合規則 備考

( ) [ ] -> . 左から右→

! ~ ++ -- + - * & (type) sizeof 右から左← 単項演算子

* / % 左から右→ 二項演算子 + - 左から右→ 二項演算子 << >> 左から右→ bitシフト < <= > >= 左から右→ 関係演算子 == != 左から右→ 等値演算子 & 左から右→ bit毎のAND ^ 左から右→ bit毎のXOR | 左から右→ bit毎のOR && 左から右→ 論理演算子(AND) || 左から右→ 論理演算子(OR) ?: 右から左← 三項演算子 = += -= *= /= %= &= ^= |= <<= >>= 右から左← 代入演算子 , 左から右→ [1] p.65. より 高 低 優先度

(74)
(75)

配列変数

• 同じ変数名で複数の要素を管理する

char a[10]; // 要素数10のchar型変数の宣言

教科書 pp.85-108. a[0] ? a[1] ? a[2] ? a[3] ? a[9] ? ... 要素数10の添え字付き変数 初期値式が与えられなかった場合、値は不定 [1] pp.103-104., p.273.

(76)

配列変数

• 配列変数の要素への代入

char a[10]; // 要素数10のchar型変数の宣言

a[0] = 'a'; // 0番目の要素へ代入

教科書 pp.85-108. a[0] 'a' a[1] ? a[2] ? a[3] ? a[9] ? ... 要素数10の添え字付き変数 初期値式が与えられなかったので、値は不定 宣言後の代入 [1] pp.103-104., p.273.

(77)

配列変数

• 添え字は値が取れれば変数や数式でも良い

int i = 1;

char a[10]; // 要素数10のchar型変数の宣言

a[i + 1] = 'a'; // 2番目の要素へ代入

教科書 pp.85-108. a[0] ? a[1] ? a[2] 'a' a[3] ? a[9] ? ... 要素数10の添え字付き変数 [1] pp.103-104., p.273.

(78)

配列変数

• 確保した領域外はアクセスは禁止

char a[10]; // 要素数10のchar型変数の宣言

short b = 0x1234;

a[10] = 'a';// 宣言された領域外へのアクセス

教科書 pp.85-108. a[0] ? a[1] ? a[2] ? a[3] ? a[9] ? ... 要素数10の添え字付き変数 [1] pp.103-104., p.273. b 0x1234 a[10] 'a' 他の変数が使っていたらその値を壊してしまう ここに書き込むと何が起こるか分からない

(79)

配列変数

• 初期値式による配列変数の初期化

char a[10] = {'a', 'b'}; //初期値式付きの

//要素数10のchar型変数の宣言

教科書 pp.85-108. a[0] 'a' a[1] 'b' a[2] 0 a[3] 0 a[9] 0 ... 要素数10の添え字付き変数 初期値式が要素数より少ない場合、残りは0で初期化 初期値式による初期化 [1] pp.103-104., p.273.

(80)

配列変数

• 初期値式による配列変数の初期化

char a[] = {'a', 'b'}; //初期値式付きで

//要素数を省略したchar型変数の宣言

教科書 pp.85-108. a[0] 'a' a[1] 'b' 初期値式の要素数分確保される 初期値式による初期化 [1] pp.103-104., p.273.

(81)

配列変数

• 文字列による初期化(要素数指定)

char a[10] = "ab"; //文字列による初期値付きの

//要素数10のchar型変数の宣言

教科書 pp.85-108. a[0] 'a' a[1] 'b' a[2] 0 a[3] 0 a[9] 0 ... 要素数10の添え字付き変数 初期値式が要素数より少ない場合、残りは0で初期化 文字列と文字列終端の'¥0' [1] pp.103-104., p.273.

(82)

配列変数

• 文字列による初期化(要素数自動決定)

char a[] = "ab";//文字列による初期値付きで

//要素数を省略したchar型変数の宣言

教科書 pp.85-108. a[0] 'a' a[1] 'b' a[2] 0 文字列の文字数+文字列終端'¥0'の1文字分の要素 文字列と文字列終端の'¥0' [1] pp.103-104., p.273.

(83)

変数の初期化

• 明示的な初期化がない場合

• 外的変数、静的変数→0

• 自動変数、レジスタ変数→不定

• 初期化する場合

• 外的変数、静的変数←定数式でのみ初期化可

• コンパイル時に1度だけ初期化される

• 自動変数、レジスタ変数←任意の式で初期化可

• 実行時にブロック毎に初期化される

[1] pp.103-104., p.273.

(84)

配列変数の初期化

• 要素数を与えない場合

• 初期値式の数で配列のサイズが決まる

• 要素数を与えた場合

• 初期値式を与えない場合

• 値は不定

• 初期値式を与える場合

• 要素数を超えるとエラー

• 要素数に足りない部分は0で初期化される

[1] pp.103-104., p.273.

(85)

制御構造

(86)

条件分岐

(87)

条件分岐

(if 文)

• 真偽値による場合分け

if (条件式) {

// 条件式が真の場合の処理1

}

条件式 処理1 真 偽 教科書 pp.130-133.

(88)

条件分岐

(if, else 文)

• 真偽値による場合分け

if (条件式) {

// 条件式が真の場合の処理1

} else {

// 条件式が偽の場合の処理2

}

条件式 処理1 処理2 真 偽 教科書 pp.130-133.

(89)

入れ子の条件分岐

(if, else 文)

• 真偽値による場合分け

if (条件式1) {

// 条件式1が真の場合の処理1

} else {

if (条件式2) {

// 条件式1が偽かつ

// 条件式2が真の場合の処理2

} else {

// 条件式1が偽かつ

// 条件式2が偽の場合の処理3

}

}

条件式1 処理1 真 偽 条件式2 処理2 処理3 真 偽 if は任意の数入れ子に出来ます。 教科書 pp.130-133.

(90)

条件分岐

(if, else if, else 文)

• 真偽値による場合分け

if (条件式1) {

// 条件式1が真の場合の処理1

} else if (条件式2) {

// 条件式1が偽かつ

// 条件式2が真の場合の処理2

} else {

// 条件式1が偽かつ

// 条件式2が偽の場合の処理3

}

条件式1 処理1 真 偽 条件式2 処理2 処理3 真 偽 else if は任意の数追加出来ます。 教科書 pp.130-133.

(91)

多分岐判断機構 (switch 文)

• 値による場合分け

switch (式) {

case 値1:

// 式が値1の場合の処理1

case 値2:

// 式が値2の場合の処理2

default:

// 他の条件に

// 当てはまらない場合の処理N

};

式 処理1 値1 値2 break 文を入れておかないと 次の条件の処理を 連続して実行するので注意。 処理2 処理N default break break break 教科書 pp.134-140.

(92)

繰り返し(ループ)

for文, while文, do-while 文によるループと

(93)

ループの再開と脱出

• continue 文

• while, do while, for 文内で使用可能

• 以降の処理を中断してループ末尾から再開する

• for文では後処理(第3パラメータ)も実行する

• break 文

• while, do while, for, switch 文内で使用可能

• 以降の処理を中断してループを脱出する

• switch文の場合はswitch文から脱出する

(94)

後判定ループ (do while 文)

• 真偽値による繰り返し

do {

// 条件式 が真の場合の処理

} while (条件式);

条件式 処理 真 偽 do while 文は、ループ内の処理を 最低1回は実行する。 教科書 p.123. continue break

(95)

前判定ループ (while 文)

• 真偽値による繰り返し

while (条件式) {

// 条件式が真の場合の処理

}

教科書 pp.119-122. 式2 真 偽 処理 continue break

(96)

初期化・更新処理付きループ (for 文)

• 真偽値による繰り返し

for (式1; 式2; 式3) {

// 式2が真の場合の処理

};

式2 式1 真 偽 前判定ループだが 式1による初期化と 式3による更新処理を ひとまとめにして書ける。 処理 式3 教科書 pp.124-129. continue break

(97)

for文とwhile文 (前判定ループ)

• 以下のループは等価

• continue時の式3の扱いに注意

for (式1; 式2; 式3) {

// 式2が真の場合の処理

};

式2 式1 真 偽 処理 式3 教科書 pp.123-129. for文の continue break

式1;

while (式2) {

// 式2が真の場合の処理

式3;

};

while文の continue

(98)

for文とwhile文 (前判定ループ)

• 以下のループは等価

• continue時の式3の扱いに注意

for (i = 0; i < 10; i++) {

// ループ内の処理

};

教科書 pp.123-129.

i = 0;

while (i < 10) {

//ループ内の処理

i++;

};

i < 10 i = 0 真 偽 処理 i++ for文の continue break while文の continue

(99)

後判定ループ (do while 文)

• continue, break 後の処理(iの値)に注目

looptest_dowhile.c int i = 0, j = 0, n; fprintf(stderr, "n = "); scanf("%d", &n); do { j++; printf("%d, %d: 1st", i, j);

if (j == 2) {printf(" continue¥n"); continue;} printf(" 2nd");

if (j == 4) {printf(" break¥n"); break;} printf(" 3rd¥n"); i++; } while (i < n); 教科書 pp.119-122. mintty + bash $ ./looptest_dowhile n = 10 0, 1: 1st 2nd 3rd 1, 2: 1st continue 1, 3: 1st 2nd 3rd 2, 4: 1st 2nd break mintty + bash $ ./looptest_dowhile n = 0 0, 1: 1st 2nd 3rd do while 文は、 ループ内の処理を 最低1回は実行する。

(100)

前判定ループ (while 文)

• continue, break 後の処理(iの値)に注目

looptest_while.c int i = 0, j = 0, n; fprintf(stderr, "n = "); scanf("%d", &n); while (i < n) { j++; printf("%d, %d: 1st", i, j);

if (j == 2) {printf(" continue¥n"); continue;} printf(" 2nd");

if (j == 4) {printf(" break¥n"); break;} printf(" 3rd¥n"); i++; } 教科書 p.123. mintty + bash $ ./looptest_while n = 10 0, 1: 1st 2nd 3rd 1, 2: 1st continue 1, 3: 1st 2nd 3rd 2, 4: 1st 2nd break mintty + bash $ ./looptest_while n = 0

(101)

初期化・更新処理付きループ (for 文)

• continue, break 後の処理(iの値)に注目

looptest_for.c int i = 0, j = 0, n; fprintf(stderr, "n = "); scanf("%d", &n); for (i = 0; i < n; i++) { j++; printf("%d, %d: 1st", i, j);

if (j == 2) {printf(" continue¥n"); continue;} printf(" 2nd");

if (j == 4) {printf(" break¥n"); break;} printf(" 3rd¥n"); } 教科書 pp.124-129. mintty + bash $ ./looptest_for n = 10 0, 1: 1st 2nd 3rd 1, 2: 1st continue 2, 3: 1st 2nd 3rd 3, 4: 1st 2nd break mintty + bash $ ./looptest_for n = 0

(102)

continue 文

• 以下のループ内に更に小さなループが含ま

れない場合の continue は goto contin と同義

for (...) { // ... contin: ; } [1] p.281. do { // ... contin: ; } while (...); while (...) { // ... contin: ; }

(103)

goto文

• 指定した名札付き文へ移動(ジャンプ)する

• 名札(label)は以下のように設定出来る

ラベル名: 文

[1] p.281.

do while 文相当 while 文相当 for 文相当 loop: ;

{

// something to do contin: ;

}

if (expr) goto loop; brk: ; loop: ; if (expr) { // something to do contin: ; goto loop; } brk: ; expr1; loop: ; if (expr2) { // something to do contin: ; expr3; goto loop; } brk: ; goto 文は 余程理由がない限り使わないこと

(104)

参考文献

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

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

拠、共立出版(1989)

(105)

宿題

• 次回までに以下の事をやっておくこと。

• 教科書の第3章の終わりまで読み、指示された操

作を試して動作を確認する。

• 不明な点、疑問点についてメモし、次回の授業に

持参する。または、本講義の Moodle コース上に

ある第3週宿題用フォーラムに書き込んでおく。

参照

関連したドキュメント

地下水採取等対象物 質と地下水採取を行う

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

理由:ボイラー MCR範囲内の 定格出力超過出 力は技術評価に て問題なしと確 認 済 み で あ る が、複数の火力

Dual I/O リードコマンドは、SI/SIO0、SO/SIO1 のピン機能が入出力に切り替わり、アドレス入力 とデータ出力の両方を x2

ƒ 、または Arduinoのリセットボタン”oƒ、2 }~x してか らコマンド @2 しま Q*した Arduino す。 プログラムを Arduino に…き:む Äsについては「

(2)燃料GMは,定格熱出力一定運転にあたり,原子炉熱出力について運転管理目標を

その対策として、図 4.5.3‑1 に示すように、整流器出力と減流回路との間に Zener Diode として、Zener Voltage 100V