C⾔語 学習教材
筑波⼤学 システム情報系 三⾕純
最終更新⽇ 2023/4/19(C) 2023 Jun Mitani

図表出典：三⾕純著『C ⾔語 ゼロからはじめるプログラミング』（翔泳社 刊）

本資料の位置づけ
本資料は『 C⾔語 ゼロからはじめるプログラミング』
を専⾨学校・⼤学・企業などで教科書として採⽤された教員・指導員を対象に、教科書の内容を解説するための副教材として作られています。
上記に該当する場合は、⾃由にご使⽤ください。
授業の進め⽅などに応じて、改変していただいて結構です。※ このページを削除して構いません
ただし、⺠間企業が商⽤、ビジネス⽬的で利⽤する際には別途許諾が必要ですので、著者までご連絡ください。

出版社
発売⽇
ISBN

： 翔泳社
： 2022/9/20：9784798174655

2

はじめに

プログラミングを学ぶ意義（1/2）• ソフトウェアを使う⽴場から、作る⽴場へ– パソコン、スマートフォンなどで動作するアプリ– TV、エアコン、洗濯機などの電⼦機器の制御– 電⼦決済、電⼦申請、各種のシステム• 個⼈での簡単な開発– 電卓・Excelの⼀歩先– データ処理・データ解析– 個⼈⽤途のアプリ開発• スキルアップ– 情報処理関係の資格取得– プログラミングコンテスト

プログラミングを学ぶ意義（2/2）• 実際に開発する⽴場になる予定が無くても– アルゴリズム的思考（ものごとを処理する⼿順に関する合理的な考え⽅）が⾝につく– ソフトウェアの開発の様⼦、動作原理がわかることによる広い視野の獲得– 専⾨⽤語の理解、ITエンジニアとのコミュニケーション– 将来にプログラミングを独習したくなったときに役⽴つ（異なるプログラミング⾔語でも考え⽅の基本は同じ）

この講義で学ぶこと• プログラミング⾔語に拠らない、プログラミン
グ全般の基礎知識• プログラミングに関する基本的な⽤語と考え⽅• C⾔語というプログラミング⾔語活⽤の基礎

よりよく学ぶために• 実際に⼿を動かしてプログラムコードを⼊⼒する、⼀部を変更して実験する

実際に試してみよう• Webで公開されているプログラムコードを動かしてみる、⼀部を変更して実験する• プログラミングコンテストに参加してみる（モチベーションアップ、実⼒向上、他者のコードからの学び）
学習

実践

学習

実践

学習

実践

全体の流れ1. C⾔語に触れる
2. C⾔語の基本
3. 条件分岐と繰り返し
4. 関数
5. アドレスとポインタ
6. ⽂字列の扱いと構造体
7. ⼀歩進んだC⾔語プログラミング
8. データ構造とアルゴリズム

第1章 C⾔語に触れる

プログラムとは• コンピュータに命令を与えるものが「プログラム」• プログラムを作成するための専⽤⾔語が「プログラミング⾔語」• その中の1つに「C⾔語」がある

さまざまなプログラミング⾔語
• C
• C++
• C#
• Perl• PHP• Java

歴史のある⾔語、OS開発、組み込みプログラム•

ブラウザで動作、動的なWebページ

JavaScript• Python

C⾔語の後継、オブジェクト指向
C++⾔語の後継、⽶マイクソロソフト
スクリプト⾔語、⼿軽な開発
サーバサイド、Webページ⽣成
オブジェクト指向、⼤規模システム

修得が容易、機械学習分野で普及※ C⾔語の特徴
メモリーに直接アクセスできる。
うまく扱えば、効率的で⾼速の動作するプログラムを作成できる。
他の⾔語の基礎となる⾔語

プログラムコードが実⾏されるまで

C⾔語のプログラムコード
C⾔語の最も基本的なプログラムコードの形• 半⾓英数と記号で記述する• ⼈が読んで理解できるテキスト形式

C⾔語のプログラムコード「こんにちは」という⽂字列を出⼒するプログラムコード• 命令⽂の末尾にはセミコロン（;）をつける• 空⽩や改⾏は好きな場所に⼊れてかまわない• プログラムコードの中の⼤⽂字と⼩⽂字は区別される

ブロックとインデント

ブロック： { と } で囲まれた範囲
ブロックの中にブロックがあることも（ブロックのネスト）
インデント： プログラムコードを⾒やすくするために⼊れる⾏頭の空⽩ブロックの階層の深さにあわせてインデントの数を変える（最近のエディタは⾃動で調整してくれる）※ インデントは無くてもプログラムに影響しないコメント⽂

コメント⽂
・プログラムコードの中に記したメモ書き
・1 ⾏のコメント⽂には // を使⽤
・複数⾏のコメント⽂は /* と */ で囲む。
・コンパイラに無視される（プログラムの動作には影響しない）

プログラムの作成と実⾏

Visual Studio とは• Visual Studio は以下のものを含む統合開発環境の1つ– エディタ：プログラムコードを記述する– コンパイラ：コンパイルを⾏う（オブジェクトファ
イルを⽣成する）– リンカ：リンクを⾏う（実⾏ファイルを⽣成する）

Visual Studio のインストール（Windows）• Visual Studio の⼊⼿
https://visualstudio.microsoft.com/ja/downloads/※ MacOSでの開発環境の構築は付録を参照

Visual Studio のインストール（Windows）

Visual Studioの画⾯構成

コードエディター
ソリューション
エクスプローラー

プロパティ
出⼒・エラー⼀覧

プログラムを作成して実⾏する
1.プロジェクトの作成
2.プログラムコードの作成
3.プログラムの実⾏

1. プロジェクトの作成

2. プログラムコードの作成
1. [プロジェクト]メニューの[新しい項⽬の追加]を選択
2. ファイルの拡張⼦を .c にする

3. コードエディターにプログラムコードを⼊⼒
4．[ファイル］メニューの［すべて保存］を選択してプログラムコー
ドを保存

3. プログラムの実⾏(1/2)
1. [プロジェクト]メニューの[（プロジェクト名）のプロパティ]を選択
2. [構成プロパティ]→[リンカー]→[システム]を選択
3. 右側の[サブシステム]に[コンソール（/SUBSYSTEM:CONSOLE）]を選択※ プログラム実行直後にコンソールウィンドウが閉じてしまう場合には、この設定を確認

3. プログラムの実⾏(2/2)[デバッグ]メニュー→[デバッグなしで開始]を選択※ 練習⽤であれば、毎回新しいプロジェクトファイルを作る必要は無い。エディターの中でプログラムコードを更新して、その都度、実⾏結果を確認する

プログラムコードの間違い
Error（エラー）の種類•

コンパイルエラー（Compile Error）– キーワードのつづりミス– ⽂法上の間違い•

ランタイムエラー または 実⾏時エラー （Runtime Error）– コンパイル時には発⾒されず、プログラムを実⾏している最中に⾒つかる問題

Visual Studioで最初のプログラムを作ってみよう

第2章 C⾔語の基本

出⼒

画⾯へ⽂字列を出⼒する
⽂字列を出⼒する printf 関数
printf(出力する内容);
・⽂字列をダブルクォーテーション( " )で囲む
・「\n」で改⾏

数値の埋め込み
⽂字列の中に「%d」と記述した場所が数字に置き換わる

数値の埋め込み#include <stdio.h>
int main(void){
printf("今年は西暦 %d 年です¥n", 2022);
printf("フルマラソンのコースは %f キロ¥n", 42.195);
printf("%d歳の男の子の平均身長は %f cm¥n", 10, 138.9);}「変換指定」%d : 整数の埋め込み%f : ⼩数点を含む数の埋め込み

表⽰する桁数の指定%.2f ：⼩数点以下2桁を表⽰%.5f ：⼩数点以下5桁を表⽰

変数
変数：値を⼊れておく⼊れ物のこと#include <stdio.h>
変数の宣言

int main(void){
int i;
i = 5;
printf("%d", i);
}

値の代入

値の参照

変数の宣⾔型名 変数名;
int i;
整数を表す型名

変数名• 英字、数字、アンダースコア（ _ ）が使える• 先頭の⽂字が数字であってはいけない• ⼤⽂字と⼩⽂字が区別される• C⾔語で⽤途が決まっている単語を変数名にはできない

値の代⼊
変数名 = 値;
i = 5;
整数名

値

左辺の変数に右辺の値を⼊れる操作

複数回実⾏した場合は、後から代⼊した値に上書きされる

i = 5;
i = 10;
printf("%d", i);
実行結果

10

値の参照

変数の初期化
初期化：変数に最初の値を⼊れること
int i;
i = 5;
1⾏にまとめられる

int i = 5;

変数の型
よく使⽤する型
・整数： int型
・⼩数点を含む数： double型
・⽂字：char型※ char型は a〜z,A〜Z,0〜9, #!?>
といった半⾓英数字記号1⽂字※ 実⾏環境によって異なる場合があるさまざまな型の変数#include <stdio.h>
int main(void){
int i = 1;
double d = 0.2;
char c = 'A';

「変換指定」%d : 整数の埋め込み%f : ⼩数点を含む数の埋め込み%c ：⽂字の埋め込み※ char型の値は、⽂字をシン
グルクォーテーションで囲む

printf("iの値は %d¥n", i);printf("dの値は %f¥n", d);printf("cの値は %c¥n", c);}
実行結果iの値は 1dの値は 0.200000
cの値は A

実際に試してみよう算術演算⼦と式

計算を⾏う用語

算術演算⼦
算術演算子

変数を含む算術演算

実行結果

jの値は 20

いろいろな計算をしてみよう

変数の値を変更する
例：変数iの値を 3 増やすi = i + 3;
※ 「i の値に3を加えた」値を、変数iに代⼊する
i ← i + 3 のイメージ
短縮表現

i += 3;

さまざまな短縮表現
演算⼦

演算の内容

使⽤例

+=

加算代⼊

a += 2;（a = a + 2; と同じ）

-=

減算代⼊

a -= 2;（a = a - 2; と同じ）

*=

乗算代⼊

a *= 2;（a = a * 2; と同じ）

/=

除算代⼊

a /= 2;（a = a / 2; と同じ）

%=

剰余代⼊

a %= 2;（a = a % 2; と同じ）++

インクリメントa++;（a = a + 1; と同じ）--

デクリメントa--;（a = a - 1; と同じ）

いろいろな計算をしてみようキーボードからの⼊⼒を受け取る
キーボードからの⼊⼒を受け取ることで、⼊⼒に応じた処理を⾏うプログラムを作成できるようになる#include <stdio.h>
int main(void){
int i;
printf("整数を入力してください￥n");キーボードからの⼊⼒を変数iで受け取る
scanf_s("%d", &i);printf("%dが入力されました¥n", i);}
実行結果

整数を入力してください
キーボードからの⼊⼒
99
99が入力されました

実際に試してみよう

演算と型

異なる型の値の代⼊
int i = 1.99;
printf("%d", i);
実行結果

1
int型の変数には整数しか代⼊できない。
⼩数点を含む数を代⼊すると、⼩数点以下が無視される異なる型を含む演算型によって変数（⼊れ物）の⼤きさが異なる※ double 型の⽅が⼤きい型の異なる変数が含まれる演算では、⼤きい型に統⼀されて演算が⾏われる※ double 型の変数とint型の変数が含まれる演算では、double型に統⼀される

実行結果

5.500000整数どうしの割り算
int 型どうしの割り算では、結果もint型になるので注意が必要

実行結果

cの値は 0.000000

double 型に型変換（キャスト）して計算する

実行結果

cの値は 0.500000

実際に試してみよう

第3章 条件分岐と繰り返し

条件分岐

条件分岐
条件による処理の分岐「もしも○○ならば××を実⾏する」
if (○○) {××;}

if (条件式) {命令文; //条件式が「真」の場合に実行される}

条件分岐の例
if (age < 18) {printf("まだ選挙権はありません¥n");}関係演算⼦• 関係演算⼦を使って、2つの値を⽐較できる• ⽐較した結果は 「真」 または 「偽」になる

if 〜 else ⽂「もしも○○ならば××を実⾏し、そうでなければ△△を実⾏する」
if (○○) {××;} else {△△;}if (条件式) {//条件式が「真」の場合
命令文1;} else {//条件式が「偽」の場合
命令文2;
}

例
if (age < 18) {printf("まだ選挙権はありません¥n");} else {printf("投票に行きましょう¥n");}複数の if 〜 else ⽂
if〜else⽂を連結して、条件に応じた複数の分岐を⾏える
if (age < 4) {printf("入場料は無料です¥n");} else if (age < 13) {printf("子供料金で入場できます¥n");} else {printf("大人料金が必要です¥n");}

実際に試してみようワン・モア・ステップ：条件式に数値を使う• if ⽂の条件式に数値を使うことができる• 0が偽、それ以外が真であるとみなされる
if (0) {命令文}

if (1) {命令文}

命令文は実行されない

命令文は実行される

if (i) {命令文}
変数iの値が0でない場合に命令文が実行される

実際に試してみようswitch⽂

式の値によって処理を切り替える。break;でブロックを抜ける。

switch⽂の例(1)switch (score) {case 1:
printf("もっと頑張りましょう¥n");break;
case 2:
printf("もう少し頑張りましょう¥n");break;
case 3:printf("普通です¥n");break;
case 4:printf("よくできました¥n");break;
case 5:printf("大変よくできました¥n");break;
default:
printf("想定されていない点数です¥n");}printf("switchブロックを抜けました¥n");

実際に試してみようswitch⽂の例(2)switch (score) {case 1:case 2:
printf("もっと頑張りましょう¥n");break;
case 3:case 4:case 5:printf("合格です¥n");break;
default:
printf("想定されていない点数です¥n");}

実際に試してみようワン・モア・ステップ ： 3項演算⼦
int c;
if (a > b) {c = a;} else {c = b;}

int c = (a > b) ? a : b;（構⽂） 条件式 ? 値1 : 値2
条件式が「真」の場合に、式の値が「値1」になる。「偽」の場合には「値2」になる論理演算⼦
論理演算⼦を使って複数の条件式を組み合わせられる

論理演算⼦の例
ageが13以上 かつ ageが65未満
age >= 13 && age < 65

ageが13未満 または age が65以上
age < 13 || age >= 65

ageが13以上 かつ age が65未満 かつ 20でない
age >= 13 && age < 65 && age !=20

演算⼦の優先度
算術演算⼦が関係演算⼦より優先される
a + 10 > b * 5(a + 10) > (b * 5)

関係演算⼦が論理演算⼦より優先される
a > 10 && b < 3(a > 10) && (b < 3)

カッコの付け⽅で論理演算の結果が異なる
x && ( y || z )(x && y ) || z

実際に試してみよう

処理の繰り返し

繰り返し処理• ある処理を繰り返し実⾏したいことがよくある• ループ構⽂を使⽤すると、繰り返し処理を簡単に記述できる• C⾔語には3つのループ構⽂がある
for⽂
while⽂do 〜 while⽂

for⽂
for⽂の構⽂for (最初の処理; 条件式; 命令文の実行後に行う処理) {命令文}

1. 「最初の処理」を⾏う
2. 「条件式」が真なら「命令⽂」を⾏う
偽 ならfor⽂を終了する
3. 「命令⽂の後に⾏う処理」を⾏う
4. 2.に戻る

for⽂の例
for (int i = 0; i < 5; i++) {printf("こんにちは¥n");}forループ内で変数を使う
for ループ内で変数を使⽤することで、例えば1から100までを順番に⾜し合わせる計算ができる
int sum = 0;
for (int i = 1; i <= 100; i++) {sum += i;printf("%dを加えました¥n", i);}printf("合計は%dです¥n", sum);
実行結果

1を加えました2を加えました3を加えました…（中略）…
99を加えました
100を加えました
合計は5050です

実際に試してみよう

変数のスコープ• 変数には扱える範囲が決まっている。これを「変数のスコープ」と呼ぶ• スコープは変数の宣⾔が⾏われた場所から、そのブロック{ } の終わりまで
int main(void){
int sum = 0;
for (int i = 1; i <= 100; i++) {sum += i;printf("%dを加えました¥n", i);}printf("合計は%dです¥n", sum);}while⽂
while⽂の構⽂
while (条件式) {命令文}

1. 「条件式」が真なら「命令⽂」を⾏う
偽 ならwhileループを終了する
2. 1.に戻る※ for⽂と同じ繰り返し命令を書けるwhile⽂の例
int i = 0;
while (i < 5) {printf("こんにちは¥n");i++; // この命令文が無いと「無限ループ」}
int i = 5;
while (i > 0) {printf("こんにちは¥n");i--; // この命令文が無いと「無限ループ」}
実際に試してみようdo 〜 while⽂
do 〜 while⽂の構⽂
do {命令文} while (条件式);
必ず1回は実⾏される

1. 「命令⽂」を実⾏する
2. 「条件式」が真なら1.に戻る。
偽ならdo〜whileループを終了する※ for⽂、while⽂と同じ繰り返し命令を書けるdo 〜 while⽂の例
int i = 0;
do {printf("こんにちは¥n");i++;} while (i < 5);
int i = 5;
do {printf("%d¥n", i);i--;} while(i > 0);
実際に試してみようループの処理を中断する「break」
break; でループ処理を強制終了できる
int sum = 0;
for (int i = 1; i <= 10; i++) {sum += i;printf("変数sumに%dを加えました。", i);printf("sumは%d¥n", sum);if (sum > 20) {printf("合計が20を超えました。¥n");break;}}

ループ内の処理をスキップする「continue」
continue; でブロック内の残りの命令⽂をスキップできる
int sum = 0;
for (int i = 1; i <= 10; i++) {if (i % 2 == 0) {continue;}sum += i;printf("変数sumに%dを加えました。", i);printf("sumは%d¥n", sum);}
実際に試してみようループ処理のネスト
ループ処理の中にループ処理を⼊れられる
for (int a = 1; a <= 3; a++) {printf("a = %d¥n", a);//★
for (int b = 1; b <= 3; b++) {printf("b = %d¥n", b);//☆
}}★の命令⽂は3回実⾏される☆の命令⽂は9回実⾏される

実際に試してみよう

配列

1次元配列• 複数の値の⼊れ物が並んだもの（1次元配列とも呼ぶ）• 複数の値をまとめて扱うときに便利

配列の使い⽅
1. 配列を表す変数を宣⾔する
int scores[5];要素の数を指定する
2. 配列に値を⼊れる
scores[0] = 50;…
scores[4] = 80;[]の中の数字はインデックス
0〜(要素の数-1)を指定する

3. 配列に⼊っている値を参照する。
例：printf("%d¥n", scores[i]);配列の使⽤配列の使⽤配列は次のようにしても初期化できる
int scores[5] = {50, 55, 70, 65, 80};

要素の数の記述を省略できる
int scores[] = {50, 55, 70, 65, 80};

実際に試してみよう

多次元配列（配列の配列）
int scores[3][5];scores[0][0] = 50;scores[2][3] = 65;

2次元配列の宣⾔と初期化
2次元配列は次のようにしても初期化できる

最初の配列の要素数の記述は省略できる

第4章 関数

関数とは

関数とは
• ⻑いプログラムが必要になるときは、命令⽂を分けて管理した⽅が⾒通しがよくなる• 関数は複数の命令⽂をまとめたもの
関数の定義
void 関数名(void) {
命令文1
命令文2
‥‥
}
※ 関数をプログラムコードで記述することを「関数の定義」と呼ぶ

関数の定義の例

関数の呼び出し

実際に試してみようmain関数
int main(void)• C⾔語では、プログラムが実⾏されるときに、このmain関数が呼び出される• main関数は、プログラムの開始位置となる特別な関数関数を呼び出すときの処理の流れ

関数の呼び出しの階層#include <stdio.h>void func_a(void){}
void func_b(void){
func_a(void);}
int main(void){
func_b();}

実際に試してみよう

関数の引数

引数とは引数（ひきすう）
関数には、呼び出すときに値を渡すことができる。この値を「引数」と呼ぶ。
引数のある関数の定義

void 関数名(型 変数名) {命令文}

引数のある関数の例
引数の受け渡しには、関数名の後ろのカッコ()を使⽤する。
#include <stdio.h>

startという名前のint型の変数で値を受け取る

void countdown(int start){
printf("関数が受け取った値：%d¥n", start);
printf("カウントダウンをします¥n");for (int i = start; i >= 0; i--) {printf("%d¥n", i);}}
int main(void){
countdown(3);countdown(10);}

実際に試してみよう引数が複数ある関数の例
カンマで区切って複数の引数を指定できる#include <stdio.h>void countdown(int start, int end){
printf("カウントダウンをします¥n");for (int i = start; i >= end; i--) {printf("%d¥n", i);}}
int main(void){
countdown(7, 3);}

実際に試してみよう実引数と仮引数
実引数：関数を呼び出すときに、関数に渡される値
実引数

countdown(7, 3);

仮引数：関数側で値を受け取るために準備される変数
仮引数

void countdown(int start, int end)
実引数の値がコピーされて、それが仮引数に代⼊される

実引数と仮引数#include <stdio.h>void func(int i) {i++;}
int main(void){
int i = 10;
printf("(1) iの値は%d¥n", i);func(i);printf("(2) iの値は%d¥n", i);}

実際に試してみよう

実行結果(1) iの値は10(2) iの値は10

iの値はfunc関数呼び出し前後で変化しない
func関数には、値の複製（コピー）が渡される

関数の戻り値戻り値とは戻り値関数から返される値
戻り値のある関数の定義

戻り値の型 関数名(引数列) {命令文
return 戻り値;}

戻り値のある関数の例１• return を使って値を戻すようにする• 戻り値は1つだけ• 戻り値の型を関数名の前に記す#include <stdio.h>
double circle_area(double r){
return r * r * 3.14159;}
int main(void){
double d = circle_area(2.5);
printf("半径2.5の円の面積は %.2f¥n", d);}

実際に試してみよう

関数のまとめ
引数なし、戻り値なし
void 関数名(void) {
命令文}

引数あり、戻り値なし
void 関数名(型 変数名) {命令文}

引数あり、戻り値あり
戻り値の型 関数名(型 変数名) {命令文
return 戻り値;}ワン・モア・ステップ：論理演算式の値
if (d > 0) {return 1;} else {return 0;}

論理演算式は値を持つ真のときに1
偽のときに0

return (d > 0);if (is_positive_number(d) == 1) { 命令文 }if (is_positive_number(d)) { 命令文 }関数のプロトタイプ宣⾔
main関数から、他の関数を呼び出すときには、その関数がどのようなものであるかをコンパイラが知っている必要がある
1. main関数よりも前に関数の定義を記述する
2. プロトタイプ宣⾔(どのような関数であるかを記したもの）を、関数の前に記述して、関数の定義はmain関数の後ろに記述する関数のプロトタイプ宣言

戻り値の型 関数名(引数列);

関数のプロトタイプ宣⾔の例#include<stdio.h>void func1(int a);void func2();関数のプロトタイプ宣⾔

int main(void){
func1(10);func2();}
void func1(int a){
printf("func1が呼び出されました¥n");printf("引数の値は%d¥n", a);}void func2(){
printf("func2が呼び出されました¥n");}

第5章 アドレスとポインタ

アドレスとポインタ

コンピューターのメモリー• コンピューターがプログラムを実⾏している途中に扱う、さまざまな情報がメモリーに格納される• メモリーを、1 バイト単位で区切れるように⽬盛りがついた、横に⻑い箱のようなものだとみなす• ⽬盛りに割り当てられた数字（番地）をアドレスというアドレス
int i = 5;
• int型の箱（サイズ 4バイト）がメモリー上に確保される• その領域に、5という値のデー
タが格納される

printf("%p", &i);

メモリー上の変数iの位置（アドレス）を知る

実行結果

0133FF00&変数名

16進数表現（実⾏のたびに変化する。具体的な値にあまり意味はない）
変数のアドレスを参照するための記号「&」はアドレス演算⼦と呼ばれる。

ポインタ• アドレスはポインタ変数に代⼊できる
int i = 5;
int *p = &i;

ポインタ変数（変数名の前に*記号をつける）※ 図では、ポインタ変数の⼊れ物のサイズを4バイトで表していますが、このサイズは実⾏環
境によって異なります。たとえば、64bit環境では8バイトになります。

ポインタを使った値の参照•pに⼊っているアドレスから、そこに存在する値を⾒にいくには、プログ
ラムコードの中で「*p」と記述する• 「i」と「*p」は、メモリー上の同じ値を参照する
int i = 5;
int *p = &i;printf("iのアドレスは%p¥n", &i);printf("pの値は%p¥n", p);printf("iの値は%d¥n", i);printf("*pの値は%d¥n", *p);

実際に試してみよう

実行結果

iのアドレスは00B3F77C
pの値は00B3F77C
iの値は5*pの値は5
別名 *pポインタの活⽤

ポインタを使って値を変更する
int i = 5;
int *p = &i;••*p は変数 i の別名としてふるまう
これ以降のプログラムコードに*pが登場したときには、これを変数iに置き換えて読むと理解しやすくなる•「int j = *p;」は、「int j = i;」と同じように働き、変数jに変数iの値が代⼊される•「*p = 10;」は「i = 10;」と同じように働き、変数iの値が10になる。

•

つまり、ポインタを使って、変数の値を変更できるポインタを使って値を変更する
int i = 5;
int *p = &i;
int j = *p;printf("jの値は%d¥n", j);*p = 10;printf("iの値は%d¥n", i);
実行結果

実際に試してみよう

jの値は5
iの値は10• 変数iと*pは、名前が違うだけで、まったく同じようにふるまう。
• *pはiのエイリアス（別名）であるという。

ポインタが指す先を変更する
int i = 5;
int j = 8;
int *p = &i;printf("*pの値は%d¥n", *p);
ポインタ変数に変数jのアドレスを代⼊
p = &j;printf("*pの値は%d¥n", *p);
実行結果*pの値は5*pの値は8ポインタを引数とする関数#include <stdio.h>void func(int *p){*p = 100;}
int main(void){
int i = 5;
func(&i);printf("iの値は%d¥n", i);}
実行結果

実際に試してみよう

iの値は100
関数にアドレスを渡すと、そのアドレスに格納されている値を、関数の中で変更できる

値の交換をする swap 関数#include <stdio.h>void swap(int *a, int *b){
int tmp = *a;*a = *b;*b = tmp;}
int main(void){
int a = 2;
int b = 3;
printf("a=%d, b=%d¥n", a, b);
printf("swap関数を呼び出します¥n");swap(&a, &b);
printf("a=%d, b=%d¥n", a, b);}
実行結果

a=2, b=3
swap関数を呼び出します
a=3, b=2

実際に試してみよう配列とポインタ

配列の要素のアドレス
int scores[3] = {10, 20, 30};
printf("scores[0]のアドレス: %p¥n", &scores[0]);printf("scores[1]のアドレス: %p¥n", &scores[1]);printf("scores[2]のアドレス: %p¥n", &scores[2]);実行結果• 配列の要素は、メモリ上に連続して格納される

配列とポインタ
int scores[3] = {10, 20, 30};
int *p = &scores[0];int *p = scores;scores[0] = 5;••

どちらの表記も同じ意味
配列名だけの表記は、先頭要素のアドレスを表す

どちらの表記も同じ意味*p = 5;配列の要素には、異なる表記（別名）でアクセスできるポインタに対する加算
int scores[] = { 10, 20, 30 };int *p = scores;
printf("*pの値は%d¥n", *p);printf("*(p + 1)の値は%d¥n", *(p + 1));
printf("*(p + 2)の値は%d¥n", *(p + 2));
実行結果

*pの値は10*(p + 1)の値は20*(p + 2)の値は30•ポインタ変数に対して1だけ加算すると、型のサイズ分だけ、アドレスの値が増えるポインタ変数のインクリメント
ポインタが配列の先頭の要素を指しているとき、ポインタ変数の値を1だけ増やすと、配列の次の要素を指すようになる。
int scores[] ={ 10, 20, 30 };int *p = scores;
printf("%d¥n", *p);p++;
printf("%d¥n", *p);p++;
printf("%d¥n", *p);
実行結果

10
20
30

実際に試してみよう

関数の引数に配列を使う#include <stdio.h>void func(int *p)
引数を int p[] と書くこともできる{
p[0] = 98;
p[1] = 99;
あたかもp⾃体が配列かのように扱える
p[2] = 100;}
int main(void){
int scores[] = { 10, 20, 30 };func(scores);
配列の先頭の要素のアドレスをfunc関数に渡している
for (int i = 0; i < 3; i++) {printf("scores[%d]の値は%d¥n", i, scores[i]);
}
}
実行結果

scores[0]の値は98
scores[1]の値は99
scores[2]の値は100関数ポインタ

関数のアドレスと関数ポインタ• 関数もメモリーに格納される• 関数にもアドレスがある• 関数のアドレスは、関数名を書くだけで参照できる（アドレス演算⼦「&」をつける必要は無い）
printf("関数funcのアドレスは%p¥n", func);• 関数のアドレスを格納するための変数は、次のように宣⾔する。
関数の戻り値の型 (*変数名) (関数の引数の型);
例 「戻り値なし」「引数がint型の値」である関数のアドレスを格納するための変数

void (*pF) (int);
変数名関数ポインタを使⽤する例#include <stdio.h>void func(int a){
printf("func関数が呼び出されました。引数の値は%d¥n", a);}
int main(void){
void (*pF)(int) = func;(*pF)(10);}
実行結果

func関数が呼び出されました。引数の値は10func(10);どちらの表記も同じ意味(*pF)(10);関数のアドレスと関数ポインタのイメージ

関数ポインタの配列• 関数のアドレスを格納する配列の宣⾔
関数の戻り値の型 (*配列名[]) (関数の引数の型);

例 「戻り値なし」「引数なし」である関数のアドレスを格納する配列の宣⾔

void (*pFs[]) ();配列名

関数ポインタの配列の例#include <stdio.h>void func_dog() { printf("わんわん¥n"); }void func_cat() { printf("にゃー¥n"); }void func_pig() { printf("ブーブー¥n"); }int main(void){
void (*pFs[])() = { func_dog, func_cat, func_pig };for (int i = 0; i < 3; i++) {(*pFs[i])();
}
}
実行結果

わんわん
にゃー
ブーブー

⾼階関数
•

関数のアドレスを引数として受け取る関数を⾼階関数という⾼階関数の例
void func(void (*pF)(int)){(*pF)(10);「戻り値無し」「引数がint型の値」の関数}のアドレスを変数pFで受け取るpFが指す『「戻り値無し」「引数がint型の値」の関数』に、値10を渡す

第6章 ⽂字列の扱いと構造体

⽂字列と配列

配列への⽂字列の格納••

⽂字列はchar型の配列に1⽂字ずつ格納する
⽂字列の末尾には、NULL⽂字（'\0'）を格納する決まりがある•

3⽂字の"dog"を格納するには、末尾にNULL⽂字を⼊れることを考慮して、4以上の要素を持つchar型の配列を準備する※ 扱う⽂字はchar型で表現可能な半⾓英数記号に限定するchar型の配列に格納された⽂字列の出⼒• printf関数でchar型の配列に格納された⽂字列を出⼒するには、変換指定に%sを使⽤する
char str[4];str[0] = 'd';str[1] = 'o';str[2] = 'g';str[3] = '¥0';
printf("犬は英語で %s¥n", str);
実行結果

⽝は英語で dog

char型の配列の初期化
char型の配列に⽂字列を格納するには、複数の⽅法がある
char str[4] = {'d', 'o', 'g', '¥0'};char str[4] = "dog";

どれでも同じchar str[] = "dog";

注意：このような書き⽅ができるのは、char型の配列の宣⾔と同時に代⼊を⾏う初期化時だけ
char str[4];str = "dog";← コンパイルエラー

実際に試してみよう標準⼊⼒から⽂字列を受け取る#include <stdio.h>
int main(void){char str[32];printf("名前を入力してください¥n");scanf_s("%s", str, 32);
printf("%sさん、こんにちは¥n", str);}
実行結果

名前を入力してくださいjun
junさん、こんにちは••

⼊⼒される⽂字列を格納するchar型の配列は、⼗分な要素数を持つようにする
scanf_s関数を使って⽂字列の⼊⼒を受け取る

実際に試してみよう※ Visual Studio 以外では scanf_s関数が使えないことがある。その場合はscanf関数を使⽤する

⽂字列の操作

⽂字列へのポインタ• ⽂字列の扱いは配列の扱いと同じ• 関数に⽂字列を渡すときには、先頭の要素のアドレスを渡す（復習）配列名だけを書くと、その配列の先頭の要素のアドレスを表す#include <stdio.h>void func(char *s){
printf("引数で渡された文字列: %s¥n", s);}
int main(void){char str[] = "dog";func(str);}⽂字列の配列
⽂字列の配列は、配列の配列
char strs[3][5] = { "dog", "cat", "bird" };for (int i = 0; i < 3; i++) {printf("%s¥n", strs[i]);}実際に試してみよう
実行結果

dog
cat
bird

string.h のインクルード
string.h をインクルードすることで、⽂字列操作
を⾏うための様々な関数を使⽤できるようになる#include <stdio.h>#include <string.h>
⽂字列操作を⾏う関数のプロトタイプ宣⾔が書かれている

例：
・⽂字列のコピー strcpy_s （または strcpy）関数
・⽂字列の⽐較 strcmp 関数
・⻑さの取得 strlen関数
・⽂字列の連結 strcat_s （または strcat）関数
・⽂字列の書き出し sprintf_s （または sprintf）関数

ワン・モア・ステップ
Visual Studio 独⾃の「_s」関数• strcpy_s関数のように末尾に「_s」がつく関数は、
Visual Studioを開発したMicrosoft社が独⾃に作った関数• 引数に「⽂字列を格納する配列の要素数」の指定が追加されることで、オーバーフロー（またはオーバーラン）を未然に防げる• Visual Studio では、C⾔語標準の strcpy 関数を使うと、コンパイルエラーになるが#define _CRT_SECURE_NO_WARNINGSという1⾏を追加することで、コンパイルエラーにならない
ようにできる

⽂字列操作のための関数の使⽤例
・strcpy_s 関数による⽂字列の代⼊
char str[4];strcpy_s(str, 4, "dog");
printf("%s", str);// dog・strcpy_s 関数による⽂字列のコピー
char str1[] = "Hello";char str2[16];strcpy_s(str2, 16, str1);
printf("%s", str2);// Hello・strcmp 関数による⽂字列の⽐較
char str1[] = "Hello";char str2[] = "HELLO";
if (strcmp(str1, str2) == 0) {printf("同じ");} else {printf("違う"); // こちらが出力される}

⽂字列操作のための関数の使⽤例
・strlen 関数による⽂字数の取得
char str[32] = "Hello";
int len = strlen(str);
printf("%d", len); // 5
・strcat_s 関数による⽂字列の連結
char str[32] = "Hello";
strcat_s(str, 32, " world!");printf("%s", str); // Hello world!
・sprintf_s関数による⽂字列への出⼒
char str[32];char name[] = "Taro";
sprintf_s(str, 32, "Hello %s!", name);printf("%s", str); // Hello Taro!

構造体

⼀緒に管理したい情報• ⾃分で新しい型を定義できる ← 構造体• 構造体には「整数」と「⽂字列」といった複数の値をもたせることができる• 構造体の名前には struct というキーワードをつける
名前（char型の配列）と年齢（int型）の情報を持つ、
struct Person という名前の構造体

struct Person{
char name[16];int age;};

構造体のメンバ

構造体
struct Person{
char name[16];int age;};•

構造体 struct Person 型の変数の宣⾔と初期化

struct Person a = { "SUZUKI TARO", 17 };•メンバ変数の参照

printf("%s¥n", a.name);printf("%d¥n", a.age);
構造体の変数名.メンバ変数名という表記でメンバ変数を参照できる

構造体の使⽤例#include <stdio.h>
struct Person{
char name[16];int age;};int main(void){
struct Person a = { "SUZUKI TARO", 17 };struct Person b = { "SATO HANAKO", 19 };printf("名前:%s, 年齢:%d¥n", a.name, a.age);printf("名前:%s, 年齢:%d¥n", b.name, b.age);}
実行結果

名前:SUZUKI TARO, 年齢:17
名前:SATO HANAKO, 年齢:19

実際に試してみようtypedef で型に別名をつける・ typedef 宣⾔によって、⻑い名前の型名を短くしたり、覚えやす
い名前にできる
typedef 宣⾔

typedef 型名 別の呼び名;
例：int を seisuu と表記できるようにする#include <stdio.h>
typedef int seisuu;
int main(void){seisuu i = 10;
printf("iは%d¥n", i);}構造体の定義とtypedef宣⾔
構造体の定義とtypedef宣⾔を同時に⾏うことができる
typedef struct Person{
char name[16];int age;} Person;struct Person の別名

struct Person 型の変数の宣⾔を次のように表記できる
Person a = { "SUZUKI TARO", 17 };実際に試してみよう

構造体の応⽤

構造体と関数
これまでに学習した他の型と同じように構造体を関数の引数にできる
void print_info(Person p){
printf("名前:%s, 年齢:%d¥n", p.name, p.age);}関数の中でメンバ変数の値を変更したい場合は、ポインタ
渡し（アドレスを渡す）にする
void birthday(Person *p){(*p).age++;}アロー演算⼦（ ->）void birthday(Person *p){(*p).age++;}どちらの表記でも動作は同じ
void birthday(Person *p){p->age++;}値渡しとアドレス渡し
void func(Person p){
printf("%d", p.age);}

Person型の変数の複製が変数pに代⼊される

void func(Person *p){
printf("%d", (*p).age);(*p).age++;}

Person型の変数のアドレスが変数pに代⼊される○ pのメンバ変数を参照する
× pのメンバ変数を変更する（呼び出しもとには影響しない）○pのメンバ変数を参照する○pのメンバ変数を変更する※ 値を参照するだけであっても、複製を⾏う処理が⽣じないので、より効率的

const 修飾⼦
値が変化しない変数であることを明⽰するためにconst修飾⼦を使⽤する
変数aの値は変更できない
const int a = 10;a++;コンパイルエラー
メンバ変数の値を変更できないvoid func(const Person *p){p->age++;}コンパイルエラー

実際に試してみよう※ この関数にアドレスを渡しても、メンバ変数の値が変更されることが無いことを知ることができる

構造体と配列
構造体も他の型と同じように配列に格納できる
Person p[2];次のようにして、宣⾔と初期化を同時にできる
Person p[] = { { "SUZUKI TARO", 17 },{ "SATO HANAKO", 19 } };配列の要素へのアクセス

for (int i = 0; i < 2; i++) {printf("名前:%s, 年齢:%d¥n", p[i].name, p[i].age);}ポインタを含む構造体#include <stdio.h>
typedef struct Person{
char name[16];int age;struct Person *mother;} Person;struct Person *型のポインタ変数

int main(void){
Person p0 = { "SUZUKI AKIKO", 44, NULL };Person p1 = { "SUZUKI TARO", 17, &p0 };printf("%s の母親は %s¥n", p1.name, p1.mother->name);}
実行結果

SUZUKI TARO の母親は SUZUKI AKIKO

実際に試してみよう

第7章 ⼀歩進んだC⾔語プログラミング

ファイル⼊出⼒

テキストファイルの読み込み••

テキストファイル：⽂字列が保存されたファイル
バイナリファイル：テキストファイルではないファイル（画像デー
タ、⾳声データなどのファイル）•

テキストファイルの読み込みの⼿順
1. ファイルを開く（fopen_s関数〔またはfopen関数〕を使⽤）
2. ファイルから⽂字列を読み込み、処理を⾏う（fgets関数などを使⽤）
3. ファイルを閉じる（fclose関数を使⽤）

ファイルの読み込みとファイルの保存場所
FILE *fp = NULL;
fopen_s(&fp, "data/sample.txt", "r")ファイルの場所（相対パス）の指定

プロジェクトファイル（.vcxproj）があるフォルダが基準

sample.txt の中身
Humpty Dumpty sat on a wall,
Humpty Dumpty had a great fall.
All the king's horses and all the king's men
Couldn't put Humpty together again.

テキストファイル読み込みの例#include <stdio.h>
int main(void)
ファイルポインタ（ストリームとも呼ぶ）{
FILE *fp = NULL;
char line[128];
if (fopen_s(&fp, "data/sample.txt", "r") != 0) {printf("ファイルを開けませんでした¥n");return 1;
モード指定}
while (fgets(line, 128, fp) != NULL) {printf(">>> %s", line);}
fclose(fp);return 0;}

実際に試してみようテキストファイルに書かれたデータの読み込み
FILE *fp = NULL;
char line[128];
if (fopen_s(&fp, "data/scores.txt", "r") != 0) {printf("ファイルを開けませんでした¥n");return 1;}
int total = 0;
int i;
while (fscanf_s(fp, "%d", &i) != EOF) {printf("%d を読み込みました¥n", i);total += i;}

テキストファイルに書かれている数字を1つずつ読み込む

printf("合計は %d¥n", total);
fclose(fp);return 0;

実際に試してみようテキストファイルの書き出し•

テキストファイルの書き出しの⼿順
1. ファイルを開く（fopen_s関数〔またはfopen関数〕を使⽤）
2. ⽂字列をファイルに書き出す（fprintf関数などを使⽤）
3. ファイルを閉じる（fclose関数を使⽤）書き出しのモード指定

FILE *fp;
if (fopen_s(&fp, "output.txt", "w") != 0) {printf("ファイルを開けませんでした¥n");return 1;}
for (int i = 0; i < 10; i++) {fprintf(fp, "[%d]¥n", i);}fclose(fp);return 0;

実際に試してみようグローバル変数と複数ファイルへの分割

グローバル変数••••

変数が使⽤できる範囲は、宣⾔されたブロックの中に限られる（変数のスコープ）関数の中で宣⾔した変数、または引数の受け取りのために使⽤する変数をローカル変数とよぶプログラム全体で使⽤できるグローバル変数というものがある
グローバル変数は、関数の外側で宣⾔し、どの関数の中からも使⽤できる#include <stdio.h>
int a = 10;
void func(void) {グローバル変数。プログラム全体のどこからでも参照できるa++;

int main(void){func();
printf("%d¥n", a);}}

実際に試してみよう

複数のファイルに分ける

実際に試してみようマクロと列挙

プリプロセッサの処理• コンパイルが⾏われる前に、プログラムコードを⾃動編集するプリプロセスと呼ばれる操作が⾏われる。
• この操作を⾏うプログラムをプリプロセッサと呼ぶ。
• 例： インクルード命令#include <ファイル名>
インクルード⽂の場所に、指定されたファイルの中⾝を埋め込む• 例：マクロ（置換処理）#define 置き換えられる⽂字列 置き換える⽂字列
コンパイル前に⽂字列の置き換えが⾏われるマクロの例#define NUM 10

これ以降の「NUM」を「10」に置き換える

int x[NUM]
int y[NUM]#define PI 3.14159265359

これ以降の「PI」を「3.14159265359」に置き換える関数形式マクロ• マクロと関数の仕組みを組み合わせたもの#define MAX(x, y) (x > y ? x : y)(x > y ? x : y)MAX(x, y)置き換えられる(a > b ? a : b)

MAX(a, b)
置き換えられるMAX(p0.age, a1.age)(p0.age > p1.age ? p0.age : p1.age)

置き換えられる

条件付きコンパイル•

条件に応じてコンパイル対象となるプログラムコードを切り替えることができる#ifdef マクロ名
プログラムコード

マクロ名が定義されている（「#define マクロ名」が存在する）場合のみ、コンパイル対象となる#endif#include <stdio.h>#define ADDRESS_CHECK
int main(void){
int a = 9;
#ifdef ADDRESS_CHECK
printf("aのアドレスは%p¥n", &a);#endifprintf("aの値は%d¥n", a);}

実際に試してみよう

列挙型
•
•
•

列挙型という、限られた値のみ格納できる型がある
列挙型は、構造体と同じように、⾃分で新しく定義する型
列挙型は、⾃分で定義したキーワード（識別⼦）を格納するための型•

enum というキーワードを最初につける必要がある

例

enum Size { S, M, L, XL };「enum Size」が型名
S, M, L, XL のいずれかの値をとることができる
enum Size a = XL;

実際に試してみよう

第8章 データ構造とアルゴリズム

アルゴリズムと計算量アルゴリズムと計算量• ⽬的を達成するための⼿順のことをアルゴリズ
ムという• アルゴリズムによる効率の違いを計算量によって⽰す• データの⼤きさをnで表したときに、計算回数や計算時間がnに⽐例するとき、計算量をO(n)と表記する（オーダー表記）

線形探索と2分探索
⽬的：昇順に並んだデータ列から、⽬的の値の場
所を⾒つける• 線形探索：前から順にデータを⾒ていく
O(n)の計算量• 2分探索：対象を半分に絞りながら⾒ていくO(log n)の計算量

ワン・モア・ステップ！ 計算量と計算時間
計算量と計算時間の関係

線形探索の例#include <stdio.h>#define DATA_NUM 10
int data[] = { 1, 7, 11, 12, 33, 57, 63, 72, 89, 99 };int main(void){
int target = 57;
for (int i = 0; i < DATA_NUM; i++) {if (data[i] == target) {printf("data[%d] に見つかりました¥n", i);return 0;}}

for⽂で先頭から順に調べていく

printf("見つかりませんでした¥n");}

実際に試してみよう2分探索アルゴリズム
例： 57を⾒つけたい#include <stdio.h>#define DATA_NUM 10
int data[] = { 1, 7, 11, 12, 33, 57, 63, 72, 89, 99 };int main(void){
int target = 57;
int left_index = 0;
int right_index = DATA_NUM - 1;
int middle_index = 0;
while (left_index <= right_index) {middle_index = (left_index + right_index) / 2;
printf("left:%d, middle:%d, right:%d¥n",left_index, middle_index, right_index);if (data[middle_index] == target) {printf("値 %d は data[%d] に見つかりました¥n",target, middle_index);
return 0;} else if (data[middle_index] < target) {left_index = middle_index + 1;
} else {right_index = middle_index - 1;}}
printf("値 %d は見つかりませんでした¥n", target);}

探索範囲を半分に絞っていくデータの格納配列とスタック• データの格納⽅法の1つにスタックがある• 後に⼊れたものほど先に取り出される（後⼊れ先出し）• push操作：先頭に要素を追加する• pop操作：先頭から要素を取り出す

#include <stdio.h>#define STACK_SIZE 5

スタックの例

int stack[STACK_SIZE];
int head_index = -1;
void push(int value) {head_index++;stack[head_index] = value;}
int pop() {int value = stack[head_index];
head_index--;return value;}
void main() {push(5);// [5][][][][]
push(9);// [5][9][][][]
push(3);// [5][9][3][][]
printf("pop %d¥n", pop()); // 3,
printf("pop %d¥n", pop()); // 9,
printf("pop %d¥n", pop()); // 5,}[5][9][3][][][5][9][3][][][5][9][3][][]

リスト• 複数のデータを管理する仕組み• データを各ノードに格納する• ノードは、次のノードへのポインタを併せ持つ

#include <stdio.h>
typedef struct Node {char str[256];
struct Node *next;}Node;

スタックの例

リストの例

Node *head = NULL;
void printout(void) {Node *node = head;
while (node != NULL) {printf("[%s]", node->str);
node = node->next;}
printf("¥n");}
int main(void) {Node n0 = { "This", NULL };Node n1 = { "is", NULL };Node n2 = { "a", NULL };Node n3 = { "cat", NULL };head = &n0;
n0.next = &n1;
n1.next = &n2;
n2.next = &n3;
printout();
Node new_node = { "not", &n2 };n1.next = &new_node;printout();}

ノードを順にたどっていく

新しいノードの追加

リストでのノードの追加と削除

整列（ソート）ソート• データを順序に従って並べ替える処理をソートという• もっとも単純なソートアルゴリズムの1つにバブ
ルソートがある• 計算量はO( ଶ )で、効率は悪い

バブルソートの仕組み
例：初期状態が 7, 2, 6, 9, 4 である数字の列を昇順に並び替える様⼦

#include <stdio.h>

スタックの例#define DATA_NUM 5
int data[] = { 7, 2, 6, 9, 4 };バブルソートの例

void printout() {for (int i = 0; i < DATA_NUM; i++) {printf("%d ", data[i]);}printf("¥n");}
int main(void) {printout();for (int i = 0; i < DATA_NUM - 1; i++) {for (int j = DATA_NUM - 1; j > i; j--) {if (data[j] < data[j - 1]) {int tmp = data[j];data[j] = data[j - 1];
2つの値の交換
data[j - 1] = tmp;
printout();}}}
printf("---¥n");printout();}

⼆重ループで並べ替えを⾏う終190