C言語入門

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週宿題用フォーラムに書き込んでおく。