1 C言語入門 第3週 プログラミング言語Ⅰ(実習を含む。), 計算機言語Ⅰ・計算機言語演習Ⅰ, 情報処理言語Ⅰ(実習を含む。) 2 先週の復習1 3 各データ型のサイズ(1/4) • sizeoftest.c による比較 32bit版Cygwin+gcc 64bit版Cygwin+gcc $ gcc sizetest.c && ./a char: 1 wchar_t: 2 shor: 2 int: 4 long: 4 long long: 8 float: 4 double: 8 long double: 12 $ gcc sizetest.c && ./a char: 1 wchar_t: 2 shor: 2 int: 4 long: 8 long long: 8 float: 4 double: 8 long double: 16 コンパイルする環境により 割り当てビット数や 最大値と最小値が異なる可能性がある 4 各データ型のサイズ(2/4) • sizeoftest.c による比較 Borland C++ 5.5 >bcc32 sizeoftest.c && sizeoftest Borland C++ 5.5.1 for Win32 Copyright (c) 1993, 2000 Borland sizeoftest.c: Turbo Incremental Link 5.00 Copyright (c) 1997, 2000 Borland char: 1 wchar_t: 2 shor: 2 int: 4 long: 4 float: 4 double: 8 long double: 10 コンパイルする環境により 割り当てビット数や 最大値と最小値が異なる可能性がある 5 各データ型のサイズ(3/4) • sizeoftest.c による比較 Visual Studio 2013 Express Desktop Windows 32bit版 >cl sizeoftest.c && sizeoftest Microsoft(R) C/C++ Optimizing Compiler Version 18.00.21005.1 for x86 Copyright (C) Microsoft Corporation. All rights reserved. sizeoftest.c Microsoft (R) Incremental Linker Version 12.00.21005.1 Copyright (C) Microsoft Corporation. All rights reserved. /out:sizeoftest.exe sizeoftest.obj char: 1 wchar_t: 2 shor: 2 int: 4 long: 4 long long: 8 float: 4 double: 8 long double: 8 コンパイルする環境により 割り当てビット数や 最大値と最小値が異なる可能性がある 6 各データ型のサイズ(4/4) • sizeoftest.c による比較 Visual Studio 2013 Express Desktop Windows 64bit版 >cl sizeoftest.c && sizeoftest Microsoft(R) C/C++ Optimizing Compiler Version 18.00.21005.1 for x64 Copyright (C) Microsoft Corporation. All rights reserved. sizeoftest.c Microsoft (R) Incremental Linker Version 12.00.21005.1 Copyright (C) Microsoft Corporation. All rights reserved. /out:sizeoftest.exe sizeoftest.obj char: 1 wchar_t: 2 shor: 2 int: 4 long: 4 long long: 8 float: 4 double: 8 long double: 8 コンパイルする環境により 割り当てビット数や 最大値と最小値が異なる可能性がある 7 補足 データ型のサイズ 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 が使えない。 8 変数と定数 9 変数の割り当てとバイトオーダー • 32bit int型の場合 : : : : 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 0x?? : : : : int a; // 変数の宣言 32bit 0x???????? a 10 変数の割り当てとバイトオーダー Intel の x86 系 CPU 等 • 32bit int型の場合(Little Endian) : : : : 0x~00 0x78 0x~01 0x56 0x~02 0x34 0x~03 0x12 0x~04 0x?? 0x~05 0x?? 0x~06 0x?? 0x~07 0x?? 0x~08 0x?? : : : : int a = 0x12345678; // 変数の宣言と初期化 32bit 0x12345678 a 11 変数の割り当てとバイトオーダー ネットワーク上を流れるデータ等 • 32bit int型の場合(Big Endian) : : : : 0x~00 0x12 0x~01 0x34 0x~02 0x56 0x~03 0x78 0x~04 0x?? 0x~05 0x?? 0x~06 0x?? 0x~07 0x?? 0x~08 0x?? : : : : int a = 0x12345678; // 変数の宣言と初期化 32bit 0x12345678 a 12 変数の割り当てとバイトオーダー Intel の x86 系 CPU 等 • 16bit short型の場合(Little Endian) : : : : 0x~00 0x34 0x~01 0x12 0x~02 0x?? 0x~03 0x?? 0x~04 0x?? 0x~05 0x?? 0x~06 0x?? 0x~07 0x?? 0x~08 0x?? : : : : 16bit short a = 0x1234; // 変数の宣言と初期化 0x1234 a 同じ場所から32bitで取ると 0x????1234 a 13 変数の割り当てとバイトオーダー ネットワーク上を流れるデータ等 • 16bit short型の場合(Big Endian) : : : : 0x~00 0x12 0x~01 0x34 0x~02 0x?? 0x~03 0x?? 0x~04 0x?? 0x~05 0x?? 0x~06 0x?? 0x~07 0x?? 0x~08 0x?? : : : : 16bit short a = 0x1234; // 変数の宣言と初期化 0x1234 a 同じ場所から32bitで取ると 0x1234???? a 14 バイトオーダーに関する注意点 • ファイル保存時に注意が必要 • 以下のコードはファイルに保存した値が実行 環境により異なるかも? int32_t a = 0x12345678; // ... fwrite(&a, sizeof(a), 1, fp); 例: 第1週のサンプルプログラム • wavtest.c • bmptest.c Little Endian の環境では正常に動くが Big Endian の環境では正常に動かない! 15 バイトオーダーに関する注意点 • 正常な動作の例 0x12345678 0x12345678 a a ファイルへ 書き込み Little Endian の PC 0x78 0x56 0x34 0x12 ファイルから 読み込み Little Endian の PC 16 バイトオーダーに関する注意点 • 不具合の例 0x12345678 0x78563412 a a ファイルへ 書き込み Little Endian の PC 0x78 0x56 0x34 0x12 ファイルから 読み込み Big Endian の PC 17 バイトオーダーに関する注意点 • 不具合の例 0x12345678 0x78563412 a a ファイルへ 書き込み Big Endian の PC 0x12 0x34 0x56 0x78 ファイルから 読み込み Little Endian の PC 18 バイトオーダーの解決方法の例 • ビット演算を利用 • 1バイトずつに切り分けてファイルへ保存する int32_t a = 0x12345678; // ... fputc((a ) & 0xFF, fp); fputc((a >> 8) & 0xFF, fp); fputc((a >> 16) & 0xFF, fp); fputc((a >> 24) & 0xFF, fp); 絶対に Little Endian で 書き込むことが出来る。 19 教科書 pp.149-206. バイトオーダーの解決方法の例 • よく使う処理は関数にしてまとめる • 再利用し易くなる int fputle32(int32_t a) { int r; r = fputc((a ) & r = fputc((a >> 8) & r = fputc((a >> 16) & r = fputc((a >> 24) & return 4; } 0xFF, 0xFF, 0xFF, 0xFF, fp); fp); fp); fp); if if if if (r (r (r (r == == == == EOF) EOF) EOF) EOF) return return return return EOF; EOF; EOF; EOF; 20 バイトオーダーの解決方法の例 • バイトオーダーを考慮したライブラリを使う • SDL (Simple DirectMedia Layer) ライブラリ • • • • SDL_SwapBE32 関数 http://wiki.libsdl.org/SDL_SwapBE32 SDL_SwapLE32 関数 http://wiki.libsdl.org/SDL_SwapLE32 int32_t a = 0x12345678; Uint32 x = SDL_SwapLE32(a); // ... fwrite(&x, sizeof(x), 1, fp); 絶対に Little Endian で 書き込むことが出来る。 21 fwrite 関数 • size_t fwrite(const void *BUF, size_t SIZE, size_t COUNT, FILE *fp); • 引数: • • • • BUF: SIZE: COUNT: fp: 保存するデータへのポインタ 1要素当りのサイズ 保存する要素数 ファイル構造体へのポインタ • 戻り値: • 書き込んだ要素数 • 正常終了の場合COUNTに同じ 22 fputc 関数 • int fputc(int c, FILE *fp); • 引数: • c: • fp: 書き込む文字 ファイル構造体へのポインタ • 戻り値: • 書き込んだ文字c • エラーの場合EOF 23 値の表示 24 教科書 pp.61, 64-66, 98. 値の表示 • printf関数を使う 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; } いろんな値を表示できる。 $ i d s gcc printftest.c && ./a = 128 = 0.123000 = hello, world 25 教科書 pp.61, 64-66, 98. printf 関数 • int printf(const char *FORMAT, ...); • 引数: • FORMAT: • ...: 書式 任意の数の引数 • 戻り値: • 書き出された文字数。 • エラーの場合負の数。 参考: [1] pp.305-306. 26 教科書 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. 27 教科書 pp.61, 64-66, 98. printf: フラグ • -: 左揃えで印字 • +: 数を符号付きで印字 • スペース: 最初の文字が符号でない場合スペース を前に付ける • 0: フィールド幅いっぱいに左側から0を詰める • #: 別の出力形式を指定。 • • • • o: 先頭の桁を0にする x: 0でない結果の先頭を0xにする e,f,g: 出力に必ず小数点を付ける g: 末尾の0を削除しない 参考: [1] pp.305-306. 28 教科書 pp.61, 64-66, 98. printf: 最小フィールド幅 • 変換された引数は少なくともこの幅になる。 • 必要ならもっと広い幅のフィールドに印字。 • 変換された引数がフィールド幅よりも短い場 合padding(=詰め物)が行われる。 • paddingは通常はスペース。フラグに0が指 定された場合は0が用いられる。 • *: 次の引数の値を用いる 参考: [1] pp.305-306. 29 教科書 pp.61, 64-66, 98. printf: .精度 • 「.」(ピリオド): フィールド幅と精度の分離子 (separator) • 文字列に対しては印字する最大文字数 • e,fの対しては小数点以下に印字すべき桁 数 • gに対しては有効数字の桁数 • 整数に対しては印字すべき最小桁数(頭に0 が付加される) • *: 次の引数の値を用いる 参考: [1] pp.305-306. 30 教科書 pp.61, 64-66, 98. printf: 長さ修飾子 • h: short または float として扱う • l: long として扱う • L: long double として扱う 参考: [1] pp.305-306. 31 教科書 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. 32 教科書 pp.61, 64-66, 98. printf の詳細 • ここでは概略しか示せていないのと一部不正 確な部分もあるので、詳細は bash から man コマンドを用いて以下の方法で確認すること man sprintf • 邦訳は以下のページ • http://linuxjm.sourceforge.jp/html/LDP_man-pages/man3/printf.3.html 教科書 pp.61, 64-66, 98. sprintf のマニュアル導入 • 以下のコマンドを mintty+bash から実行 学内の場合はまず以下の PROXY 設定が必要 export http_proxy=http://proxy.cc.yamaguchi-u.ac.jp:8080/ cygwin-doc パッケージのインストール apt-cyg install cygwin-doc 33 34 値の読み込み 35 教科書 pp.80-83, 254. 値の読み込み • scanf関数を使う scanftest.c int i; double d; char s[16]; キーボードから入力した値を 変数に保存して利用出来る printf("i = "); scanf("%d", &i); printf("d = "); scanf("%lf", &d); printf("s = "); scanf("%s", &s); printf("i = %d\n", i); printf("d = %f\n", d); printf("s = %s\n", s); $ i d s i d s gcc -g scanftest.c && ./a = 1234 = 1234e-5 = hello, world = 1234 = 0.012340 = hello, 36 教科書 pp.80-83, 254. scanf 関数 • int scanf(const char *FORMAT, ...); • 引数: • FORMAT: • ...: 書式 任意の数の引数 値を格納する変数へのポインタ • 戻り値: • 変換され代入された入力項目の数。 • ファイル終端またはエラーの場合EOF。 参考: [1] pp.307-309. 37 教科書 pp.80-83, 254. scanf: 書式 • スペース、タブ: 無視される • (%でない)普通の文字: 入力の次の空白でない文字とマッチ • 変換仕様: • %[*][最大フィールド幅][ターゲット幅]変換文字 int a; scanf("%d", &a); &: アドレス演算子 変数へのポインタを得る スカラ変数の前には & を付ける 配列変数、ポインタ変数には不要 入力文字列を10進数として扱い int型の整数型変数へ代入 参考: [1] pp.250, 307-309. 38 教科書 pp.80-83, 254. scanf: 変換仕様 • *: 入力フィールドはスキップされる 代入抑止 • 最大フィールド幅: 読み込む最大文字数 • ターゲット幅: • h: int を short に • l: int を long に、float を double に • L: float を long doubleに 参考: [1] pp.307-309. 39 教科書 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. 40 教科書 pp.80-83, 254. scanfの詳細 • ここでは概略しか示せていないのと一部不正 確な部分もあるので、詳細は bash から man コマンドを用いて以下の方法で確認すること man sscanf • 邦訳は以下のページ • http://linuxjm.sourceforge.jp/html/LDP_man-pages/man3/scanf.3.html 41 教科書 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 42 補足 scanf: C99 の stdint.h の場合 • #include<inttypes.h> して SCN~ を使う • "%hd" → "%"SCNd16 • "%d" → "%"SCNd32 • "%u" → "%"SCNu32 使う bit 数を確実に保証出来る 実装依存なので 欲しい桁数が扱えないかも? 参考: [1] pp.307-309. 43 備考 buffer obverflow の脆弱性 • 確保した配列よりも長い文字列を入力 $ i d s i d s gcc -g scanftest.c && ./a = 1234 = 1234e-5 = 0123456789abcdefg@@@@@@@@@@@@@@@@ = 1077952576 = 32.501961 = 0123456789abcdefg@@@@@@@@@@@@@@@@ 他の変数の領域を 侵食してしまう 44 備考 buffer obverflow の脆弱性の仕組み • メモリ上の変数の割り当て 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; 確保したサイズ以上の データを書き込むと 他の変数のデータを 上書きしてしまう。 45 備考 buffer obverflow の脆弱性の対策 • 最大フィールド幅を明記する! • "%s" → "%15s" 終端文字列'\0'も格納する必要があるため、 最大フィールド幅は 確保したバイト数 -1 以下にする必要がある。 char s[16]; なら最大15文字まで 46 教科書 p.68. マクロ • preprocessor のキーワード置換機能 • 書式: #define マクロ名 置換内容 • 定数等に名前を付ける際に使う area_of_a_circle.c #define PI 3.14 // ... float r; printf("circle radius = "); scanf("%f", &r); printf("Area of a circle = %f\n", PI * r * r); 47 教科書 p.68. マクロ • 先程の EOF とか SCNd32 もマクロ $ grep "#define.EOF" /usr/include/stdio.h #define EOF (-1) $ grep SCNd32 /usr/include/inttypes.h #define SCNd32 "d" それぞれの環境に 適切な数値や文字列等が設定されている 48 教科書 p.68. マクロ • PI に関しては実は math.h で提供されている $ grep M_PI /usr/include/math.h #define M_PI 3.14159265358979323846 #define M_TWOPI (M_PI * 2.0) #define M_PI_2 1.57079632679489661923 #define M_PI_4 0.78539816339744830962 49 教科書 pp.203-206. ファイルの包含 • preprocessor のファイル取り込み機能 • 別のファイルに記述されたプログラムやマクロ等 を取り込む際に使う • 書式: • #include <ファイル名> //システム提供ファイル用 • /usr/include 等から探して取り込む • #include "ファイル名" //ユーザー作成ファイル用 • 作業ディレクトリから探して取り込む 50 備考: UNIX コマンド grep コマンド • grep [OPTIONS] PATTERN [FILE ...] • 検索文字列を含むファイルを検索する • 引数 • PATTERN: 正規表現等による検索文字列 • FILE: 検索対象のファイルやディレクトリ • OPTIONS: • • • • • -R: ディレクトリ下のすべてのファイルを検索 -n: 行番号を表示 -A NUM: マッチ位置の後NUM行も表示 -B NUM: マッチ位置の前NUM行も表示 -C NUM: マッチ位置の前後NUM行も表示 51 備考: UNIX コマンド 正規表現 • • • • • • • • c \c . [...] [^...] * + ? 文字c 文字\c 任意の一文字 []内の任意の一文字 []内に含まれない任意の一文字 直前のパターンが0回以上反復 直前のパターンが1回以上反復 直線のパターンが0または1回出現 52 演算子 53 教科書 p.78, 84, 195. sizeof 演算子 • 変数やデータ型の割り当てバイト数を求める arraysizetest.c int a[10]; // 要素数10のint型の配列変数 printf("%d\n", printf("%d\n", printf("%d\n", printf("%d\n", sizeof(int)); //int型の割り当てバイト数 sizeof(a)); //配列変数aの割り当てバイト数 sizeof(a[0]));//変数a[0]の割り当てバイト数 sizeof(a)/sizeof(a[0])); //配列変数aの要素数 $ gcc -g arraysizetest.c && ./a 4 40 4 10 54 教科書 p.70, 84. 型変換(cast)演算子 • (変換したい型) 値 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); b の値を double 型に 変換 cast 演算子 (type) type: 任意のデータ型 整数同士の割り算だと 1/2 が 0 になっている。 $ gcc casttest.c && ./a 0.000000 0.500000 55 教科書 p.69, 84. 算術演算子 単項演算子 二項演算子 算術演算子 演算子の機能 書式 + 被演算数の値 + expr - 被演算数の符号反転 - expr + 加算 expr1 + expr2 - 減算 expr1 – expr2 * 乗算 expr1 * expr2 / 除算 expr1 / expr2 % 剰余算 expr1 % expr2 56 教科書 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のような演算と代入を同時に行う 57 教科書 pp.78-79, 84. bit演算子 算術演算子 演算子の機能 単項演算子 ~ 1の補数 ~ expr 二項演算子 << 左シフト expr1 << expr2 >> 右シフト expr1 >> expr2 & ビット毎のAND expr1 & expr2 ^ ビット毎のXOR expr1 ^ expr2 | ビット毎のOR expr1 | expr2 教科書 pp.78-79, 84. 58 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 59 教科書 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 環境依存なので、環境によっては 論理シフトになる可能性も 考慮しておくこと。 60 教科書 pp.78-79, 84. 右シフト • 実際の環境はどうなっているのか? bitshifttest.c unsigned int uc = 0xe8000000; // == 0b11100100... signed int sc = 0xe8000000; // == 0b11100100... uc >>= 2 + 8 * 3; sc >>= 2 + 8 * 3; printf("%02x\n", uc & 0xff); // 0b00111001 == 0x3a printf("%02x\n", sc & 0xff); // 0b11111001 == 0xfa ? 61 教科書 pp.78-79, 84. 右シフト • 算術シフトになっている環境が多い? $ gcc bitshifttest.c && ./a 3a fa 64bit 版 cygwin GNU C 4.8.2 >bcc32 bitshifttest.c && bitshifttest ... 3a fa Borland C++ 5.5 >cl bitshifttest.c && bitshifttest ... 3a fa Visual Studio 2013 Express Desktop Windows 64bit 版 62 教科書 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)となる 63 教科書 pp.78-79, 84. C言語の論理値(真偽値) • 数値を論理値として用いている 論理値 数値 真偽値判定時 偽 0 0のみが偽として扱われる 真 1 0以外はすべて真として扱われる • 論理演算とビット毎の論理演算に注意 logictest.c 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 && || & | y); y); y); y); $ x x x x gcc logictest.c && ./a && y = 1 || y = 1 & y = 0 | y = 3 64 教科書 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つの論理値として扱う ビット毎の論理演算では 各ビットを個別に扱う 65 教科書 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: 元の値をそのまま通過 66 教科書 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でクリア 67 教科書 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 を取ると元に戻るので 簡易暗号的な使い方も出来る 68 教科書 pp.78-79., p.84. 1の補数演算子 • 要は単なるビット毎の論理反転 ~ 1 1 1 0 0 1 0 0 ビット毎の論理反転 0 0 0 1 1 0 1 1 69 補数とは • 基数𝑏(𝑏進数) 𝑛桁で表現可能な整数𝑎に対し • 𝑏𝑛 − 𝑎 • 𝑏𝑛 − 𝑎 − 1 : 基数(𝑏)の補数 : 減基数(𝑏 − 1)の補数 • 例: 2進数8桁で表す1について • 2の補数 • 0b100000000 – 0b00000001 = 0b11111111 • 1の補数(単なるビット毎の論理反転) • 0b100000000 – 0b00000001 - 1 = 0b11111110 訂正: 2015-01-13 誤: 0c100000000 正: 0b100000000 70 教科書 pp.73-74., p.84. インクレメント、デクレメントの演算子 前置演算子 後置演算子 算術演算子 演算子の機能 ++ インクレメント ++expr -- デクレメント --expr ++ インクレメント expr++ -- デクレメント expr-- int i = 5; printf("%d\n", printf("%d\n", printf("%d\n", printf("%d\n", 前置演算子は演算後に値を取り出す。 後置演算子は演算前に値を取り出す。 ++i); --i); i++); i--); $ gcc incrtest.c && ./a 6 5 5 6 71 教科書 pp.117-118, 147. 比較演算子 (関係演算子、等値演算子) 関係演算子 等値演算子 演算子 比較の意味 < 左辺が小 expr1 < <= 左辺が小または等しい expr1 <= expr2 > 左辺が大 expr1 > >= 左辺が大または等しい expr1 >= expr2 == 等しい expr1 == expr2 != 等しくない expr1 != expr2 expr2 expr2 演算結果は真(=1)または偽(=0)となる 72 演算子の優先度 優先度 高 演算子 ( ) [ ] -> 備考 左から右→ . 右から左← 単項演算子 左から右→ 二項演算子 左から右→ 二項演算子 左から右→ bitシフト 左から右→ 関係演算子 左から右→ 等値演算子 & 左から右→ bit毎のAND ^ 左から右→ bit毎のXOR | 左から右→ bit毎のOR && 左から右→ 論理演算子(AND) || 左から右→ 論理演算子(OR) ?: 右から左← 三項演算子 右から左← 代入演算子 ! ~ ++ * / % + - << < == = 低 結合規則 , -- + - * & (type) sizeof >> <= > >= != += -= *= /= %= &= ^= |= <<= >>= 左から右→ [1] p.65. より 73 配列変数 74 教科書 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. 75 教科書 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. 76 教科書 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. 77 教科書 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. 78 教科書 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. 79 教科書 pp.85-108. 配列変数 • 初期値式による配列変数の初期化 char a[] = {'a', 'b'}; //初期値式付きで //要素数を省略したchar型変数の宣言 初期値式による初期化 'a' 'b' a[0] a[1] 初期値式の要素数分確保される [1] pp.103-104., p.273. 80 教科書 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. 81 教科書 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. 82 変数の初期化 • 明示的な初期化がない場合 • 外的変数、静的変数→0 • 自動変数、レジスタ変数→不定 • 初期化する場合 • 外的変数、静的変数←定数式でのみ初期化可 • コンパイル時に1度だけ初期化される • 自動変数、レジスタ変数←任意の式で初期化可 • 実行時にブロック毎に初期化される [1] pp.103-104., p.273. 83 配列変数の初期化 • 要素数を与えない場合 • 初期値式の数で配列のサイズが決まる • 要素数を与えた場合 • 初期値式を与えない場合 • 値は不定 • 初期値式を与える場合 • 要素数を超えるとエラー • 要素数に足りない部分は0で初期化される [1] pp.103-104., p.273. 84 制御構造 教科書 pp.130-133. 85 条件分岐 (if 文) • 真偽値による場合分け 真 if (条件式) { // 条件式が真の場合の処理1 } 処理1 条件式 偽 教科書 pp.130-133. 86 条件分岐 (if, else 文) • 真偽値による場合分け if (条件式) { // 条件式が真の場合の処理1 } else { // 条件式が偽の場合の処理2 } 真 処理1 条件式 偽 処理2 教科書 pp.130-133. 87 入れ子の条件分岐 (if, else 文) • 真偽値による場合分け 真 if (条件式1) { // 条件式1が真の場合の処理1 処理1 } else { if (条件式2) { // 条件式1が偽かつ // 条件式2が真の場合の処理2 } else { // 条件式1が偽かつ // 条件式2が偽の場合の処理3 } if は任意の数入れ子に出来ます。 } 条件式1 真 処理2 偽 条件式2 偽 処理3 教科書 pp.130-133. 88 条件分岐 (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 89 教科書 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 90 教科書 pp.119-122. 前判定ループ (while 文) • 真偽値による繰り返し while (条件式) { // 条件式が真の場合の処理 } 条件式 真 処理 偽 91 教科書 p.123. 後判定ループ (do while 文) • 真偽値による繰り返し 処理 do { // 条件式 が真の場合の処理 } while (条件式); 真 条件式 偽 do while 文は、ループ内の処理を 最低1回は実行する。 92 教科書 pp.124-129. 初期化・更新処理付きループ (for 文) • 真偽値による繰り返し for(式1; 式2; 式3) { // 式2が真の場合の処理 }; 式1 式2 真 前判定ループだが 式1による初期化と 式3による更新処理を ひとまとめにして書ける。 処理 式3 偽 93 参考文献 • [1] B.W.カーニハン/D.M.リッチー著 石田晴久 訳、プログラミング言語C 第2版 ANSI 規格準 拠、共立出版(1989)
© Copyright 2025 ExpyDoc