2015/9/7-8 1 大 島 聡 史 情 報 基 盤 センター 助 教
2015/9/7-8 2 目 標 (C 言 語 を 全 く 知 らない 状 態 から) C 言 語 をある 程 度 使 えるようになる C 言 語 の 基 本 的 な 知 識 を 得 る ベクトルや 行 列 を 使 った 計 算 を 行 うプログラムを 作 れるよ うになる
2015/9/7-8 3 C 言 語 とは? 手 続 き 型 言 語 書 いてある 処 理 を( 基 本 的 に) 上 から 順 番 に 行 っていく C 言 語 の 用 途 システムソフトウェア(OS コンパイラ 通 信 ) 科 学 技 術 計 算 数 値 計 算 ( 特 に 計 算 速 度 が 重 要 なもの Fortran で 行 うことも 多 い) 電 化 製 品 などの 組 み 込 みシステム C 言 語 の 主 な 構 成 要 素 関 数 変 数 配 列 フロー 制 御 ( 分 岐 繰 り 返 し) コメント
2015/9/7-8 4 1. テキストエディタでソースコードを 書 く emacsやviなどのテキストエディタ eclipseなどの 統 合 開 発 環 境 WordやWriterなどのリッチな 文 字 装 飾 機 能 を 持 つエディタ(ワープロ ソフト)は( 普 通 は) 使 わない 2. コンパイラを 使 って 実 行 可 能 形 式 に 変 換 (コンパイル)する gccなど ( 特 に 指 定 しないと)a.outというファイルが 作 成 される 3. 実 行 する./a.out メモ C 言 語 を 用 いて 書 かれたプログラムを 実 行 するにはコンパイルが 必 要 コンパイル 不 要 のプログラミング 環 境 もある シェルスクリプト Perl Python Ruby JavaScriptなど
2015/9/7-8 5 書 く 内 容 #include <stdio.h> #include <stdlib.h> int main(int argc, char**argv){ printf( Hello, World! n ); return EXIT_SUCCESS; はダブルクオーテーション はバックスラッシュ n は 改 行 を 意 味 する 特 殊 な 記 述 何 故 か 初 めてのプログラミング では Hello, World! と 出 力 す るという 伝 統 (?)がある ファイル 名 は 何 でも 良 いが 拡 張 子 は.c とする( 例 source.c) 注 意 点 など C 言 語 プログラムは 全 て 半 角 英 数 で 書 く C 言 語 では 日 本 語 が 使 えない と いうわけではないが コメント 文 以 外 では 使 わないこと 全 角 スペースもNG タブはOK( 基 本 的 に 半 角 スペース と 同 じように 扱 われる) ; を 忘 れない 文 ( 処 理 )の 区 切 りは 改 行 ではなく セミコロン 改 行 は 適 当 に 入 れて 良 い 区 切 りっぽいところ はだいたい OK 逆 に 改 行 せずに 違 う 処 理 を 入 れて も 良 い ;のあとにつなげてよい int main 以 降 は 一 行 にまとめられる
2015/9/7-8 6 コンパイル 最 低 限 必 要 な 内 容 gcc source.c gccは 代 表 的 なCコンパイラの 一 つ オープンソース 書 き 間 違 いがあれば 指 摘 してくれる よく 使 うオプション(コンパイラによって 違 う) 出 力 ファイル 名 を 指 定 する 問 題 があるかもしれないときに 警 告 する コンパイラによる 最 適 化 を 行 う デバッグのときに 色 々な 情 報 を 出 す -o ファイル 名 -Wall -O -O2 -O3 など -g 詳 しくはman gccかgcc --helpかオンラインの 資 料 を 参 照 実 行./a.out カレントディレクトリにあるプログラムを 実 行 するときには./ が 必 要 別 のディレクトリにあるプログラムを 実 行 するときは 気 にしなくて 良 い 実 行 権 が 必 要 ( 普 通 は 勝 手 に 付 くので 気 にしなくて 良 い)
2015/9/7-8 7 #include <stdio.h> #include <stdlib.h> int main(int argc, char**argv){ printf( Hello, world! n ); return EXIT_SUCCESS; main int 型 引 数 とchar** 型 引 数 をとることができるint 型 の 関 数 プログラムが 実 行 開 始 される 場 所 なので 絶 対 に 必 要 printf 指 定 されたものを 画 面 に 表 示 するための 関 数 stdio.hというヘッダファイルをincludeすると 使 える return 処 理 の 結 果 を 伝 える 記 述 #include 別 のファイルの 内 容 を 取 り 込 むための 記 述 他 人 (ライブラリ)に 提 供 されている 機 能 を 利 用 する 時 などに 使 う で 括 ったときはソースと 同 じ 場 所 にある ファイル <>で 括 ったときはあるルールに より 決 められた 場 所 にあるファイル( 環 境 変 数 やコンパイルオプションで 設 定 可 能 ) 後 述 関 数 引 数 int 型 char** 型 return EXIT_SUCCESS; はプログラムが 正 常 終 了 したことを 意 味 する 記 述 stdlib.hというヘッダファイルをincludeすると 使 える 利 用 可 能 な 機 能 の 情 報 が 書 かれたテキストファイル(と 思 っておこう)
2015/9/7-8 8 変 数 値 を 保 持 するための 入 れ 物 関 数 手 続 きをひとまとめにしたもの 関 数 を 実 行 中 に 別 の 関 数 を 実 行 することも 可 能 引 数 として 値 を 受 け 取 ることや 実 行 元 に 結 果 を 返 すことも 可 能 してもしなくても 構 わない
2015/9/7-8 9 宣 言 の 例 ここから という 変 数 を 使 います int a; int 型 の 変 数 a float v, x; float 型 の 変 数 vとx 主 なデータ 型 の 例 変 数 にはこのような 種 類 の 値 が 入 ります char 8bit 整 数 ( 文 字 にも 使 う) int 32bit 整 数 float 32bit 単 精 度 浮 動 小 数 (1.0f のように 書 く) double 32bit 倍 精 度 浮 動 小 数 unsigned 別 のデータ 型 の 前 に 付 けて 0 以 上 の 値 にしか 使 えないようにする void 型 無 し( 特 殊 なもの) 利 用 例 ( 参 照 と 代 入 ) 宣 言 時 の 値 は 不 定 int a = 0; のように 宣 言 と 初 期 化 を 同 時 に 行 うことも 可 能 -128 ~ 127-2,147,483,648 ~ 2,147,483,647 +- 10^38 +- 10^308 型 によって 表 現 できる 範 囲 や 必 要 なデータ 量 ( 使 用 するメモリ の 量 )が 違 う x = 10; xに10を 設 定 ( 代 入 ) y = x * 2; yにxの2 倍 の 値 を 設 定 (xを 参 照 して2 倍 化 してyに 代 入 )
2015/9/7-8 10 いわゆる 普 通 の 四 則 演 算 はもちろんとして 様 々な 演 算 子 が 用 意 されている 算 術 演 算 子 代 入 演 算 子 比 較 演 算 子 論 理 演 算 子 算 術 演 算 子 演 算 子 種 別 例 備 考 + 加 算 - 減 算 * 乗 算 / 除 算 整 数 型 に 使 うと 切 り 捨 て % 剰 余 % 整 数 型 でしか 利 用 できない もう 少 し あとで 代 入 演 算 子 演 算 子 種 別 例 備 考 = 代 入 に を 代 入 する = 算 術 演 算 代 入 は 算 術 演 算 子 を 演 算 して に 代 入
2015/9/7-8 11 データ 型 の 違 う 値 や 変 数 を 計 算 しようとするとどうなる? 可 能 であれば 拡 張 や 切 り 詰 めが 行 われて 計 算 が 実 行 される (-Wallオプションを 付 けてコンパイルすれば) コンパイラが 警 告 やエラーを 出 すので 気 がつける こともある int x; double d; d = 1.6; x = d; xには1が 入 る int x; double d; d = 1.6; x = 2 * d * d; xには2でも4でもなく5が 入 る ( 高 精 度 側 で 計 算 してから 切 り 詰 められる) (データ 型 ) 値 (データ 型 ) 変 数 によりデータ 型 の 変 換 が 可 能 int x; double d; d = 1.6; x = 2 * (int)d * (int)d; (int)1.6 = 1 であるため xには2が 入 る 計 算 の 優 先 順 位 ( 強 ) 単 項 >2 項 算 術 >2 項 比 較 >2 項 代 入 >2 項 論 理 ( 弱 ) ( 強 ) 乗 算 > 除 算 > 加 算 > 減 算 ( 弱 ) 不 安 があるときや 読 みやすくしたいときは ( ) で 括 ったり 型 を 明 示 したりすると 良 い x = ( a + b ) * ( c + (( d + e ) * f ))
2015/9/7-8 12 関 数 の 構 造 書 き 方 引 数 は 複 数 設 定 可 能 返 値 は1つだけ 複 数 の 値 を 返 したい 場 合 は 参 照 渡 し を 使 う 返 値 が 不 要 な 場 合 はvoid 型 ただし 返 した 値 は 使 われないどころか 受 け 取 られなくても 構 わないため 適 当 な 型 にして 適 当 な 値 を 返 す ことが 多 い とりあえずint 型 にしておこう 引 数 変 数 に 何 かを 代 入 しても 呼 び 出 し 元 に 影 響 は 無 い( 参 照 渡 し では 影 響 する) 関 数 の 実 行 方 法 関 数 名 ( 引 数 ); returnの 結 果 を 受 け 取 る 場 合 は 受 け 取 り 先 = 関 数 名 ( 引 数 ); 関 数 の 宣 言 (プロトタイプ 宣 言 ) 返 値 の 型 関 数 名 ( 引 数 の 設 定 ){ 関 数 の 中 身 return 返 値 ; 型 情 報 などがおかしくても 動 いてしま うことは 多 い( 注 意 が 必 要 ) 特 にmain 関 数 は 引 数 無 しやvoid 型 返 値 無 しでも 良 い コンパイラが 警 告 を 出 す 可 能 性 は 高 い int hoge(){return 1;に 対 して x = hoge();でもよいしhoge();でもよい 実 際 に 使 う 場 所 より 前 に 関 数 の 形 状 ( 名 前 引 数 型 返 値 型 )を 書 いておく 読 みやすくするためでもあるが 複 数 ソースコードを 用 いる 際 に 重 要 ( 後 述 ) もちろん 宣 言 ではなく 実 体 をその 場 に 書 いてしまっても 良 い
2015/9/7-8 13 変 数 を 使 うためには 使 うより 前 に 宣 言 が 行 われていなければならない 宣 言 がなければ 変 数 の 存 在 を 認 識 できない 関 数 内 で 宣 言 した( 使 い 始 めた) 変 数 は その 関 数 の 中 でしか 使 えない 関 数 の 途 中 で 宣 言 した 場 合 は 宣 言 の 後 でのみ 使 える 参 考 : 古 いC 言 語 では 関 数 やブロックの 最 初 でしか 変 数 を 宣 言 できなかった { と で 括 ると 変 数 の 存 在 範 囲 を 制 限 できる 括 弧 内 で 宣 言 した 変 数 は 括 弧 内 でしか 使 えない 一 番 外 側 (どの 関 数 の 中 でもない)の 変 数 は グローバル 変 数 どこからでも 読 み 書 きできるため 便 利 だがバグの 温 床 注 意 が 必 要 グローバル 変 数 ローカル 変 数 同 じスコープで 同 じ 名 前 の 変 数 を 宣 言 することはできない 型 が 違 ってもダメ スコープが 異 なる 場 合 は 狭 い 範 囲 が 優 先 される( 外 と 内 では 別 の 変 数 が 存 在 す るかのように 扱 われる)
2015/9/7-8 14 定 数 = 値 が 変 わらない( 変 えられない) 変 数 宣 言 方 法 :constというキーワードを 付 ける 例 const int n = 10; 書 き 換 えられないので 宣 言 と 同 時 に 代 入 する 書 き 換 えられないことに 何 の 意 味 があるのか? プログラム 中 に 出 てくる 値 をわかりやすい 文 字 列 で 表 現 できる( 意 味 がわかりやすくなる) 何 度 も 使 う 値 を 一 気 に 変 更 できる プログラムの 先 頭 にまとめて 書 いておくことが 多 い 関 数 の 引 数 に 使 うことで この 関 数 内 ではこの 引 数 と して 与 えられたものを 書 き 換 えませんよ と 宣 言 でき る( 値 渡 しと 参 照 渡 し)
2015/9/7-8 15 変 数 名 関 数 名 定 数 の 名 前 に 関 する 制 限 先 頭 文 字 はアルファベットか 一 部 の 記 号 短 くても 長 くても 良 いが 使 いやすい わかりやす い 程 度 にするべき 参 考 :ハンガリアン 記 法 ( 従 っても 従 わなくても 良 い) 文 字 と 文 字 列 の 違 い( 詳 細 は 後 述 ) 文 字 a シングルクオーテーションで 括 る 文 字 列 a abc ダブルクオーテーションで 括 る 文 字 はchar(-128から127の 数 値 と 等 価 ) 文 字 列 はcharの 配 列
2015/9/7-8 16 #include <stdio.h> #include <stdlib.h> // 一 行 コメント: 関 数 のプロトタイプ int getnum(int n); int main(int argc, char**argv){ int x=1, y; y = getnum(x); printf( Hello, world! %d n, y); return EXIT_SUCCESS; /* コメント( 複 数 行 いれたい 場 合 ) 引 数 を2 倍 にするだけの 関 数 */ int getnum(int n){ return n * 2; int 型 変 数 を 返 すgetnum 関 数 が 存 在 する ことを 宣 言 プログラムの 実 行 はmain 関 数 から int 型 の 変 数 xとyを 用 意 xは 初 期 値 1 getnum 関 数 に 変 数 xを 渡 して 処 理 させる getnumの 結 果 を 変 数 yに 格 納 printfに 文 字 列 と 変 数 yを 渡 して 表 示 %dはint 型 変 数 を 表 示 する 記 法 ( 後 述 ) EXIT_SUCCESSを 返 して 終 了 getnumはint 型 引 数 を 受 け 取 りint 型 の 値 を 返 す 関 数 受 け 取 った 変 数 nを2 倍 にして 返 す コメントには//か/**/を 使 う
2015/9/7-8 17 C 言 語 プログラムの 基 本 的 な 構 造 がわかった 変 数 値 を 色 々と 書 き 換 えて 表 示 するプログラ ムが 作 れるようになった #include <stdio.h> #include <stdlib.h> int getnum(int); int main(int c, char**v){ int x=1,y=1,z=1; printf( %d n, x=getnum(x)); printf( %d n, z=getnum(y=getnum(y))); printf( %d %d %d n, x, y, z); return EXIT_SUCCESS; int getnum(int n){return n * 2; %dの 数 と 変 数 の 数 は 同 じにすること 実 は 宣 言 時 に 引 数 の 名 前 は 不 要 mainの 引 数 の 名 前 を 変 え ても 良 い 関 数 呼 び 出 し 内 に 関 数 呼 び 出 しを 入 れても 良 いし そ の 際 に 代 入 を 行 っても 良 い ( 自 分 が 混 乱 しないように 注 意 ) 出 力 結 果 2 4 2 2 4
2015/9/7-8 18 条 件 分 岐 処 理 if ( 条 件 式 ){ 真 手 続 き else { 偽 手 続 き else 以 降 は 省 略 可 能 繰 り 返 し 処 理 (ループ) while ( 条 件 式 ) { 繰 り 返 し 実 行 させたい 対 象 手 続 き 条 件 式 を 評 価 し 真 の 間 ループを 続 ける do { 対 象 手 続 き while ( 条 件 式 ) ループを 行 ってから 条 件 式 を 評 価 真 の 間 ループを 続 ける for ( 初 期 化 ; 条 件 式 ; 再 初 期 化 ) { 対 象 手 続 き 値 を 変 えながらループをする for (i = 0; i < 10; i++){ print( %d n, i); 1. ループに 入 る 際 に 初 期 化 部 分 を 実 行 2. 条 件 式 を 評 価 し 真 ならループを 行 う 3. ループ 後 再 初 期 化 を 行 い2に 戻 る 条 件 式 には 真 偽 を 返 す 式 が 入 る 条 件 式 が 満 たされ 続 けると 無 限 ループになるため 注 意 特 にwhileとdoは 終 了 条 件 が 成 立 するように 対 象 手 続 きを 書 く 必 要 がある( 条 件 式 の 中 に 書 くという 方 法 もあるが)
2015/9/7-8 19 比 較 演 算 子 と 論 理 演 算 子 条 件 式 によく 利 用 される 演 算 子 種 別 例 備 考 < 小 なり <= 以 下 > 大 なり >= 以 上 == 等 しい 代 入 との 間 違 いに 注 意!= 異 なる! && AND && ((x!=1) && (y==1)) 等 とつかう OR ((x!=1) (y==1)) 等 とつかう! NOT! 単 項 演 算 子!((x!=1) && (y==1)) & AND & ビット 演 算 のAND OR ビット 演 算 のOR 比 較 演 算 子 は 偽 の 場 合 整 数 型 0 真 の 場 合 はそれ 以 外 の 整 数 が 入 る ( 一 般 的 には1が 入 る)
2015/9/7-8 20 インクリメント デクリメント 変 数 の 値 を+1, -1する( 変 数 の 値 が 更 新 される) ++A Aに1を 加 える 加 えた 後 のAを 返 す A++ Aに1を 加 える 加 える 前 のAを 返 す forループの 制 御 によく 用 いられる 複 合 代 入 演 算 子 ( 演 算 子 = ) 元 となる 変 数 に 対 して 演 算 を 行 いながら 代 入 する 元 の 変 数 に 対 して 参 照 と 更 新 が 行 われる 例 x += 1, x -= 1 x *= 2, x /= 2 x &= 1, x = 1, x ^= 1
2015/9/7-8 21 長 さを 持 つ 変 数 を 使 うことができる 関 連 した 値 をひとまとめにして 扱 うときなどに 使 う 数 値 計 算 プログラミングにおいてはベクトルの 表 現 として 一 般 的 定 義 方 法 右 は 全 て int 型 長 さ3 の 配 列 の 例 アクセス 方 法 int a[3]; int b[3] = {1, 2, 3; int c[] = {1, 2, 3; int d[3] = {1, 2; int e[3] = {; 変 数 名 [ 添 え 字 番 号 ] 添 え 字 番 号 (インデックス)は 0 から 配 列 長 -1 まで 注 意 : 配 列 のサイズを 超 えてもエラーが 起 きずに 実 行 されることがある 予 期 せぬデータ 改 変 などが 起 こるため 注 意 すること 配 列 そのものへの 代 入 はできない 初 期 値 は 不 定 初 期 値 は 順 に1, 2, 3 初 期 値 は 順 に1, 2, 3 長 さ 指 定 が 無 い 例 初 期 値 は 順 に1, 2, 0 個 数 が 足 りないと 0 初 期 値 は 順 に0, 0, 0 {だけの 場 合 は 全 部 0 double 型 などでも 同 じような 挙 動 となる int a[3]; a = 数 値 or 変 数 ; といった 記 述 はNG( 例 外 あり)
2015/9/7-8 22 #include <stdio.h> #include <stdlib.h> int main(int argc, char **argv) { int a[3]; int b[3] = {1, 2, 3; int c[] = {1, 2, 3; int d[3] = {1, 2; int i; for (i = 0; i < 3; i++) { printf("a[%d]=%d, ", i, a[i]); printf("b[%d]=%d, ", i, b[i]); printf("c[%d]=%d, ", i, c[i]); printf("d[%d]=%d n", i, d[i]); printf("a[%d]=%d n", 3, a[3]); return EXIT_SUCCESS; 出 力 結 果 a[0]=1627408016, b[0]=1, c[0]=1, d[0]=1 a[1]=-1, b[1]=2, c[1]=2, d[1]=2 a[2]=1, b[2]=3, c[2]=3, d[2]=0 a[3]=4198562 初 期 化 されていないため 何 が 表 示 されるか 不 定 範 囲 外 を 参 照 してしまっているた め 何 が 表 示 されるかわからない エラー 終 了 してしまうこともある Segmentation Faultなどの 出 力 を 伴 うことがある for (i = 0; i < 配 列 の 要 素 数 ; i++) というループは 頻 出 パターン
2015/9/7-8 23 行 列 はどのように 表 現 すれば 良 いだろうか? 長 い 配 列 を 用 意 して 使 う 3 行 4 列 の 行 列 であれば12 要 素 あればよい アクセスする 際 にはその 都 度 位 置 を 算 出 二 次 元 の 配 列 を 使 う 3 行 4 列 の 二 次 元 配 列 matrix[3][4] matrix[12] 正 直 どちらを 使 っても 構 わない 使 い 方 が 悪 いと 性 能 が 低 下 するが 正 しく 使 う 分 には 問 題 ない コンパイラによる 最 適 化 に 影 響 することがある これ 以 降 は 二 次 元 配 列 にて 表 現 することにする
2015/9/7-8 24 表 現 方 法 X[a][b] 長 さa*bの 二 次 元 配 列 Y[a][b][c] 長 さa*b*cの 三 次 元 配 列 Z[a][b][c][d] 長 さa*b*c*dの 四 次 元 配 列 二 次 元 配 列 と 行 列 の 対 応 付 けイメージ matrix[3][4] 長 さ4のベクトルが 3 本 束 ねられている ようなイメージ matrix[0][0] matrix[0][3] matrix[2][0] matrix[2][3]
2015/9/7-8 25 配 列 の 宣 言 int a[3][4]; int b[3][4] = {{1, 2, 3, 4, {5, 6, 7, 8, {9, 10, 11, 12; int c[2][2][2] = {{{ 1, 2,{ 3, 4, {{ 5, 6,{ 7, 8; int d[][2] = {{1,2,{3,4; 配 列 へのアクセス int a[3][4], b[3][4]; for(i=0; i<3; i++){ for(j=0; j<4; j++){ a[i][j] = i*4+j; b[i][j] = a[i][j] * 2; 改 行 は 見 やすさの 都 合 によるもの 3x4 行 列 の 作 成 作 成 と 同 時 に 定 数 にて 初 期 化 b[0][0]=1, b[0][1]=2, 作 成 と 同 時 に 初 期 化 括 弧 の 位 置 と 数 に 注 意 左 端 の 次 元 指 定 だけは 省 略 可 能 配 列 の 値 をまとめて 変 更 するよう な 機 能 はないため ループを 用 い て 処 理 をするのが 一 般 的 memset 関 数 などが 役 に 立 つが 今 回 は 扱 わない iループとjループは 逆 にしても 動 く が 性 能 が 落 ちやすい( 後 半 最 適 化 の 中 で 触 れる)
2015/9/7-8 26 いきなり 計 算 機 (コンピュータアーキテクチャ)の 話 になってしまうが ( 基 本 的 に)データはメモリ 上 に 配 置 される メモリには 番 地 (アドレス)が 振 られている 番 地 がないと 訪 ねられない & 記 号 を 使 うとアドレスを 参 照 できる 0001 0002 0003 0004 番 地 (アドレス) 1byte(8bit) 分 のメモリ * 記 号 を 使 うとそのアドレスにあるデータ( 値 )を 参 照 できる 変 数 とは あるアドレスを 先 頭 に 必 要 バイト 数 分 を 占 有 するもの 配 列 とは 連 続 するアドレスを 先 頭 に 必 要 バイト 数 分 * 要 素 数 分 だ け 占 有 するもの
2015/9/7-8 27 int a = 1; int *b = NULL; b = &a; printf( a = %d, *b = %d n, a, *b); a = 2; printf( a = %d, *b = %d n, a, *b); *b = 3; printf( a = %d, *b = %d n, a, *b); printf( &a = %x, b = %x n, &a, b); 実 行 結 果 NULLは 何 も 無 い 空 っぽの 意 味 ( 基 本 的 に)0と 等 価 ポインタ の 初 期 化 時 によく 利 用 する a = 1, *b = 1 a = 2, *b = 2 a = 3, *b = 3 &a = 24cb04, b = 24cb04 b = &a bはaと 同 じ 番 地 を 指 し 示 す 状 態 となる a = 2 aを 変 更 したため 同 じ 番 地 のデータを 示 す *b も 変 更 された *b = 3 *b にあるデータはaのこと そのためaもbも 変 更 された 最 後 はaのアドレスとbを 表 示 %xによってアドレスを16 進 数 で 表 示 させると 同 じであることがわかる ( 具 体 的 な 値 は 環 境 によって 異 なるが とにかく&aと bは 同 じ 値 である コンパイラに 型 が 合 わない 旨 の 警 告 を 出 されると 思 うが 今 回 はわかっていてやっているため 気 にしなくても 良 い )
2015/9/7-8 28 void func1(int a){ a = a + 1; void func2(int *a){ *a = *a + 1; int n = 1; func1(n); printf( a = %d n, n); func2(&n); printf( a = %d n, n); 実 行 結 果 a = 1 a = 2 引 数 が 通 常 の 単 体 変 数 の 場 合 関 数 内 で 引 数 を 書 き 換 えても 呼 び 出 し 元 に 影 響 がない: 値 渡 し 変 数 の 値 のコピーが 渡 されると 思 えば 良 い コピーが 変 更 されてもオリジナルに 影 響 は 生 じない 引 数 がポインタの 場 合 関 数 内 で 引 数 を 書 き 換 えると 呼 び 出 し 元 に 影 響 が 生 じる: 参 照 渡 し 変 数 のアドレスが 渡 されると 思 えば 良 い アドレスを 辿 ればオリジナルにも 影 響 が 生 じる 引 数 にconstをつけると 書 き 換 えられない 引 数 となる
2015/9/7-8 29 参 照 渡 しがわかると SWAP 関 数 を 作 ることが できる void swap(?,?){ int a = 1; int b = 2; printf( a = %d, b = %d n, a, b); swap(?,?); printf( a = %d, b = %d n, a, b); どうすれば 良 いのか 考 えてみよう ヒント: 値 を 変 えたいのだから 参 照 渡 しは 必 須 直 接 交 換 できないのなら 一 時 的 に 別 の 変 数 に
2015/9/7-8 30 入 力 これまで 計 算 用 のデータをソースコードに 直 書 きしてきたが 実 際 の 問 題 ( 例 えば 大 きな 行 列 の 入 力 )で 行 うのは 無 理 がある 出 力 多 くの 出 力 を 行 うと 画 面 が 溢 れる 時 間 もかかる 入 力 を 受 け 取 ったり ファイルを 読 み 書 きしたりするには どうすれば 良 いだろうか? 実 行 時 引 数 の 活 用 文 字 と 文 字 列 の 扱 い 方 実 行 時 に 長 さの 決 まる 配 列 を 作 る 方 法 もう 少 し 詳 しいprintfの 使 い 方 キーボード 入 力 の 受 け 取 り 方 ファイル 入 出 力 の 方 法
2015/9/7-8 31 作 成 したプログラムを 実 行 する 際 に 与 える 引 数 をプログラ ム 中 で 使 うことができる./a.out 1024 実 行 時 引 数 はmain 関 数 の 引 数 として 与 えられる int main(int argc, char **argv) argc 引 数 の 数 argv 引 数 として 与 えられたもの 一 覧 注 意 : 第 一 引 数 (argv[0])には 必 ずプログラム 名 (./a.outなど)が 入 る 例 :./a.out size 1024 argc = 3 argv[0] =./a.out argv[1] = size argv[2] = 1024 argvは 全 て 文 字 列 として 与 えられる ため そのままでは 問 題 サイズなど( 数 値 )には 使 えない
2015/9/7-8 32 C 言 語 における 文 字 と 文 字 列 の 表 現 方 法 文 字 一 文 字 だけを (シングルクオーテーション)で 括 って 使 う char 型 つまり-128~127の 整 数 値 と 等 価 ( printfで 試 そう) 文 字 列 0 文 字 以 上 を (ダブルクオーテーション)で 括 って 使 う char 型 の 配 列 char[n] 実 は 末 尾 に 0 という 特 殊 な 値 が 設 定 されている( 終 端 文 字 )ため 配 列 の 長 さとしては 見 た 目 の 文 字 列 より1つ 長 くなる char str[] = abc ; とすると strの 長 さは4 str[]={ a, b, c, 0 と 等 価 charの 配 列 なので 配 列 自 体 の 代 入 はできない» char str[] = abc ; に 対 して str = xyz ; はNG» 文 字 列 操 作 用 の 関 数 群 を 使 う argvがchar**なのは 文 字 列 の 配 列 であるため
2015/9/7-8 33 文 字 列 に 対 して 操 作 をするための 関 数 が 数 多 く 用 意 さ れている(ここでは 紹 介 だけ) コピー strcpy, strncpy など 検 索 strstr, strchr など 出 力 putc, puts, putchar など 取 得 scanf など 引 数 として 取 得 した 文 字 列 を 数 字 に 変 換 するにはatoi が 便 利 int atoi(const char *) char 型 の 配 列 を 渡 すとint 型 の 数 値 を 返 してくれる argvから 数 字 を 得 る 際 に 有 用 constが 付 いているがただのchar*を 渡 してOK
2015/9/7-8 34 問 題 : 引 数 に 与 えられた 長 さのベクトルを 用 意 して 各 要 素 の 値 を2 倍 にするプログラムを 作 成 せよ ただ し ベクトルの 各 要 素 の 初 期 値 は 配 列 のインデック ス 値 とする 実 行 時 に 長 さの 決 まる 配 列 はどうやって 作 れば 良 いのだろうか? int main(int argc, char **argv) { int length; int vector[?]; length = atoi(argv[1]); 実 行 しないと 長 さがわからない? 長 さがわからない 配 列?? 十 分 に 長 い 配 列 を 用 意 しておくという 別 解 もあるにはある ( 十 分 に 長 いとはいったいいくつなのか? あまりにも 長 すぎるとメモリ が 不 足 して 実 行 できない コード 書 き 換 え 再 コンパイル )
2015/9/7-8 35 動 的 な( 実 行 時 の) 配 列 の 作 成 にはmalloc 関 数 を 使 う target = (データ 型 *)malloc( 作 成 サイズ); 例 int *vector; または int vector[]; を 宣 言 しておく vector = (int*)malloc(sizeof(int)*10); sizeofは 対 象 の 長 さ(バイト 数 )を 返 す 関 数 引 数 はデータ 型 使 いたいデータ 型 を 指 定 する 作 成 した 時 点 では 中 身 が 不 定 なので 注 意 が 必 要 ( calloc) 利 用 可 能 なメモリ 容 量 を 使 い 尽 くすほど 確 保 しようとするとエラーする 不 要 になったらfree 関 数 で 破 棄 する free(vector); 判 定 のためについでにvector=NULL;することが 多 い 破 棄 せずにプログラムを 終 えても 構 わない 多 重 malloc/freeや 不 要 なfree/mallocの 繰 り 返 しを 行 わないように 注 意 ループ 処 理 に 注 意 : 多 重 はエラー 終 了 する 繰 り 返 しは 実 行 時 間 が 延 びる 引 数 として 受 け 取 った 数 値 分 の 配 列 を 作 るには? int 型 10 個 分 の 場 所 を 確 保 し そのアドレスを 受 け 取 った と 思 えば 良 い N=atoi(argv[1])を 使 ってmalloc(sizeof(データ 型 )*N)を 実 行 すれば 良 い
2015/9/7-8 36 行 列 ( 二 次 元 配 列 )の 動 的 確 保 1. 二 段 階 malloc 行 数 分 だけmallocしたうえで 各 要 素 から 列 数 分 だけmallocしなおす 2. 大 きな 領 域 を 確 保 して 使 う 全 要 素 分 のメモリを 確 保 して 別 途 列 の 先 頭 を 指 し 示 すポインタを 持 つ 赤 はmalloc 青 は 参 照 を 意 味 する
2015/9/7-8 37 row 行 col 列 の 行 列 を 動 的 に 作 成 する 例 int **mat; mat = (int**)malloc(sizeof(int*)*row); for(i=0; i<row; i++){ mat[i] = (int*)malloc(sizeof(int)*col); // 行 列 に 対 する 計 算 はここで 行 う for(i=0; i<row; i++){ free(mat[i]); free(mat); int **mat; mat = (int**)malloc(sizeof(int*)*row); mat[0] = (int*)malloc(sizeof(int)*row*col); for(i=1; i<row; i++){ mat[i] = mat[i-1]+sizeof(int)*col; // 行 列 に 対 する 計 算 はここで 行 う free(mat[0]); free(mat); 行 数 分 の 列 ポインタを 作 成 し 各 行 のための 領 域 を 個 別 に 確 保 行 をまたいだメモリ(アドレス) が 連 続 にならない( 性 能 が 下 がる 原 因 となる) 可 能 性 が0ではない 行 数 分 の 列 ポインタを 作 成 するのは 一 緒 だが 全 要 素 分 のメモリを 一 度 に 確 保 し 各 行 の 先 頭 位 置 をあわせる mallocが 減 った 分 freeも 減 る
2015/9/7-8 38 配 列 を 受 け 取 る 関 数 の 作 り 方 引 数 に[]や*を 使 えば 良 い sizeof 関 数 で 配 列 長 を 取 得 できないためlenで 長 さを 渡 すという 想 定 もちろん 関 数 内 で 値 を 更 新 すれば 呼 び 出 し 元 の 値 も 変 化 する 実 は int vector[n]に 対 して &vector[0]とvectorは 等 価 である void funca(int len, int v[]){ int i; for(i=0; i<len; i++){ printf( %d, v[i]); printf( n ); void funcb(int len, int *v){ int i; for(i=0; i<len; i++){ printf( %d, v[i]); printf( n );
2015/9/7-8 39 mallocして 返 すためにはポインタを 一 段 階 深 くせねばならない(と 覚 えておこう) 二 次 元 配 列 の 場 合 もやはりもう 一 段 階 深 くなる void my_alloc(int N, int **v){ *v = (int*)malloc(sizeof(int)*n); void my_free(int **v){ free(*v); int *vector = NULL; my_alloc(10, &vector); // vectorを 用 いた 操 作 for(i=0; i<10; i++)vector[i] = i; for(i=0; i<10; i++)printf(" %d", vector[i]); printf(" n"); my_free(&vector);
2015/9/7-8 40 printfとは: 指 定 されたフォーマット( 書 式 )に 従 って 文 字 列 を 出 力 する 関 数 printf( フォーマット 指 定 文 字 列, 変 数,, 変 数 ); printf( 今 日 は%d 日 です n, day); フォーマット 指 定 文 字 列 の 使 い 方 と 例 %<フラグ>< 最 小 フィールド 幅 >< 精 度 >< 長 さ 修 飾 子 >< 変 換 指 定 文 字 > フラグ: 左 詰 め(-) 正 符 号 付 け(+) ゼロで 埋 める(0) など 最 小 フィールド 幅 : 数 値 出 力 幅 精 度 : 小 数 点 以 下 の 桁 数 長 さ 修 飾 子 :( 省 略 ) 変 換 指 定 文 字 :( 次 ページ) %2d 整 数 2 文 字 分 以 上 の 幅 で 出 力 %-2d 左 詰 めされる %02d 1 桁 の 場 合 は0を 付 加 %+d 正 の 値 には+が 付 く %2.1f 整 数 部 分 2 文 字 + 小 数 1 桁 を 表 示 %% %そのものを 表 示
2015/9/7-8 41 変 換 指 定 文 字 は 表 示 したい 変 数 の 型 と 合 わせる 必 要 がある 合 わない 場 合 は 適 当 に 変 換 される( 予 期 せぬ 出 力 になるこ ともあるので 注 意 ) 指 定 文 字 意 味 対 象 変 数 のデータ 型 %c 1 文 字 として 出 力 する 文 字 型 %d 10 進 数 で 出 力 する %x 16 進 数 で 出 力 する 整 数 型 %o 8 進 数 で 出 力 する %f [-]dddd.ddddddの 形 式 で 出 力 する %e 指 数 形 式 で 出 力 する 浮 動 小 数 点 型 %g %fと%eから 自 動 選 択 される %s 文 字 列 として 出 力 する 文 字 列
2015/9/7-8 42 エスケープシーケンス 特 殊 な 意 味 を 持 つ 文 字 列 いずれも 円 記 号 (バック スラッシュ)で 始 まる エスケープシーケンス 意 味 n 改 行 a 警 告 音 ( 音 が 出 る) t タブ b バックスペース 0 文 字 列 の 終 端
2015/9/7-8 43 printfの 亜 種 について printfは 書 式 に 従 い 画 面 ( 標 準 出 力 )に 結 果 を 表 示 するもの sprintfを 使 うと 結 果 を 文 字 列 に 格 納 できる 使 い 方 sprint( 格 納 先 char 配 列, フォーマット 指 定 文 字 列, 変 数,, 変 数 ); 例 char str[0xff]; int i=1, j=2; sprint(str, i = %d, j = %d n, i, j); printf(str); 実 行 結 果 :i = 1, j = 2 0xffは16 進 表 記 で255のこと 格 納 するのに 十 分 な 長 さがあれば 良 い snprintf 関 数 を 使 うと 長 さの 制 限 ができて 安 全 実 はprintfにはchar 配 列 をそのまま 渡 しても 良 い
2015/9/7-8 44 sscanf 関 数 :printfと 同 様 のフォーマット 文 字 列 を 用 いて 逆 に 文 字 列 か ら 変 数 への 格 納 を 行 うことができる sscanf( 元 となる 文 字 列, フォーマット 指 定 文 字 列, 代 入 先 の 位 置, ); sscanf(buf, 今 日 は%d 日 です, &day); char dofweek[3]; scanf(buf, 今 日 の 曜 日 は%3sです, dofweek); フォーマット 指 定 文 字 列 の 例 格 納 先 のアドレス(ポインタ)が 必 要 大 雑 把 には 変 数 に 格 納 する 場 合 は&をつける 配 列 に 格 納 する 場 合 は&をつけない %<フィールド 幅 >< 変 換 指 定 文 字 > フィールド 幅 は 変 換 する 文 字 数 か 文 字 列 の 短 い 方 sscanf( 1234, %2d, &a); なら a=12になる sscanf( 1234, %8d, &a); なら a=1234になる 文 字 列 を 受 けるときは 代 入 する 変 数 のサイズ-1を 超 えないよう 必 ず 指 定 する こと( 終 端 文 字 0が 最 後 の 1つを 埋 めるため ) floatは%fだがdoubleは%lfとなることに 注 意 (printfでは 両 方 %fで 良 い)
2015/9/7-8 45 sscanf 関 数 ではなくscanf 関 数 を 使 うと 変 数 ではなくキー ボード 入 力 ( 標 準 入 力 )から 入 力 を 受 け 取 ることができる scanf( フォーマット 指 定 文 字 列, 代 入 先, ); scanf( %10s, str); 文 字 列 (10 文 字 分 )の 取 得 scanf( %d, &value); 整 数 値 の 取 得 変 な 入 力 (フォーマットに 合 わない 文 字 列 や 長 い 文 字 列 な ど)をされてしまった 際 の 処 理 が 難 しいなどの 問 題 がある ため 積 極 的 に 使 う 必 要 は 無 い 実 行 時 引 数 やファイルの 入 出 力 で 十 分 である
2015/9/7-8 46 基 本 手 順 : 開 いて 読 み 書 きして 閉 じる 開 く:fopen(ファイル 名, モード) モード r: 読 み 専 用 w: 書 き 専 用 ( 上 書 き) a: 追 記 r+: 読 み 書 き w+: 書 き 読 み( 上 書 き) a+: 読 み 追 記 上 書 きの 場 合 開 いた 瞬 間 に ファイルの 内 容 が 空 っぽになる w, a, w+, a+ ではファイルが 存 在 し ない 場 合 には 自 動 的 に 作 成 される 失 敗 するとNULLが 返 る 閉 じる:fclose(ファイルポインタ) #include <stdio.h> #include <stdlib.h> int main(int argc, char**argv){ FILE *fp;//ファイルポインタ fp = fopen( filename, r ); if(fp == NULL){ perror( fopen: ); exit(exit_failure); /* ここに 読 み 書 き 処 理 */ fclose(fp); return EXIT_SUCCESS; perrorはエラーメッセージの 出 力 exitは( 関 数 終 了 ではなく) プログラム 終 了 処 理
2015/9/7-8 47 fgets:テキストファイルを 行 単 位 で 読 む 第 一 引 数 : 読 み 込 んだ 内 容 を 格 納 するchar 配 列 第 二 引 数 : 読 み 込 み 先 のサイズ サイズ-1か 改 行 があるまで 読 み 込 む 読 めなかった 分 は 次 回 読 む -1は 0を 自 動 付 加 するため 改 行 も 読 むので 実 質 行 のサイズより2つは 大 きくとる 第 三 引 数 :fopenから 得 られたファイルポインタ ファイルを 全 部 読 み 終 わるかエラーするとNULLが 返 る fscanf:scanf/sscanfと 同 様 だが 入 力 元 がファイルポインタ 返 り 値 は 読 み 込 み 個 数 終 端 orエラー 時 にはEOFが 返 る 頻 出 事 例 :fscanfでファイルから 要 素 毎 に 変 数 へ 代 入 また はfgetsで 一 行 読 んでsscanfにより 変 数 へ 代 入 char s[16]; while(fgets(s, 16, fp)!= NULL){ sscanf(s, format, &value);
2015/9/7-8 48 fprintf:テキストファイルをフォーマット 指 定 文 字 列 に 従 って 書 き 込 む 第 一 引 数 :fopenから 得 られたファイルポインタ 第 二 引 数 以 降 :printfと 同 じ フォーマット 指 定 文 字 列 不 要 ( 変 数 値 なし テキスト のみ)ならfputsでも 十 分 fprintf(fp, format, value, ); fputs( 文 字 列, fp); その 他 の 入 出 力 関 数 の 例 :fread, fwriteなど
2015/9/7-8 49 プログラム 例 (エラー 処 理 をしていない 点 に 注 意 ) #include <stdio.h> #include <stdlib.h> int main(int argc, char **argv){ int **mat = NULL; int i, j, row, col; char tmp[0xff]; FILE *fp; fp = fopen("data.dat", "r"); fgets(tmp, 0xff, fp); sscanf(tmp, "%d %d", &row, &col); printf("%d rows, %d cols n", row, col); mat = (int**)malloc(sizeof(int*)*row); mat[0] = (int*)malloc(sizeof(int)*row*col); for(i=1; i<row; i++){ mat[i] = mat[i-1]+sizeof(int)*col; 対 象 ファイル data.dat の 中 身 一 行 目 : 行 数 列 数 二 行 目 から: 各 行 の 要 素 for(i=0; i<row; i++){ for(j=0; j<col; j++){ fscanf(fp, "%d", &mat[i][j]); fclose(fp); for(i=0; i<row; i++){ for(j=0; j<col; j++){ printf(" %d", mat[i][j]); printf(" n"); free(mat[0]); free(mat); return 0; 3 4 11 21 31 41 12 22 32 42 13 23 33 43