C言語入門
第3週
プログラミング言語Ⅰ(実習を含む。),
計算機言語Ⅰ・計算機言語演習Ⅰ,
先週の復習
出力のリダイレクト
• 書式: コマンド > ファイル
• コマンドの出力をファイルに繋ぐ
• コマンド実行時の出力をファイルに保存出来る。
• 例: 第1週の hello.c について
mintty + bash./a > hello_result.txt
コマンドプロンプトhello > hello_result.txt
hello_result.txthello, world
値の表示
値の表示
• 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
printf 関数
• int printf(const char *FORMAT, ...);
• 引数:
• FORMAT:
書式
• ...:
任意の数の引数
• 戻り値:
• 書き出された文字数。
• エラーの場合負の数。
教科書 pp.61, 64-66, 98. 参考: [1] pp.305-306.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.printf: フラグ
• -: 左揃えで印字
• +: 数を符号付きで印字
• スペース: 最初の文字が符号でない場合スペース
を前に付ける
• 0: フィールド幅いっぱいに左側から0を詰める
• #: 別の出力形式を指定。
• o: 先頭の桁を0にする
• x: 0でない結果の先頭を0xにする
• e,f,g: 出力に必ず小数点を付ける
• g: 末尾の0を削除しない
教科書 pp.61, 64-66, 98. 参考: [1] pp.305-306.printf: 最小フィールド幅
• 変換された引数は少なくともこの幅になる。
• 必要ならもっと広い幅のフィールドに印字。
• 変換された引数がフィールド幅よりも短い場
合padding(=詰め物)が行われる。
• paddingは通常はスペース。フラグに0が指
定された場合は0が用いられる。
• *: 次の引数の値を用いる
教科書 pp.61, 64-66, 98. 参考: [1] pp.305-306.printf: .精度
• 「.」(ピリオド): フィールド幅と精度の分離子
(separator)
• 文字列に対しては印字する最大文字数
• e,fの対しては小数点以下に印字すべき桁
数
• gに対しては有効数字の桁数
• 整数に対しては印字すべき最小桁数(頭に0
が付加される)
• *: 次の引数の値を用いる
教科書 pp.61, 64-66, 98. 参考: [1] pp.305-306.printf: 長さ修飾子
• h: short または float として扱う
• l: long として扱う
• L: long double として扱う
教科書 pp.61, 64-66, 98. 参考: [1] pp.305-306.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.
printf の詳細
• ここでは概略しか示せていないのと一部不正
確な部分もあるので、詳細は bash から man
コマンドを用いて以下の方法で確認すること
• 邦訳は以下のページ
•
http://linuxjm.sourceforge.jp/html/LDP_man-pages/man3/printf.3.html
mintty+bashman sprintf
教科書 pp.61, 64-66, 98. mintty+bashman 3 printf
3 はマニュアルのセクション番号を意味する。 セクション 3 はサブルーチン (つまりライブラリ関数) 関連のマニュアル http://linuxjf.sourceforge.jp/JFdocs/Man-Page-2.html printf はセクション 1 にもあるので sprintf かセクショ ン 3 の printf を引く必要がある。 2015-04-27 修正&追記printf のマニュアル導入
• printf のマニュアルが引けない場合、
以下のコマンドを mintty+bash から実行
cygwin-doc パッケージのインストール
mintty+bash
apt-cyg install cygwin-doc
演習: 円の面積を計算せよ(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 追記マクロ
• 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マクロ
• コンパイル時のオプション -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マクロ
• 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)
マクロ
• 先程の 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.ファイルの包含
• preprocessor のファイル取り込み機能
• 別のファイルに記述されたプログラムやマクロ等
を取り込む際に使う
• 書式:
• #include <ファイル名>
//システム提供ファイル用
• /usr/include 等から探して取り込む
• #include "ファイル名"
//ユーザー作成ファイル用
• 作業ディレクトリから探して取り込む
教科書 pp.203-206.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 コマンド正規表現
表記
意味
c
文字c
¥c
文字¥c
.
任意の一文字
[...]
[]内の任意の一文字
[^...]
[]内に含まれない任意の一文字
*
直前のパターンが0回以上反復
+
直前のパターンが1回以上反復
?
直線のパターンが0または1回出現
|
前後の正規表現の何れか
(...)
()内の正規表現をグループ化
^
行頭にマッチ
$
行末にマッチ
備考: UNIX コマンド演習: 円の面積を計算せよ(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 個の面積を
計算せよ。
fprintf 関数
• int fprintf(FILE *fp,
const char *FORMAT, ...);
• printfの結果をfpへ書き出す
• 引数:
• fp:
FILE 構造体へのポインタ
• FORMAT:
書式
• ...:
任意の数の引数
• 戻り値:
• 書き出された文字数
• エラーの場合負の数
教科書 pp.61, 64-66, 98, 300. 参考: [1] pp.305-306.標準入出力と標準エラー出力
• 以下の入出力が利用できる
• 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 は標準では対象外(指定すれば対
象にすることも可能)
標準入出力と標準エラー出力
• パイプやリダイレクトで処理されたくない内容
は stderr へ出力すると良い
• fprintf を使うと、出力先を変更出来る
[1] pp.196, 199, 218. stdiotest.cprintf("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)がファイルへ出力されず 画面に出力されている事が確認出来る。
標準入出力と標準エラー出力
• stdin,stdout,stderrはstdio.hで定義されている
• stdio.h は
st
an
d
ard
i
nput /
o
utput
h
eader
値の読み込み
値の読み込み
• 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 20scanf 関数
• int scanf(const char *FORMAT, ...);
• 引数:
• FORMAT:
書式
• ...:
任意の数の引数
値を格納する変数へのポインタ
• 戻り値:
• 変換され代入された入力項目の数。
• ファイル終端またはエラーの場合EOF。
教科書 pp.80-83, 254. 参考: [1] pp.307-309.scanf: 書式
• スペース、タブ: 無視される
• (%でない)普通の文字:
入力の次の空白でない文字とマッチ
• 変換仕様:
• %[*][最大フィールド幅][ターゲット幅]変換文字
int a;
scanf("
%d
", &a);
教科書 pp.80-83, 254. 参考: [1] pp.250, 307-309. 入力文字列を10進数として扱い int型の整数型変数へ代入 スカラ変数の前には & を付ける 配列変数、ポインタ変数には不要 &: アドレス演算子 変数へのポインタを得るscanf: 変換仕様
• *: 入力フィールドはスキップされる
代入抑止
• 最大フィールド幅: 読み込む最大文字数
• ターゲット幅:
• h: int を short に
• l: int を long に、float を double に
• L: float を long doubleに
教科書 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. 教科書 pp.80-83, 254.scanf の詳細
• ここでは概略しか示せていないのと一部不正
確な部分もあるので、詳細は bash から man
コマンドを用いて以下の方法で確認すること
• 邦訳は以下のページ
•
http://linuxjm.sourceforge.jp/html/LDP_man-pages/man3/scanf.3.html
教科書 pp.80-83, 254. mintty+bashman scanf
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 32bitint a;
scanf("%d", &a);
a 0x???????? &a &: アドレス演算子 変数が配置されているメモリ上のアドレスが得られる このアドレスのことをC言語ではポインタと呼ぶ 教科書 pp.80-83, 254. scanf に値の格納先の アドレスを渡すscanf: C99 の stdint.h の場合
参考: [1] pp.307-309. 補足• #include<inttypes.h> してSCN~を使う
• "%hd" → "%"SCNd16
• "%d" → "%"SCNd32
• "%u" → "%"SCNu32
実装依存なので 欲しい桁数が扱えないかも? 使う bit 数を確実に保証出来るbuffer overflow の脆弱性
• 確保した配列よりも長い文字列を入力
備考
mintty+bash+gcc
$ gcc scanftest.c && ./a
i = 1234
d = 1234e-5
s = 0123456789abcdefg@@@@@@@@@@@@@@@@
i: 1077952576
d: 32.501961
s: 0123456789abcdefg@@@@@@@@@@@@@@@@
他の変数の領域を
侵食してしまう
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; 確保したサイズ以上の データを書き込むと 他の変数のデータを 上書きしてしまう。 備考buffer overflow の脆弱性の対策
• 最大フィールド幅を明記する!
• "%s" → "%15s"
終端文字列'¥0'も格納する必要があるため、
最大フィールド幅は
確保したバイト数 -1 以下にする必要がある。
char s[16]; なら最大15文字まで
備考演習: 円の面積を計算せよ(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 個の
面積を計算せよ。
sizeof 演算子
• コンパイル時に変数やデータ型の割り当てバ
イト数を求める演算子
• sizeof オブジェクト
• sizeof(型名)
教科書 p.78, 84, 195.sizeof 演算子の例1
• 例)各データ型の割り当てバイト数
教科書 p.78, 84, 195. sizeof_ex1.cprintf("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
各データ型のサイズ(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 コンパイルする環境により 割り当てビット数や 最大値と最小値が異なる可能性がある
各データ型のサイズ(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 コンパイルする環境により 割り当てビット数や 最大値と最小値が異なる可能性がある
各データ型のサイズ(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 コンパイルする環境により 割り当てビット数や 最大値と最小値が異なる可能性がある
各データ型のサイズ(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 コンパイルする環境により 割り当てビット数や 最大値と最小値が異なる可能性がある
各データ型のサイズ(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
データ型のサイズ
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 が使えない。 補足
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
sizeof 演算子の例3
• 例)配列変数の割り当てバイト数
教科書 p.78, 84, 195. sizeof_ex3.cint 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 の要素数
型変換(cast)演算子
• (変換したい型) 値
教科書 p.70, 84. casttest.cint 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
暗黙の算術変換 (概略)
• 二項演算子の両辺が異なる型の場合
以下の手順で型を変換(符号ありの場合)
• 基本的には大きい方へと型をそろえて行く処理
1. 片方がlong double: 他方をlong doubleに変換
2. 片方がdouble: 他方を doubleに変換
3. 片方がfloat: 他方をfloatに変換
4. char, shortをintに変換
5. 片方がlong: 他方をlongに変換
算術演算子
算術演算子 演算子の機能 書式 単項演算子 + 被演算数の値 + expr - 被演算数の符号反転 - expr 二項演算子 + 加算 expr1 + expr2 - 減算 expr1 – expr2 * 乗算 expr1 * expr2 / 除算 expr1 / expr2 % 剰余算 expr1 % expr2 教科書 p.69, 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 教科書 pp.75-79, 84.bit演算子
算術演算子 演算子の機能単項演算子 ~ 1の補数 ~ expr
二項演算子 << 左シフト expr1 << expr2 >> 右シフト expr1 >> expr2 & ビット毎のAND expr1 & expr2 ^ ビット毎のXOR expr1 ^ expr2 | ビット毎のOR expr1 | expr2 教科書 pp.78-79, 84.
• 利用可能 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.• 最上位ビットより上位は符号拡張される
• 符号ビットが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.• 実際の環境はどうなっているのか?
右シフト
教科書 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• 算術シフトになっている環境が多い?
右シフト
教科書 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
論理演算
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.
C言語の論理値(真偽値)
• 数値を論理値として用いている
• 論理演算とビット毎の論理演算に注意
論理値 数値 真偽値判定時 偽 0 0のみが偽として扱われる 真 1 0以外はすべて真として扱われる logictest.cint 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
論理演算とビット毎の論理演算
• 論理演算を行う単位が違う
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.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.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.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.1の補数演算子
• 要は単なるビット毎の論理反転
1 1 1 0 0 1 0 0
ビット毎の論理反転
0 0 0 1 1 0 1 1
教科書 pp.78-79., p.84.~
補数とは
• 基数
𝑏(𝑏進数) 𝑛桁で表現可能な整数𝑎に対し
•
𝑏
𝑛
− 𝑎
: 基数(𝑏)の補数
•
𝑏
𝑛
− 𝑎 − 1 : 減基数(𝑏 − 1)の補数
• 例: 2進数8桁で表す1について
• 2の補数
• 0b
1
00000000 – 0b00000001 = 0b11111111
• 1の補数(単なるビット毎の論理反転)
• 0b
1
00000000 – 0b00000001 - 1 = 0b11111110
インクレメント、デクレメントの演算子
算術演算子 演算子の機能 前置演算子 ++ インクレメント ++expr -- デクレメント --expr 後置演算子 ++ インクレメント expr++ -- デクレメント expr-- 前置演算子は演算後に値を取り出す。 後置演算子は演算前に値を取り出す。 incrtest.cint 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
比較演算子
(関係演算子、等値演算子)
演算子 比較の意味 関係演算子 < 左辺が小 expr1 < expr2 <= 左辺が小または等しい expr1 <= expr2 > 左辺が大 expr1 > expr2 >= 左辺が大または等しい expr1 >= expr2 等値演算子 == 等しい expr1 == expr2 != 等しくない expr1 != expr2 教科書 pp.117-118, 147.演算結果は真(=1)または偽(=0)となる
ポインタ演算子
• アドレス演算子
• 書式 : &オブジェクト
• オブジェクトの配置されたアドレスを得る。
• 間接演算子
• 書式 : *ポインタ
• ポインタが指すアドレスに配置されたオブジェクトを得
る。
教科書 pp.213-218.• 書式:
• 条件式 ? 式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追加修正
演算子の優先度
演算子 結合規則 備考
( ) [ ] -> . 左から右→
! ~ ++ -- + - * & (type) sizeof 右から左← 単項演算子
* / % 左から右→ 二項演算子 + - 左から右→ 二項演算子 << >> 左から右→ bitシフト < <= > >= 左から右→ 関係演算子 == != 左から右→ 等値演算子 & 左から右→ bit毎のAND ^ 左から右→ bit毎のXOR | 左から右→ bit毎のOR && 左から右→ 論理演算子(AND) || 左から右→ 論理演算子(OR) ?: 右から左← 三項演算子 = += -= *= /= %= &= ^= |= <<= >>= 右から左← 代入演算子 , 左から右→ [1] p.65. より 高 低 優先度
配列変数
• 同じ変数名で複数の要素を管理する
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.
配列変数
• 配列変数の要素への代入
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.配列変数
• 添え字は値が取れれば変数や数式でも良い
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.配列変数
• 確保した領域外はアクセスは禁止
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' 他の変数が使っていたらその値を壊してしまう ここに書き込むと何が起こるか分からない配列変数
• 初期値式による配列変数の初期化
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.配列変数
• 初期値式による配列変数の初期化
char a[] = {'a', 'b'}; //初期値式付きで
//要素数を省略したchar型変数の宣言
教科書 pp.85-108. a[0] 'a' a[1] 'b' 初期値式の要素数分確保される 初期値式による初期化 [1] pp.103-104., p.273.配列変数
• 文字列による初期化(要素数指定)
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.配列変数
• 文字列による初期化(要素数自動決定)
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.変数の初期化
• 明示的な初期化がない場合
• 外的変数、静的変数→0
• 自動変数、レジスタ変数→不定
• 初期化する場合
• 外的変数、静的変数←定数式でのみ初期化可
• コンパイル時に1度だけ初期化される
• 自動変数、レジスタ変数←任意の式で初期化可
• 実行時にブロック毎に初期化される
[1] pp.103-104., p.273.配列変数の初期化
• 要素数を与えない場合
• 初期値式の数で配列のサイズが決まる
• 要素数を与えた場合
• 初期値式を与えない場合
• 値は不定
• 初期値式を与える場合
• 要素数を超えるとエラー
• 要素数に足りない部分は0で初期化される
[1] pp.103-104., p.273.制御構造
条件分岐
条件分岐
(if 文)
• 真偽値による場合分け
if (条件式) {
// 条件式が真の場合の処理1
}
条件式 処理1 真 偽 教科書 pp.130-133.条件分岐
(if, else 文)
• 真偽値による場合分け
if (条件式) {
// 条件式が真の場合の処理1
} else {
// 条件式が偽の場合の処理2
}
条件式 処理1 処理2 真 偽 教科書 pp.130-133.入れ子の条件分岐
(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.条件分岐
(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.多分岐判断機構 (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.繰り返し(ループ)
for文, while文, do-while 文によるループと
ループの再開と脱出
• continue 文
• while, do while, for 文内で使用可能
• 以降の処理を中断してループ末尾から再開する
• for文では後処理(第3パラメータ)も実行する
• break 文
• while, do while, for, switch 文内で使用可能
• 以降の処理を中断してループを脱出する
• switch文の場合はswitch文から脱出する
後判定ループ (do while 文)
• 真偽値による繰り返し
do {
// 条件式 が真の場合の処理
} while (条件式);
条件式 処理 真 偽 do while 文は、ループ内の処理を 最低1回は実行する。 教科書 p.123. continue break前判定ループ (while 文)
• 真偽値による繰り返し
while (条件式) {
// 条件式が真の場合の処理
}
教科書 pp.119-122. 式2 真 偽 処理 continue break初期化・更新処理付きループ (for 文)
• 真偽値による繰り返し
for (式1; 式2; 式3) {
// 式2が真の場合の処理
};
式2 式1 真 偽 前判定ループだが 式1による初期化と 式3による更新処理を ひとまとめにして書ける。 処理 式3 教科書 pp.124-129. continue breakfor文とwhile文 (前判定ループ)
• 以下のループは等価
• continue時の式3の扱いに注意
for (式1; 式2; 式3) {
// 式2が真の場合の処理
};
式2 式1 真 偽 処理 式3 教科書 pp.123-129. for文の continue break式1;
while (式2) {
// 式2が真の場合の処理
式3;
};
while文の continuefor文と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後判定ループ (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回は実行する。
前判定ループ (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
初期化・更新処理付きループ (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
continue 文
• 以下のループ内に更に小さなループが含ま
れない場合の continue は goto contin と同義
for (...) { // ... contin: ; } [1] p.281. do { // ... contin: ; } while (...); while (...) { // ... contin: ; }
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 文は 余程理由がない限り使わないこと