1 C言語入門 第3週 プログラミング言語Ⅰ(実習を含む。), 計算機言語Ⅰ・計算機言語演習Ⅰ, 情報処理言語Ⅰ(実習を含む。) 2 リダイレクト 先週の復習 3 出力のリダイレクト • 書式: コマンド > ファイル • コマンドの出力をファイルに繋ぐ • コマンド実行時の出力をファイルに保存出来る。 • 例: 第1週の hello.c について mintty + bash hello_result.txt ./a > hello_result.txt hello, world コマンドプロンプト hello > hello_result.txt 4 printf 関数 値の表示 5 教科書 pp.61, 64-66, 98. 値の表示 • printf 関数を使う printftest.c 1 2 3 4 5 6 7 8 9 10 11 12 13 #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 6 教科書 pp.61, 64-66, 98. printf 関数 • int printf(const char *FORMAT, ...); • 引数: • FORMAT: • ...: 書式 任意の数の引数 • 戻り値: • 書き出された文字数。 • エラーの場合負の数。 参考: [1] pp.305-306. 7 教科書 pp.61, 64-66, 98. printf: 書式 • %~変換文字までをフィールドと呼ぶび、テン プレート(穴空き定規)ように扱われる • フィールドは以下の要素から成る • %[フラグ][最小フィールド幅][.精度][長さ修飾子]変換文字 printf("1 + 2 = %d\n", 1 + 2); 1 + 2 = \n ここに、 int型の整数型データとして解釈した 2つ目の引数の値(上記の例では1+2の計算結果)を 符号付き10進数にして印字する 参考: [1] pp.305-306. 8 教科書 pp.61, 64-66, 98. printf: フラグ • -: 左揃えで印字 • +: 数を符号付きで印字 • スペース: 最初の文字が符号でない場合スペース を前に付ける • 0: フィールド幅いっぱいに左側から0を詰める • #: 別の出力形式を指定。 • • • • o: 先頭の桁を0にする x: 0でない結果の先頭を0xにする e,f,g: 出力に必ず小数点を付ける g: 末尾の0を削除しない 参考: [1] pp.305-306. 9 教科書 pp.61, 64-66, 98. printf: 最小フィールド幅 • 変換された引数は少なくともこの幅になる。 • 必要ならもっと広い幅のフィールドに印字。 • 変換された引数がフィールド幅よりも短い場 合padding(=詰め物)が行われる。 • paddingは通常はスペース。フラグに0が指 定された場合は0が用いられる。 • *: 次の引数の値を用いる 参考: [1] pp.305-306. 10 教科書 pp.61, 64-66, 98. printf: .精度 • 「.」(ピリオド): フィールド幅と精度の分離子 (separator) • 文字列に対しては印字する最大文字数 • e,fの対しては小数点以下に印字すべき桁 数 • gに対しては有効数字の桁数 • 整数に対しては印字すべき最小桁数(頭に0 が付加される) • *: 次の引数の値を用いる 参考: [1] pp.305-306. 11 教科書 pp.61, 64-66, 98. printf: 長さ修飾子 • h: short または float として扱う • l: long として扱う • L: long double として扱う 参考: [1] pp.305-306. 12 教科書 pp.61, 64-66, 98. 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. 13 教科書 pp.61, 64-66, 98. printf の詳細 • ここでは概略しか示せていないのと一部不正 確な部分もあるので、詳細は bash から man コマンドを用いて以下の方法で確認すること 2015-04-27 修正&追記 mintty+bash mintty+bash man sprintf man 3 printf 3 はマニュアルのセクション番号を意味する。 セクション 3 はサブルーチン (つまりライブラリ関数) 関連のマニュアル http://linuxjf.sourceforge.jp/JFdocs/Man-Page-2.html printf はセクション 1 にもあるので sprintf かセクショ ン 3 の printf を引く必要がある。 • 邦訳は以下のページ • http://linuxjm.sourceforge.jp/html/LDP_man-pages/man3/printf.3.html 教科書 pp.61, 64-66, 98. printf のマニュアル導入 • printf のマニュアルが引けない場合、 以下のコマンドを mintty+bash から実行 mintty+bash apt-cyg install cygwin-doc cygwin-doc パッケージのインストール 14 15 演習: 円の面積を計算せよ(1) • area_of_a_circle1.c をダウンロードして /*WYCH1*/ の部分を書き 変えることで以下のプログラムを完成させなさい。/*WYCH2*/ /*WYCH3*/ の部分は次の演習で変更するので今はまだ書き変 えない事。なお WYCH は Write Your Code Here を略してい る。 2015-04-27 追記 • double 型の変数 r に円の半径を代入する。 • 円の面積𝑆は公式𝑆 = 𝜋𝑟 2 を用いて計算し、結果は小数点以下2桁ま で出力する。出力には printf と "%f" を用いれば良いが、前述の 精度を設定する必要がある。 • 円の面積を表示する前に、確認のため計算に用いる半径も表示する。 • r に代入する半径はソースコードにリテラル値として直接埋め込む。 異なる半径面積を計算したい時は、ソースコードのリテラル値を書き 変えてコンパイルし直すこととする。 • 半径を 1~10まで 1 刻みで増やして 計10 個の面積を計算せよ。 16 教科書 p.68. マクロ • preprocessor のキーワード置換機能 • 書式: #define マクロ名 置換内容 2015-04-25修正 誤:area_of_a_circle1.c • 定数等に名前を付ける際に使う 正:macrotest1.c macrotest1.c 1 2 3 4 5 6 7 8 #include <stdio.h> MSGはコンパイル前に "world"で置換される #define MSG "world" void main() { printf("hello, %s\n", MSG); } 17 マクロ • コンパイル時のオプション -D で外部から与え macrotest2.c ることも出来る。 普通はこのままコンパイルしても MSGがないためエラーになる mintty+bash+gcc 1 2 3 4 5 $ gcc -DMSG="\"kou\"" macrotest2.c && ./a hello, kou #include <stdio.h> void main() { printf("hello, %s\n", MSG); } 2015-04-25修正 誤:extmacrotest2.c 正:macrotest2.c コマンドプロンプト+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 18 教科書 p.68. マクロ • 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 教科書 p.68. マクロ • 先程の 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" それぞれの環境に 適切な数値や文字列等が設定されている 20 教科書 pp.203-206. ファイルの包含 • preprocessor のファイル取り込み機能 • 別のファイルに記述されたプログラムやマクロ等 を取り込む際に使う • 書式: • #include <ファイル名>//システム提供ファイル用 • /usr/include 等から探して取り込む • #include "ファイル名"//ユーザー作成ファイル用 • 作業ディレクトリから探して取り込む 21 備考: UNIX コマンド grep コマンド • grep [OPTIONS] PATTERN [FILE ...] • 検索文字列を含むファイルを検索する • 引数 • PATTERN : 正規表現等による検索文字列 • FILE : 検索対象のファイルやディレクトリ • OPTIONS • • • • • -R -n -A NUM -B NUM -C NUM : : : : : ディレクトリ下のすべてのファイルを検索 行番号を表示 マッチ位置の後NUM行も表示 マッチ位置の前NUM行も表示 マッチ位置の前後NUM行も表示 • マニュアル邦訳 • http://linuxjm.sourceforge.jp/html/GNU_grep/man1/grep.1.html 22 備考: UNIX コマンド 正規表現 表記 意味 c 文字c \c 文字\c . 任意の一文字 [...] []内の任意の一文字 [^...] []内に含まれない任意の一文字 * 直前のパターンが0回以上反復 + 直前のパターンが1回以上反復 ? 直線のパターンが0または1回出現 | 前後の正規表現の何れか (...) ()内の正規表現をグループ化 ^ 行頭にマッチ $ 行末にマッチ 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 教科書 pp.61, 64-66, 98, 300. fprintf 関数 • int fprintf(FILE *fp, const char *FORMAT, ...); • printfの結果をfpへ書き出す • 引数: • fp: • FORMAT: • ...: FILE 構造体へのポインタ 書式 任意の数の引数 • 戻り値: • 書き出された文字数 • エラーの場合負の数 参考: [1] pp.305-306. [1] pp.196, 199, 218. 標準入出力と標準エラー出力 • 以下の入出力が利用できる • stdin : standard input : 標準入力 • stdout: standard output : 標準出力 • stderr: standard error output: 標準エラー出力 • scanf や getchar 等は stdin から入力している • printf や putchar 等は stdout へ出力している • stdin, stdout はパイプやリダイレクトの対象だ が stderr は標準では対象外(指定すれば対 象にすることも可能) 25 26 [1] pp.196, 199, 218. 標準入出力と標準エラー出力 • パイプやリダイレクトで処理されたくない内容 は stderr へ出力すると良い • fprintf を使うと、出力先を変更出来る 以下の例では(*1)がファイルへ出力されず 画面に出力されている事が確認出来る。 stdiotest.c mintty + bash 6 printf("output to stdout with printf\n"); 7 fprintf(stdout, "output to stdout with fprintf\n"); 8 fprintf(stderr, "output to stderr with fprintf\n"); $ gcc stdiotest.c $ ./a > redirect.txt output to stderr with fprintf $ cat redirect.txt output to stdout with printf output to stdout with fprintf ← ← ← ← ← 標準出力(stdout)を redirect.txt へリダイレクト fprintf で明示的に stderr へ出力した結果(*1) redirect.txtに出力された内容を表示 printf で暗黙的に stdout へ出力した結果 fprintf で明示的に stdout へ出力した結果 [1] pp.196, 199, 218. 標準入出力と標準エラー出力 • stdin,stdout,stderrはstdio.hで定義されている • stdio.h は standard input / output header 27 28 scanf 関数 値の読み込み 29 教科書 pp.80-83, 254. 値の読み込み • scanf関数を使う scanftest.c 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 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); キーボードから入力した値を 変数に保存して利用出来る mintty+bash+gcc $ gcc scanftest.c && ./a i = 1234 d = 1234e-5 s = hello, world i: 1234 d: 0.012340 s: hello, 30 教科書 pp.80-83, 254. scanf 関数 • int scanf(const char *FORMAT, ...); • 引数: • FORMAT: • ...: 書式 任意の数の引数 値を格納する変数へのポインタ • 戻り値: • 変換され代入された入力項目の数。 • ファイル終端またはエラーの場合EOF。 参考: [1] pp.307-309. 31 教科書 pp.80-83, 254. scanf: 書式 • スペース、タブ: 無視される • (%でない)普通の文字: 入力の次の空白でない文字とマッチ • 変換仕様: • %[*][最大フィールド幅][ターゲット幅]変換文字 int a; scanf("%d", &a); &: アドレス演算子 変数へのポインタを得る スカラ変数の前には & を付ける 配列変数、ポインタ変数には不要 入力文字列を10進数として扱い int型の整数型変数へ代入 参考: [1] pp.250, 307-309. 32 教科書 pp.80-83, 254. scanf: 変換仕様 • *: 入力フィールドはスキップされる 代入抑止 • 最大フィールド幅: 読み込む最大文字数 • ターゲット幅: • h: int を short に • l: int を long に、float を double に • L: float を long doubleに 参考: [1] pp.307-309. 33 教科書 pp.80-83, 254. 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. 34 教科書 pp.80-83, 254. scanf の詳細 • ここでは概略しか示せていないのと一部不正 確な部分もあるので、詳細は bash から man コマンドを用いて以下の方法で確認すること mintty+bash man scanf • 邦訳は以下のページ • http://linuxjm.sourceforge.jp/html/LDP_man-pages/man3/scanf.3.html 35 教科書 pp.80-83, 254. scanf の引数とポインタ • 値の代入するには変数のアドレスが必要 : : &a : : 0x~00 0x?? 0x~01 0x?? 0x~02 0x?? 0x~03 0x?? int a; scanf("%d", &a); 32bit 0x?? 0x~04 &: アドレス演算子 0x?? 0x~05 変数が配置されているメモリ上のアドレスが得られる このアドレスのことをC言語ではポインタと呼ぶ 0x?? 0x~06 0x~07 0x?? 0x~08 0x?? : : : : scanf に値の格納先の アドレスを渡す 0x???????? a 36 補足 scanf: C99 の stdint.h の場合 • #include<inttypes.h> してSCN~を使う • "%hd" → "%"SCNd16 • "%d" → "%"SCNd32 • "%u" → "%"SCNu32 使う bit 数を確実に保証出来る 実装依存なので 欲しい桁数が扱えないかも? 参考: [1] pp.307-309. 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~00 : 0x?? : 0x~0f 0x?? 0x~10 0x?? : 0x~17 : 0x~1c : 0x~1f : : : char s[16]; double d; 0x?? : 0x?? : 0x?? : : 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 教科書 p.78, 84, 195. sizeof 演算子 • コンパイル時に変数やデータ型の割り当てバ イト数を求める演算子 • sizeof オブジェクト • sizeof(型名) 43 教科書 p.78, 84, 195. sizeof 演算子の例1 • 例)各データ型の割り当てバイト数 sizeof_ex1.c 7 8 9 10 11 12 13 14 15 16 17 printf("sizeof(char) : printf("sizeof(wchar_t) : printf("sizeof(short) : printf("sizeof(int) : printf("sizeof(long) : #ifndef __BORLANDC__ printf("sizeof(long long) : #endif printf("sizeof(float) : printf("sizeof(double) : printf("sizeof(long double): %2d\n", %2d\n", %2d\n", %2d\n", %2d\n", sizeof(char)); sizeof(wchar_t)); sizeof(short)); sizeof(int)); sizeof(long)); %2d\n", sizeof(long long)); %2d\n", sizeof(float)); %2d\n", sizeof(double)); %2d\n", sizeof(long double)); 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) : sizeof(wchar_t) : sizeof(short) : sizeof(int) : sizeof(long) : sizeof(long long) : sizeof(float) : sizeof(double) : sizeof(long double): 1 2 2 4 4 8 4 8 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) : sizeof(wchar_t) : sizeof(short) : sizeof(int) : sizeof(long) : sizeof(long long) : sizeof(float) : sizeof(double) : sizeof(long double): 1 2 2 4 4 8 4 8 8 コンパイルする環境により 割り当てビット数や 最大値と最小値が異なる可能性がある 48 各データ型のサイズ(5/5) gcc 32bit gcc 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 12 16 10 8 8 long double bcc32 cl 32bit cl 64bit 49 補足 データ型のサイズ C99における解決方法 • stdint.h ヘッダファイルを使う #include // ... int8_t uint8_t int16_t uint16_t int32_t uint32_t int64_t uint64_t 第1週のサンプルプログラム • wavtest.c • bmptest.c でも使っています。 <stdint.h> i8; ui8; i16; ui16; i32; ui32; i64; ui64; // // // // // // // // 符号付き 8bit整数 符号なし 8bit整数 符号付き16bit整数 符号なし16bit整数 符号付き32bit整数 符号なし32bit整数 符号付き64bit整数 符号なし64bit整数 注: Boarland C++ 5.5 は C99 非対応なので stdint.h が使えない。 50 教科書 p.78, 84, 195. sizeof 演算子の例2 • 例)変数やリテラルの割り当てバイト数 mintty+bash+gcc $ gcc sizeof_ex2.c && ./a sizeof(i) : 4 sizeof(d) : 8 sizeof_ex2.c sizeof(s) : 13 6 int i = 128; sizeof( 1 ): 4 7 double d = 123e-3; sizeof( 1.): 8 8 char s[] = "hello, world"; sizeof("1"): 2 9 10 11 12 13 14 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")); 51 教科書 p.78, 84, 195. sizeof 演算子の例3 • 例)配列変数の割り当てバイト数 sizeof_ex3.c 7 8 9 10 11 12 13 int a[10]; printf("sizeof(int) printf("sizeof(a) printf("sizeof(a[0]) printf("sizeof(a)/sizeof(a[0]) : %2d\n", sizeof(int)); : %2d\n", sizeof(a)); : %2d\n", 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 ← int型の割り当てバイト数 ← 配列変数a の割り当てバイト数 ← 変数a[0]の割り当てバイト数 ← 配列変数a の要素数 52 教科書 p.70, 84. 型変換(cast)演算子 • (変換したい型) 値 cast 演算子 (type) 値 type: 任意のデータ型 casttest.c 6 7 8 9 10 11 12 int a = 1; int b = 2; double x = a / b; double y = a / (double) b; printf("%f\n", x); printf("%f\n", y); int型の b の値を double 型に変換 mintty+bash+gcc $ gcc casttest.c && ./a 0.000000 0.500000 整数同士の割り算だと 1/2 が 0 になっている。 [1] pp.52-57, 240-244. 暗黙の算術変換 (概略) • 二項演算子の両辺が異なる型の場合 以下の手順で型を変換(符号ありの場合) • 基本的には大きい方へと型をそろえて行く処理 1. 2. 3. 4. 5. 片方がlong double: 他方をlong doubleに変換 片方がdouble: 他方を doubleに変換 片方がfloat: 他方をfloatに変換 char, shortをintに変換 片方がlong: 他方をlongに変換 53 54 教科書 p.69, 84. 算術演算子 単項演算子 二項演算子 算術演算子 演算子の機能 書式 + 被演算数の値 + expr - 被演算数の符号反転 - expr + 加算 expr1 + expr2 - 減算 expr1 – expr2 * 乗算 expr1 * expr2 / 除算 expr1 / expr2 % 剰余算 expr1 % expr2 55 教科書 pp.75-79, 84. 代入演算子、複合代入演算子 代入演算子 複合代入演算子 代入演算子 演算子の機能 書式 = 代入 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 複合代入演算子はvar=var+exprのような演算と代入を同時に行う 56 教科書 pp.78-79, 84. bit演算子 算術演算子 演算子の機能 単項演算子 ~ 1の補数 ~ expr 二項演算子 << 左シフト expr1 << expr2 >> 右シフト expr1 >> expr2 & ビット毎のAND expr1 & expr2 ^ ビット毎のXOR expr1 ^ expr2 | ビット毎のOR expr1 | expr2 教科書 pp.78-79, 84. 57 bitシフト(論理シフト) (符号なし整数の場合) • 利用可能 bit の外側には 0が充填される 論理シフトであれば 左シフト、右シフト共に 符号付き、符号なしで結果は共通 0xe8 1 1 1 0 0 1 0 0 0 0 0x90 1 0 0 1 0 0 0 0 0xe8 0 0 1 1 1 0 0 1 0 0 0x3a 0 0 1 1 1 0 0 1 << 2 >> 2 58 教科書 pp.78-79, 84. bitシフト(算術シフト) (符号付き整数の場合?) • 最上位ビットより上位は符号拡張される • 符号ビットが0なら0、1なら1が充填される 左シフトは 符号付き、符号なしで結果は共通 右シフトは最上位ビットの値により 符号付き、符号なしで結果が異なる 0x1b 0 0 0 0 0 1 1 0 1 1 0x06 0 0 0 0 0 1 1 0 0xe8 1 1 1 1 1 0 0 1 0 0 0xfa 1 1 1 1 1 0 0 1 >> 2 >> 2 環境依存なので、環境によっては 論理シフトになる可能性も 考慮しておくこと。 59 教科書 pp.78-79, 84. 右シフト • 実際の環境はどうなっているのか? bitshifttest.c 6 7 8 9 10 11 12 13 2015-05-15修正 誤:0b11100100 正:0b11101000 unsigned int uc = 0xe8000000; // == 0b11101000... signed int sc = 0xe8000000; // == 0b11101000... uc >>= 2 + 8 * 3; sc >>= 2 + 8 * 3; 2015-05-15修正 誤:0b00111001 正:0b00111010 printf("%02x\n", uc & 0xff); // 0b00111010 == 0x3a printf("%02x\n", sc & 0xff); // 0b11111010 == 0xfa ? 2015-05-15修正 誤:0b11111001 正:0b11111010 60 教科書 pp.78-79, 84. 右シフト • 算術シフトになっている環境が多い? 64bit 版 cygwin + GNU C 4.8.2 $ gcc bitshifttest.c && ./a 3a fa Borland C++ 5.5 >bcc32 bitshifttest.c && bitshifttest ... 3a fa Visual Studio 2013 Express Desktop Windows 64bit 版 >cl bitshifttest.c && bitshifttest ... 3a fa 61 教科書 pp.78-79, 84. 論理演算 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 ! 論理演算子による演算結果は真(=1)または偽(=0)となる 62 教科書 pp.78-79, 84. C言語の論理値(真偽値) • 数値を論理値として用いている 論理値 数値 真偽値判定時 偽 0 0のみが偽として扱われる 真 1 0以外はすべて真として扱われる • 論理演算とビット毎の論理演算に注意 6 7 8 9 10 11 logictest.c mintty+bash+gcc int x = 1; // = 0b01 int y = 2; // = 0b10 printf("x && y = %d\n", printf("x || y = %d\n", printf("x & y = %d\n", printf("x | y = %d\n", $ x x x x x x x x && || & | y); y); y); y); gcc logictest.c && ./a && y = 1 || y = 1 & y = 0 | y = 3 63 教科書 pp.78-79, 84. 論理演算とビット毎の論理演算 • 論理演算を行う単位が違う 1 1 1 0 0 1 0 0 1 1 1 0 0 1 0 0 論理演算子 ビット毎の論理演算子 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 論理演算では 全体を1つの論理値として扱う ビット毎の論理演算では 各ビットを個別に扱う 64 教科書 pp.78-79., p.84. bit 毎の AND による bit mask • bit毎にANDを取った結果が得られる 1 1 1 0 0 1 0 0 & 0 0 0 0 1 1 1 1 0 0 0 0 0 0 0 0 0 1 0 0 X & Y で 右辺の値をマスクとして用いた場合 0: 0でクリア 1: 元の値をそのまま通過 65 教科書 pp.78-79., p.84. bit 毎の OR による bit mask • bit毎にORを取った結果が得られる 1 1 1 0 0 1 0 0 | 1 1 1 1 0 0 0 0 1 1 1 1 1 1 1 1 0 1 0 0 X | Y で 右辺の値をマスクとして用いた場合 0: 元の値をそのまま通過 1: 1でクリア 66 教科書 pp.78-79., p.84. bit 毎の XOR による bit 反転 • bit毎にXORを取った結果が得られる 1 1 1 0 0 1 0 0 ^ 1 1 1 1 0 0 0 0 @ @ @ @ 0 0 0 1 0 1 0 0 X ^ Y で 右辺の値をマスクとして用いた場合 0: 元の値をそのまま通過 1: bit を 0⇔1 反転 同じ値で再度 XOR を取ると元に戻るので 簡易暗号的な使い方も出来る 67 教科書 pp.78-79., p.84. 1の補数演算子 • 要は単なるビット毎の論理反転 ~ 1 1 1 0 0 1 0 0 ビット毎の論理反転 0 0 0 1 1 0 1 1 68 補数とは • 基数𝑏(𝑏進数) 𝑛桁で表現可能な整数𝑎に対し • 𝑏𝑛 − 𝑎 • 𝑏𝑛 − 𝑎 − 1 : 基数(𝑏)の補数 : 減基数(𝑏 − 1)の補数 • 例: 2進数8桁で表す1について • 2の補数 • 0b100000000 – 0b00000001 = 0b11111111 • 1の補数(単なるビット毎の論理反転) • 0b100000000 – 0b00000001 - 1 = 0b11111110 69 教科書 pp.73-74., p.84. インクレメント、デクレメントの演算子 前置演算子 後置演算子 算術演算子 演算子の機能 ++ インクレメント ++expr -- デクレメント --expr ++ インクレメント expr++ -- デクレメント expr-前置演算子は演算後に値を取り出す。 後置演算子は演算前に値を取り出す。 6 7 8 9 10 incrtest.c mintty+bash+gcc int i = 5; printf("%d\n", printf("%d\n", printf("%d\n", printf("%d\n", $ gcc incrtest.c && ./a 6 5 5 6 ++i); --i); i++); i--); 70 教科書 pp.117-118, 147. 比較演算子 (関係演算子、等値演算子) 関係演算子 等値演算子 演算子 比較の意味 < 左辺が小 expr1 < <= 左辺が小または等しい expr1 <= expr2 > 左辺が大 expr1 > >= 左辺が大または等しい expr1 >= expr2 == 等しい expr1 == expr2 != 等しくない expr1 != expr2 expr2 expr2 演算結果は真(=1)または偽(=0)となる 71 教科書 pp.213-218. ポインタ演算子 • アドレス演算子 • 書式 : &オブジェクト • オブジェクトの配置されたアドレスを得る。 • 間接演算子 • 書式 : *ポインタ • ポインタが指すアドレスに配置されたオブジェクトを得 る。 [1] pp.63-66, 256-257. 72 条件演算子 三項演算子(?:) • 書式: 真 式1 条件式 偽 式2 • 条件式 ? 式1 : 式2 1 2 3 4 5 6 7 8 9 condexprtest.c mintty+bash+gcc #include <stdio.h> $ gcc condexprtest.c && ./a i = 1 not zero $ ./a i = 0 zero void main() { int i; fprintf(stderr, "i = ?\b"); scanf("%d", &i); printf("%s\n", i ? "not zero" : "zero"); } 2015-05-01追加修正 73 演算子の優先度 優先度 高 演算子 ( ) [ ] -> 備考 左から右→ . 右から左← 単項演算子 左から右→ 二項演算子 左から右→ 二項演算子 左から右→ bitシフト 左から右→ 関係演算子 左から右→ 等値演算子 & 左から右→ bit毎のAND ^ 左から右→ bit毎のXOR | 左から右→ bit毎のOR && 左から右→ 論理演算子(AND) || 左から右→ 論理演算子(OR) ?: 右から左← 三項演算子 右から左← 代入演算子 ! ~ ++ * / % + - << < == = 低 結合規則 , -- + - * & (type) sizeof >> <= > >= != += -= *= /= %= &= ^= |= <<= >>= 左から右→ [1] p.65. より 74 配列変数 75 教科書 pp.85-108. 配列変数 • 同じ変数名で複数の要素を管理する char a[10]; // 要素数10のchar型変数の宣言 初期値式が与えられなかった場合、値は不定 ? ? ? ? a[0] a[1] a[2] a[3] ? ... 要素数10の添え字付き変数 a[9] [1] pp.103-104., p.273. 76 教科書 pp.85-108. 配列変数 • 配列変数の要素への代入 char a[10]; // 要素数10のchar型変数の宣言 a[0] = 'a'; // 0番目の要素へ代入 宣言後の代入 初期値式が与えられなかったので、値は不定 'a' ? ? ? a[0] a[1] a[2] a[3] ? ... 要素数10の添え字付き変数 a[9] [1] pp.103-104., p.273. 77 教科書 pp.85-108. 配列変数 • 添え字は値が取れれば変数や数式でも良い int i = 1; char a[10]; // 要素数10のchar型変数の宣言 a[i + 1] = 'a'; // 2番目の要素へ代入 ? ? 'a' ? a[0] a[1] a[2] a[3] ? ... 要素数10の添え字付き変数 a[9] [1] pp.103-104., p.273. 78 教科書 pp.85-108. 配列変数 • 確保した領域外はアクセスは禁止 char a[10]; // 要素数10のchar型変数の宣言 short b = 0x1234; a[10] = 'a';// 宣言された領域外へのアクセス ? ? ? ? a[0] a[1] a[2] a[3] ここに書き込むと何が起こるか分からない 0x1234 ? 'a' b a[9] a[10] ... 他の変数が使っていたらその値を壊してしまう 要素数10の添え字付き変数 [1] pp.103-104., p.273. 79 教科書 pp.85-108. 配列変数 • 初期値式による配列変数の初期化 char a[10] = {'a', 'b'}; //初期値式付きの //要素数10のchar型変数の宣言 初期値式による初期化 初期値式が要素数より少ない場合、残りは0で初期化 'a' 'b' 0 0 a[0] a[1] a[2] a[3] 0 ... 要素数10の添え字付き変数 a[9] [1] pp.103-104., p.273. 80 教科書 pp.85-108. 配列変数 • 初期値式による配列変数の初期化 char a[] = {'a', 'b'}; //初期値式付きで //要素数を省略したchar型変数の宣言 初期値式による初期化 'a' 'b' a[0] a[1] 初期値式の要素数分確保される [1] pp.103-104., p.273. 81 教科書 pp.85-108. 配列変数 • 文字列による初期化(要素数指定) char a[10] = "ab"; 文字列と文字列終端の'\0' //文字列による初期値付きの //要素数10のchar型変数の宣言 初期値式が要素数より少ない場合、残りは0で初期化 'a' 'b' 0 0 a[0] a[1] a[2] a[3] 0 ... 要素数10の添え字付き変数 a[9] [1] pp.103-104., p.273. 82 教科書 pp.85-108. 配列変数 • 文字列による初期化(要素数自動決定) char a[] = "ab";//文字列による初期値付きで //要素数を省略したchar型変数の宣言 文字列と文字列終端の'\0' 'a' 'b' 0 a[0] a[1] a[2] 文字列の文字数+文字列終端'\0'の1文字分の要素 [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 if 文と switch 文 条件分岐 教科書 pp.130-133. 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 { 処理1 if (条件式2) { // 条件式1が偽かつ // 条件式2が真の場合の処理2 } else { // 条件式1が偽かつ // 条件式2が偽の場合の処理3 } } if は任意の数入れ子に出来ます。 条件式1 真 処理2 偽 条件式2 偽 処理3 教科書 pp.130-133. 90 条件分岐 (if, else if, else 文) • 真偽値による場合分け 真 if (条件式1) { // 条件式1が真の場合の処理1 } else if (条件式2) { // 条件式1が偽かつ // 条件式2が真の場合の処理2 } else { // 条件式1が偽かつ // 条件式2が偽の場合の処理3 } else if は任意の数追加出来ます。 条件式1 偽 処理1 真 処理2 条件式2 偽 処理3 91 教科書 pp.134-140. 多分岐判断機構 (switch 文) • 値による場合分け switch (式) { case 値1: // 式が値1の場合の処理1 case 値2: // 式が値2の場合の処理2 default: // 他の条件に // 当てはまらない場合の処理N }; break 文を入れておかないと 次の条件の処理を 連続して実行するので注意。 式 値1 値2 default 処理1 処理2 処理N break break break 92 for文, while文, do-while 文によるループと continue 文、break 文によるループの再開と脱出 繰り返し(ループ) 93 ループの再開と脱出 • continue 文 • while, do while, for 文内で使用可能 • 以降の処理を中断してループ末尾から再開する • for文では後処理(第3パラメータ)も実行する • break 文 • while, do while, for, switch 文内で使用可能 • 以降の処理を中断してループを脱出する • switch文の場合はswitch文から脱出する 94 教科書 p.123. 後判定ループ (do while 文) • 真偽値による繰り返し continue break 処理 do { // 条件式 が真の場合の処理 } while (条件式); 真 条件式 偽 do while 文は、ループ内の処理を 最低1回は実行する。 95 教科書 pp.119-122. 前判定ループ (while 文) • 真偽値による繰り返し while (条件式) { // 条件式が真の場合の処理 } 式2 偽 真 continue 処理 break 96 教科書 pp.124-129. 初期化・更新処理付きループ (for 文) • 真偽値による繰り返し 式1 for (式1; 式2; 式3) { // 式2が真の場合の処理 }; 式2 偽 真 continue 前判定ループだが 式1による初期化と 式3による更新処理を ひとまとめにして書ける。 処理 式3 break 97 教科書 pp.123-129. for文とwhile文 (前判定ループ) • 以下のループは等価 • continue時の式3の扱いに注意 for (式1; 式2; 式3) { // 式2が真の場合の処理 }; 式1; while (式2) { // 式2が真の場合の処理 式3; }; 式1 式2 偽 真 for文の continue 処理 while文の continue 式3 break 98 教科書 pp.123-129. for文とwhile文 (前判定ループ) • 以下のループは等価 • continue時の式3の扱いに注意 for (i = 0; i < 10; i++) { // ループ内の処理 }; i = 0; while (i < 10) { //ループ内の処理 i++; }; i = 0 i < 10 偽 真 for文の continue 処理 while文の continue i++ break 99 教科書 pp.119-122. 後判定ループ (do while 文) • continue, break 後の処理(iの値)に注目 looptest_dowhile.c mintty + bash int i = 0, j = 0, n; $ ./looptest_dowhile n = 10 0, 1: 1st 2nd 3rd 1, 2: 1st continue 1, 3: 1st 2nd 3rd 2, 4: 1st 2nd break 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); mintty + bash $ ./looptest_dowhile n = 0 0, 1: 1st 2nd 3rd do while 文は、 ループ内の処理を 最低1回は実行する。 100 教科書 p.123. 前判定ループ (while 文) • continue, break 後の処理(iの値)に注目 looptest_while.c mintty + bash int i = 0, j = 0, n; $ ./looptest_while n = 10 0, 1: 1st 2nd 3rd 1, 2: 1st continue 1, 3: 1st 2nd 3rd 2, 4: 1st 2nd break 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++; } mintty + bash $ ./looptest_while n = 0 101 教科書 pp.124-129. 初期化・更新処理付きループ (for 文) • continue, break 後の処理(iの値)に注目 looptest_for.c mintty + bash int i = 0, j = 0, n; $ ./looptest_for n = 10 0, 1: 1st 2nd 3rd 1, 2: 1st continue 2, 3: 1st 2nd 3rd 3, 4: 1st 2nd break fprintf(stderr, "n = "); scanf("%d", &n); for (i = 0; i < n; i++) j++; printf("%d, %d: 1st", if (j == 2) {printf(" printf(" 2nd"); if (j == 4) {printf(" printf(" 3rd\n"); } { i, j); continue\n"); continue;} break\n"); break;} mintty + bash $ ./looptest_for n = 0 102 [1] p.281. continue 文 • 以下のループ内に更に小さなループが含ま れない場合の continue は goto contin と同義 for (...) { // ... contin: ; } do { // ... contin: ; } while (...); while (...) { // ... contin: ; } 103 [1] p.281. goto文 • 指定した名札付き文へ移動(ジャンプ)する • 名札(label)は以下のように設定出来る ラベル名: 文 goto 文は 余程理由がない限り使わないこと 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: ; 104 参考文献 • [1] B.W.カーニハン/D.M.リッチー著 石田晴久 訳、プログラミング言語C 第2版 ANSI 規格準 拠、共立出版(1989) 105 宿題 • 次回までに以下の事をやっておくこと。 • 教科書の第3章の終わりまで読み、指示された操 作を試して動作を確認する。 • 不明な点、疑問点についてメモし、次回の授業に 持参する。または、本講義の Moodle コース上に ある第3週宿題用フォーラムに書き込んでおく。
© Copyright 2024 ExpyDoc