Transcript Document

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)