はじめてのCプログラミング -基礎から応用まで(授業資料)

概要: 本講座は、Visual Studio 2019 C++を使用したCプログラミングの実践的な演習資料である。Cプログラミングの初心者から中級者を対象とし、基本的なプログラミング概念から応用的な技法まで、段階的にスキルを習得できる構成となっている。各回は説明と例題プログラムで構成され、実践を通じた学習を重視している。

構成(全15回)

フェーズ テーマ 主要トピックス
導入 1〜2 開発環境とエラー対処 Visual Studioの使い方、文法エラーの対処
基礎 3〜6 プログラミングの基本要素 変数と計算、条件分岐、繰り返し、データ型
発展 7〜10 データ構造と関数 配列、関数、再帰関数、末尾再帰・多重再帰
応用 11〜15 高度な機能 ポインタ、文字列、構造体、ファイル処理、シミュレーション

学習の流れ

本講座は、導入フェーズで開発環境に習熟した後、基礎フェーズで変数・制御構造・データ型といったプログラミングの基本要素を学ぶ。発展フェーズでは配列と関数を習得し、再帰という重要な概念を理解する。応用フェーズでは、ポインタ・文字列・構造体といったC言語特有の機能を学び、ファイル処理やシミュレーションへと発展させる。

全体を通じて51の例題プログラムが用意されており、理論と実践を結びつけながら学習を進めることができる。

Visual Studio C++を用いる. Visual Studio Community 2019 C++のインストールについては、 別のページで説明している.
教材の利用条件: クリエイティブコモンズ 表示-非営利-継承 4.0 国際ライセンス(CC BY-NC-SA 4.0)に基づき、著作者表示・非営利目的・同一ライセンスでの再配布を条件として自由に利用可能である。

1. Microsoft Visual Studio 2019 C++ の使い方

資料(スライド): cp-1. Microsoft Visual Studio 2019 C++ の使い方 [PDF], [パワーポイント], [HTML]

[例題]

演習パート(クリックして展開)

概要

本ガイドは、Microsoft Visual Studio 2019を使用してCプログラムを作成・実行する手順を説明するためのものである。プログラミング入門者が、開発環境の基本的な使い方を習得することを目的とする。

課題1:Microsoft Visual Studioでのプログラム作成と実行

目的

Microsoft Visual Studio 2019を使用して、Cプログラムの作成からビルド(ソースファイルから実行型ファイルを生成する処理)、実行までの一連の手順を体験し、開発環境の基本操作を習得する。

手順

Step 1:Microsoft Visual Studioの起動

  1. Windowsのスタートメニューを開く
  2. 「Visual Studio 2019」を選択して起動する
  3. 初回起動時のみ、以下の初回起動設定を行う
    • 「後で行う。」を選択する
    • 「Visual Studioの開始」をクリックする
    • 作業の種類を選ぶ画面が表示されることを確認する

Step 2:プロジェクトの新規作成

  1. 起動画面で「新しいプロジェクトの作成」を選択する
  2. プロジェクトの種類を選ぶ画面が表示される
  3. 「コンソールアプリ」を選択する
  4. 「次へ」をクリックする
  5. 「プロジェクト名」が自動設定されていることを確認する
  6. 「場所」が自動設定されていることを確認する(ネットワークドライブを使用する場合は適宜変更する)
  7. 「作成」をクリックする
  8. 設定したプロジェクト名が画面に表示されることを確認する

Step 3:ソースファイルの編集と保存

  1. ソースファイル編集画面を確認する
  2. 以下のプログラムを入力する
#include <stdio.h>
#include <math.h>
#pragma warning(disable:4996)
int main()
{
    double x;
    double y;
    char buf[256];
    int i;
    double start_x;
    double step_x;
    FILE* fp;
    printf( "start_x =" );
    fgets( buf, 256, stdin );
    sscanf_s( buf, "%lf\n", &start_x );
    printf( "step_x =" );
    fgets( buf, 256, stdin );
    sscanf_s( buf, "%lf\n", &step_x );
    fp = fopen( "d:\\data.csv", "w" );
    for( i = 0; i < 20; i++ ) {
        x = start_x + ( i * step_x );
        y = sin( x );
        printf( "x= %f, y= %f\n", x, y );
        fprintf( fp, "x=, %f, y=, %f\n", x, y );
    }
    fprintf( stderr, "file created\n" );
    fclose( fp );
    return 0;
}
  1. 保存ボタンをクリックして保存する(メニューからも保存可能)

Step 4:ビルド

  1. メニューから「ビルド」→「ソリューションのビルド」を選択する
  2. 画面下部に「ビルド:1 正常終了,0 失敗・・・」と表示されることを確認する
  3. 表示が異なる場合は、ソースコードに入力ミスがある可能性があるため、Step 3に戻って確認・修正する

Step 5:実行

  1. ビルドが正常終了したことを確認する
  2. メニューから「デバッグ」→「デバッグなしで開始」を選択する
  3. 実行画面(コンソールウィンドウ)が表示される
  4. 「start_x =」と表示されたら、数値(例:0)を入力してEnterキーを押す

d:ドライブに関する注意事項

本プログラムは、計算結果を d:\data.csv に書き出す設定になっている。d:ドライブが存在しない環境では、プログラムの実行時にファイルの作成に失敗する。

d:ドライブが存在しない場合の対処方法:

  1. Step 3で入力したソースコード内の fp = fopen( "d:\\data.csv", "w" ); の部分を確認する
  2. ファイルパスを、自分の環境に存在するドライブ・フォルダに変更する
    • 例:fp = fopen( "c:\\temp\\data.csv", "w" );
    • 例:fp = fopen( "data.csv", "w" );(プロジェクトフォルダに出力)
  3. 変更後、ファイルを保存し、再度ビルドを行う

d:ドライブの存在確認方法:

  1. エクスプローラーを開く
  2. 「PC」または「コンピューター」をクリックする
  3. 表示されるドライブ一覧に「d:」が含まれているか確認する
  1. 「step_x =」と表示されたら、数値(例:0.1)を入力してEnterキーを押す
  2. 計算結果が20回分表示されることを確認する
  3. 「file created」と表示され、d:\data.csv(または変更後のパス)にファイルが作成されたことを確認する
  4. 何かキーを押して実行画面を閉じる

ヒント

プログラムの構造について

このプログラムは以下の3つの機能で構成されている。

  • キーボードからのデータ読み込み(start_xとstep_xの値を取得)
  • 計算の実行(sin関数を使用した計算を20回繰り返し)
  • ファイルへの書き出し(計算結果をd:\data.csvに保存)

よくある間違いの回避方法

  • 円記号(¥)とバックスラッシュ(\)は、日本語環境では同じ文字コードに対応する。環境によって表示が異なる場合があるが、キーボードの同じキーで入力できる
  • ファイルパス「d:\\data.csv」の二重バックスラッシュは、C言語の文字列内でバックスラッシュを表すための記法である
  • ビルドでエラーが発生した場合は、エラーメッセージを確認し、該当行の入力ミス(セミコロンの欠落、括弧の不一致など)を探す

手戻りについて

ビルドや実行の段階で問題が発生した場合は、ソースファイルの編集からやり直す。これはプログラム開発における通常のワークフローである。

発展:他のソフトとのデータ連携(任意)

課題1の実行により作成されたファイル(d:\data.csv)を使用して、Microsoft Excelでグラフを作成することができる。

2. Cプログラム作成時のよくある間違い

資料(スライド): cp-2. Cプログラム作成時のよくある間違い [PDF], [パワーポイント], [HTML]

演習パート(クリックして展開)

はじめに

本ガイドは、C言語プログラミングにおける「よくある間違い」を理解し、自分で発見・修正できるようになることを目的とする。各トピックについて、スライドを参照しながら確認演習を行うこと。

演習1:カッコの対応を確認する

目的

C言語における波カッコ{}と丸カッコ()の対応関係を理解し、対応が取れていない場合のエラーを認識できるようになる。

手順

  1. main関数における{}()の対応関係を確認する
  2. }を忘れた場合にどのようなエラーメッセージが表示されるか確認する
  3. )を忘れた場合のエラーメッセージを確認する
  4. 自分のプログラミング環境で、意図的に}または)を削除したコードをコンパイルし、エラーメッセージを観察する

ヒント

カッコの対応ミスは、エラーメッセージが実際の間違い箇所とは異なる行を指すことがある。エラーが出たら、カッコの開始位置まで遡って確認すること。

演習2:クオーテーションの対応を確認する

目的

ダブルクオーテーション"の対応関係を理解し、文字列リテラル(プログラム中に直接記述された文字列)の正しい記述方法を習得する。

手順

  1. printf文におけるダブルクオーテーションの使用例を確認する
  2. ダブルクオーテーションを1つ削除したコードを作成し、コンパイルエラーを確認する
  3. エラーメッセージの内容を記録する

ヒント

クオーテーションの閉じ忘れは、以降のコード全体が文字列として解釈されるため、予期しない大量のエラーが発生することがある。

演習3:乗算記号を正しく使用する

目的

C言語における乗算演算子*の使用方法を理解し、省略によるエラーを回避できるようになる。

手順

  1. 乗算を含む計算式の正しい記述例を確認する
  2. *を省略した場合のエラーを確認する
  3. 数学でabと書く式をC言語でa * bと記述する必要があることを理解する
  4. 自分でa * b * sin(theta)のような式を記述し、正しくコンパイルできることを確認する

ヒント

数学では係数と変数の間の乗算記号を省略できるが、C言語では必ず*を明記する必要がある。2xではなく2 * xと書くこと。

演習4:全角文字の使用制限を理解する

目的

C言語において全角文字が使用できる箇所と使用できない箇所を区別できるようになる。

手順

  1. 全角文字が使用可能な箇所(ダブルクオーテーション内のみ)を確認する
  2. 変数名に全角文字を使用した場合のエラーを確認する
  3. 字下げ(インデント)に全角スペースを使用した場合のエラーを確認する
  4. 自分のエディタで全角スペースが視覚的に判別できるか確認し、必要であれば設定を変更する

ヒント

全角スペースは目に見えないため、エラーの原因として発見しにくい。多くのエディタには全角スペースを可視化する機能があるので、有効にしておくことを推奨する。

演習5:キーワードのスペルを正確に書く

目的

C言語のキーワード(予約語)の正しいスペルを認識し、スペルミスを防ぐ方法を習得する。

手順

  1. 正しいキーワードreturnと誤った記述retrnの違いを確認する
  2. Visual C++等のIDE(統合開発環境)では、キーワードが色分け表示されることを確認する
  3. printfのスペルを間違えた場合のエラーを確認する
  4. 自分の開発環境で、キーワードが正しく色分けされているか確認する

ヒント

キーワードが通常の識別子と同じ色(黒など)で表示されている場合、スペルミスの可能性がある。IDEの色分け機能を活用すること。

演習6:「1」と「l」を区別する

目的

数字の「1」(いち)と小文字アルファベットの「l」(エル)を視覚的に区別し、混同によるバグを防止できるようになる。

手順

  1. 「1」と「l」の見た目の類似性を確認する
  2. 自分の開発環境のフォントで、「1」と「l」が区別しやすいか確認する
  3. 区別しにくい場合、プログラミング用フォント(等幅フォント)への変更を検討する

ヒント

変数名に小文字の「l」を単独で使用することは避けること。「l1」のような命名は特に混乱を招きやすい。

演習7:円記号とバックスラッシュの同一性を理解する

目的

日本語環境における円記号¥と英語環境におけるバックスラッシュ\が同一の文字コードであることを理解する。

手順

  1. 円記号とバックスラッシュの関係を確認する
  2. 自分のキーボードで円記号キーの位置を確認する
  3. printf("hello\n");と記述し、改行が正しく動作することを確認する
  4. 表示が¥nとなっても\nとなっても、動作は同一であることを理解する

ヒント

教科書やWebサイトによって¥\の表記が異なることがあるが、プログラムの動作には影響しない。

3. 計算

資料(スライド): cp-3. 計算 [PDF], [パワーポイント], [HTML]

[例題1(自由落下運動)] [例題2(三角形の面積)] [例題3(sin 関数による三角形の面積)]

演習パート(クリックして展開)

はじめに

このガイドは、授業スライド「cp-3. 計算(Cプログラミング入門)」に基づき、受講者が演習課題を自発的に進めるための手引きである。

演習の前に:基礎知識の確認

変数宣言とは

変数(データを格納するための名前付きの容器)を使用するには、あらかじめ変数宣言(変数の名前と型をコンピュータに伝えること)が必要である。浮動小数(小数点を含む数値)を扱う場合はdouble型を用いる。

double a;    /* 浮動小数データで、変数名は「a」 */

入力文(scanf)

キーボードからデータを読み込むにはscanf関数を用いる。浮動小数データを読み込む場合、書式は%lfを指定し、変数名の前に&を付ける。

scanf("%lf", &a);

出力文(printf)

データやメッセージを画面に表示するにはprintf関数を用いる。浮動小数データを表示する場合、書式は%fを指定する。変数名の前に&は付けない。

printf("a = %f\n", a);

\nは改行を意味する。

代入文

計算結果を変数に格納することを代入という。=は「両辺が等しい」という意味ではなく、「右辺の計算結果を左辺の変数に格納する」という意味である。

menseki = teihen * takasa * 0.5;

四則演算の演算子

演算子 意味
+
-
*
/

ライブラリ関数

数学的な計算を行うライブラリ関数(あらかじめ用意された便利な関数群)を使用するには、プログラムの先頭に以下を記述する。

#include <math.h>

本演習で使用する可能性のある関数は以下のとおりである。

関数 機能
sqrt(x) xの平方根を計算する
fmod(x, y) 浮動小数データの剰余(xをyで割った余り)を計算する

課題1:Heronの公式

目的

三角形の3辺の長さから面積を計算するプログラムを作成することで、複数の変数の使用、数式のプログラムへの変換、およびライブラリ関数(平方根)の活用方法を学ぶ。

使用する公式

Heronの公式は以下のとおりである。

s = (a + b + c) / 2

S = √(s(s-a)(s-b)(s-c))

ここで、a, b, cは三角形の3辺の長さ、sは半周長(3辺の長さの合計の半分)、Sは面積である。

手順

  1. プログラムの先頭に#include <stdio.h>#include <math.h>を記述する。
  2. main関数内で、必要な変数を宣言する。3辺の長さを格納する変数、半周長を格納する変数、面積を格納する変数が必要である。すべて浮動小数型(double)で宣言する。
  3. printf関数でメッセージを表示し、scanf関数で3辺の長さa, b, cをそれぞれ読み込む処理を記述する。
  4. 半周長sを計算する代入文を記述する。
  5. Heronの公式に基づき、面積Sを計算する代入文を記述する。平方根の計算にはsqrt関数を用いる。
  6. printf関数で計算結果を表示する処理を記述する。
  7. プログラムをコンパイルし、実行する。
  8. 動作確認として、既知の三角形(例:3辺が3, 4, 5の直角三角形は面積6)で結果を検証する。

ヒント

ヒント1:数式をプログラムに変換する際、掛け算には必ず*を明示的に記述する。数学の表記では省略されることがあるが、Cプログラミングではs(s-a)のような記述はエラーとなる。s * (s - a)のように記述すること。

ヒント2:sqrt関数の引数(関数に渡す値)には、複雑な式をそのまま記述できる。例題3のsqrt( ( x * x ) + ( y * y ) )の記述方法が参考になる。

ヒント3:変数名は自由に決められるが、何を表しているか分かりやすい名前を付けることで、プログラムの可読性が向上する。

課題2:四則演算

目的

2つの数値に対して複数の演算を行い結果を表示するプログラムを作成することで、四則演算の演算子の使い方と、複数の計算結果を出力する方法を学ぶ。

手順

  1. プログラムの先頭に#include <stdio.h>#include <math.h>を記述する。
  2. main関数内で、必要な変数を宣言する。入力する2つの数を格納する変数と、各演算結果を格納する変数が必要である。
  3. printf関数でメッセージを表示し、scanf関数で2つの数を読み込む処理を記述する。
  4. 和、差、積、商、剰余をそれぞれ計算する代入文を記述する。
  5. printf関数で各計算結果を表示する処理を記述する。
  6. プログラムをコンパイルし、実行する。
  7. 動作確認として、暗算で検証できる簡単な数値(例:10と3)で結果を確認する。

ヒント

ヒント1:浮動小数データの剰余を求めるには、/演算子ではなくfmod関数を用いる。fmod(x, y)はxをyで割った余りを返す。

ヒント2:商を求める際、整数同士の除算では小数点以下が切り捨てられる場合がある。浮動小数(double型)の変数を使用することで、小数点以下も正しく計算される。

ヒント3:出力を見やすくするために、各計算結果を別々の行に表示することを推奨する。\n(改行)を適切に使用すること。

学習のポイント

本講義で習得すべき事項は以下のとおりである。

第一に、プログラムを使って自分の思い通りの計算ができるようになることである。四則演算の演算子とライブラリ関数(三角関数、対数・指数関数、平方根など)を活用することで、様々な数学的計算をプログラムで実現できる。

第二に、見やすいプログラムを書くために、ブロック単位での字下げ(インデント)を行うことである。例題のプログラムを参考に、適切な字下げを心がけること。

4. 条件分岐と場合分け

資料(スライド): cp-4. 条件分岐と場合分け [PDF], [パワーポイント], [HTML]

[例題1(平方根の計算)] [例題2(多分岐の例)] [例題3(うるう年の判定)]

演習パート(クリックして展開)

課題1:2次方程式

目的

if文による条件分岐を用いて、2次方程式の解を判別式の値に応じて正しく求める方法を習得する。複数の条件分岐を組み合わせた実践的なプログラムを作成できるようになる。

手順

  1. 新規Cプログラムファイルを作成し、#include <stdio.h> および #include <math.h> を記述する。
  2. 2次方程式 ax² + bx + c = 0 の係数 a, b, c を格納するための変数を double 型で宣言する。
  3. 解を格納するための変数を宣言する。解が2つ存在する場合に備え、2つの変数を用意する。
  4. scanf 関数を用いて係数 a, b, c の値をキーボードから読み込む。
  5. 判別式(discriminant) D = b² - 4ac を計算する。
  6. 判別式の値に応じて条件分岐を行う。
    • D > 0 の場合:異なる2つの実数解を計算し表示する
    • D == 0 の場合:重解(1つの実数解)を計算し表示する
    • D < 0 の場合:虚数解を計算し表示する
  7. 虚数解の場合、実数部と虚数部を別々の変数で計算し、「実数部 ± 虚数部 i」の形式で表示する。
  8. プログラムをコンパイルし、複数のテストケースで動作を確認する。

ヒント

判別式(discriminant)とは、2次方程式の解の性質を判定するために用いる式 b² - 4ac のことである。この値が正であれば2つの実数解、ゼロであれば重解、負であれば虚数解となる。

平方根の計算には sqrt() 関数を使用する。この関数を使用するためには #include <math.h> が必要である。

虚数解を扱う場合、Cには複素数型もあるが、本課題では実数部用と虚数部用の変数を別々に用意して計算する方法を取る。例えば、解が「2 ± 3i」であれば、実数部として 2、虚数部として 3 を別々の変数に格納する。

発展課題として、a = 0 の場合(1次方程式になる)や、a = 0 かつ b = 0 の場合の処理も検討するとよい。これらの場合分けには、if文の入れ子または else if 文を活用する。

課題2:if文の入れ子

目的

if文を入れ子(ネスト)にして複数の条件を組み合わせる方法を習得する。2つの条件の組み合わせにより4通りの場合分けを行うプログラムを作成できるようになる。

手順

  1. 新規Cプログラムファイルを作成し、必要なヘッダファイルをインクルードする。
  2. 価格と重量を格納する変数を double 型で宣言する(浮動小数データとして扱うため)。
  3. printf でプロンプトを表示し、scanf で価格と重量を読み込む。
  4. 最初のif文で価格が1000以上かどうかを判定する。
  5. 価格が1000以上の場合のブロック内で、重量が100以上かどうかを判定する入れ子のif文を記述する。
    • 重量100以上 → "Unnecessary" を表示
    • 重量100未満 → "Expensive" を表示
  6. 価格が1000未満の場合(else節)のブロック内で、重量が100以上かどうかを判定する入れ子のif文を記述する。
    • 重量100以上 → "Reasonable" を表示
    • 重量100未満 → "Cheap" を表示
  7. プログラムをコンパイルし、4通りすべてのケースでテストを行い、正しいメッセージが表示されることを確認する。

ヒント

if文の入れ子とは、if文のブロック内にさらにif文を記述する構造のことである。本課題では、外側のif文で価格を判定し、内側のif文で重量を判定する構造が自然である。

字下げ(インデント)を正しく行うことで、プログラムの構造が視覚的に把握しやすくなる。入れ子が深くなるほど字下げの重要性が増す。

浮動小数データの読み込みには、scanf の書式指定子として %lf を使用する。

テストケースの例として、(1) 価格1500・重量150、(2) 価格1500・重量50、(3) 価格500・重量150、(4) 価格500・重量50 の4パターンを試すとよい。

課題3:曜日を求めるプログラム

目的

比較演算と論理演算を組み合わせた条件式の記述方法を習得する。ツエラーの公式を用いて、与えられた日付から曜日を計算するプログラムを作成できるようになる。

手順

  1. 新規Cプログラムファイルを作成し、#include <stdio.h> を記述する。
  2. 年、月、日を格納する変数 y, m, d を int 型(整数型)で宣言する。
  3. scanf を用いて年、月、日をキーボードから読み込む。
  4. ツエラーの公式の前提条件に従い、月の値を調整する。1月または2月の場合は、前年の13月または14月として扱う必要がある。具体的には、m が 1 または 2 のとき、y から 1 を引き、m に 12 を加える。
  5. ツエラーの公式を適用して曜日を計算する。式は (y + (y/4) - (y/100) + (y/400) + ((13 * m + 8) / 5) + d) % 7 である。
  6. 計算結果を表示する。結果は 0〜6 の数値で表され、0が日曜日、1が月曜日、以下同様に6が土曜日を表す。
  7. プログラムをコンパイルし、既知の日付(例:自分の誕生日など)でテストして正しい曜日が計算されることを確認する。

ヒント

ツエラーの公式は、グレゴリオ暦における任意の日付の曜日を求める数学的公式である。この公式では「1年の起点を3月とし、月は3月から14月まである」と考える。

月の調整処理には、論理演算子 ||(または)を用いた条件式を使用する。以下のコード例を参考にするとよい。

if ( ( m == 1 ) || ( m == 2 ) ) {
    y = y - 1;
    m = m + 12;
}

整数型変数同士の割り算では、結果が自動的に小数点以下切り捨てとなる。例えば、7 / 4 の結果は 1.75 ではなく 1 となる。ツエラーの公式ではこの性質を利用している。

剰余演算子 % は、割り算の余りを求める演算子である。例えば、17 % 7 の結果は 3 となる。この演算子を用いることで、計算結果を 0〜6 の範囲に収めている。

論理演算子について復習すると、&& は「かつ」(両方の条件が真のとき真)、|| は「または」(少なくとも一方が真のとき真)を意味する。

5. 繰り返し計算

資料(スライド): cp-5. 繰り返し計算 [PDF], [パワーポイント], [HTML]

[例題1(最大公約数の計算)] [例題2(自然数の和)] [例題3(フィボナッチ数列)] [例題4(自然数の和)] [例題5(九九の表)]

演習パート(クリックして展開)

課題1:最大公約数の計算の拡張

目的

while(1)を用いた無限ループの構造を理解し、繰り返し入力を受け付けるプログラムの作成方法を習得する。

手順

  1. 例題1のプログラムを確認し、動作を理解する
  2. 「繰り返し続けるプログラム」の構造(while(1))を確認する
  3. 例題1のプログラム全体をwhile(1)のブロック内に配置する方法を検討する
  4. 「変数の入れ替え」のテクニックを確認する(m≧nの条件を満たすために必要となる場合がある)
  5. プログラムを作成し、複数回の入力で正しく動作することを確認する

ヒント


課題2-1:ベクトルの長さ

目的

while文を用いた累積計算の手法を理解し、ベクトルの長さ(ノルム)を逐次的に計算するプログラムを作成する。

手順

  1. ヒントプログラム(二乗和の計算)を確認する
  2. ベクトルの長さの定義(各成分の二乗和の平方根)を確認する
  3. 値を1つ読み込むごとに、それまでの二乗和を更新する処理を実装する
  4. 二乗和から長さを求めるために、平方根を計算する(sqrt関数を使用、math.hのインクルードが必要)
  5. 1つ読み込むごとに現在の長さを出力するようにする

ヒント


課題2-2:ベクトルの内積

目的

2つの値を交互に読み込みながら累積計算を行う手法を習得する。

手順

  1. 内積の定義(対応する成分の積の和:x1×y1 + x2×y2 + ...)を確認する
  2. ヒントを参考に、2つの値(xとy)を交互に読み込む構造を設計する
  3. 1組(xi, yi)を読み込むごとに、積xi×yiを累積変数に加算する
  4. 1組読み込むごとに現在の内積の値を出力するようにする

ヒント


課題3:三角関数(複素数の累乗)

目的

for文を用いた繰り返し計算と、複素数の乗算アルゴリズムを理解する。ド・モアブルの定理(de Moivre's theorem:(cosθ + i sinθ)^n = cos(nθ) + i sin(nθ))を計算で確認する。

手順

  1. 複素数の積の公式を確認する
  2. ド・モアブルの定理の説明を読み、計算の流れを理解する
  3. θをラジアン単位で読み込む処理を作成する
  4. 複素数z = cosθ + i sinθの実部と虚部を変数に格納する
  5. for文でn=1から100まで繰り返し、各nについて(cosθ + i sinθ)^nを計算する
  6. 繰り返しごとに、現在の累乗結果(実部と虚部)を出力する

ヒント


課題4:平均値と標準偏差

目的

繰り返し文を用いてデータを読み込み、統計量(平均値、標準偏差)を計算するプログラムを作成する。

手順

  1. 平均値と標準偏差の計算式を確認する
    • 平均値:全データの合計 ÷ データ数
    • 標準偏差:各データと平均値の差の二乗の平均の平方根
  2. データの入力方法を設計する(データ数を先に入力する方法、または終了条件を設ける方法)
  3. 繰り返し文でデータを読み込みながら、合計と二乗和を累積する
  4. 全データ読み込み後、平均値を計算する
  5. 標準偏差を計算する(計算式:√(二乗和の平均 - 平均値の二乗)、または各データについて偏差の二乗を計算する方法)
  6. 結果を出力する

ヒント

6. 整数データと浮動小数データ

資料(スライド): cp-6. 整数データと浮動小数データ [PDF], [パワーポイント], [HTML]

[例題1(単純な金種計算)] [例題2(硬貨の金種計算)] [例題3(複利計算)]

演習パート(クリックして展開)

課題1:金種計算

目的

整数データの除算(商)と剰余演算子を組み合わせて、任意の金額を最小枚数の紙幣・硬貨に分解するアルゴリズムを実装する能力を身につける。

手順

  1. 新規Cプログラムファイルを作成し、必要なヘッダファイル(stdio.h)をインクルードする。
  2. main関数内で、金額を格納する整数変数と、各紙幣・硬貨の枚数を格納する整数変数を宣言する。紙幣は1万円札、5千円札、千円札の3種類、硬貨は500円、100円、50円、10円、5円、1円の6種類である。
  3. scanf関数を使用して、キーボードから金額を整数データとして読み込む。
  4. 高額な紙幣から順に枚数を計算する。1万円札の枚数は「金額 / 10000」で求められる。5千円札の枚数は「(金額を10000で割った余り)/ 5000」で求められる。この考え方を千円札以降にも適用する。
  5. 硬貨についても同様に、500円から順に枚数を計算する。例題2のコードを参考に、剰余演算子(%)と除算演算子(/)を組み合わせて各硬貨の枚数を算出する。
  6. printf関数を使用して、各紙幣・硬貨の枚数を表示する。
  7. プログラムをコンパイルし、実行結果例(金額13486円)と同じ出力が得られるか確認する。

ヒント

計算の順序に注意が必要である。高額から低額へ順に処理することで、各段階での「残り金額」を剰余演算子で自然に求められる。「(金額 % 上位金種)/ 当該金種」というパターンが繰り返し使われていることがわかる。

よくある間違いとして、剰余を取る対象の金額を間違えるケースがある。例えば千円札の枚数を求める際、「金額 % 10000」ではなく「金額 % 5000」を使用する点に注意する。

課題2:時間の換算

目的

整数データの除算と剰余演算子を使用して、単一の単位(秒)で表された時間を複数の単位(時・分・秒)に変換するプログラムを作成する。課題1で学んだ金種計算と同様の考え方を、異なる問題領域に応用する。

手順

  1. 新規Cプログラムファイルを作成し、stdio.hをインクルードする。
  2. main関数内で、入力する秒数xと、計算結果を格納するh(時)、m(分)、s(秒)の整数変数を宣言する。
  3. scanf関数を使用して、秒数xを読み込む。
  4. 時間h、分m、秒sを計算する。1時間は3600秒、1分は60秒であることを利用する。まずhを求め、次にhを引いた残りからmを求め、最後にsを求める。
  5. printf関数を使用して、計算結果を「h時、m分、s秒」の形式で表示する。
  6. プログラムをコンパイルし、例(x=3723のとき、1h, 2m, 3s)で正しく動作するか確認する。

ヒント

金種計算と本質的に同じ構造の問題である。紙幣・硬貨を「時・分・秒」に、金額を「総秒数」に置き換えて考えるとよい。

計算式の組み立て方として、時間hは「x / 3600」、分mは「(x % 3600)/ 60」、秒sは「x % 60」となる。3600という数値は60秒×60分から導かれる。

検算として、h×3600 + m×60 + s が元のxと一致するか確認すると、計算ロジックの正しさを検証できる。

7. 配列

資料(スライド): cp-7. 配列 [PDF], [パワーポイント], [HTML]

[例題1(月の日数)] [例題2(ベクトルの内積)] [例題3(合計点と平均点)] [例題4(棒グラフを描く)] [例題5(行列の和)]

演習パート(クリックして展開)

課題1:多項式の計算(Horner法)

目的

配列を用いて多項式の係数を格納し、Horner法(多項式を効率的に計算するアルゴリズム)による計算手法を習得する。

手順

  1. Horner法の計算原理を理解する。Horner法とは、多項式 f(x) = a₀ + a₁x + a₂x² + ... + aₙxⁿ を、f(x) = a₀ + (a₁ + (a₂ + ... + (aₙ₋₁ + aₙ・x)x...)x)x という形に変形し、内側から順に計算する手法である。
  2. サイズ21の配列を宣言し、係数a₀からaₙを格納できるようにする。
  3. 次数nを読み込む処理を作成する。nが20を超えた場合はエラーメッセージを表示して終了する。
  4. 係数a₀からaₙを読み込み、配列に格納する。
  5. xの値を読み込む処理を作成する。
  6. フローチャートを参考に、Horner法による計算処理を実装する。sumの初期値をa[n]とし、iをn-1から0まで減少させながら計算を繰り返す。
  7. xを繰り返し入力できるよう、ループ構造を追加する。
  8. コンパイル・実行し、手計算の結果と照合して検証する。

ヒント


課題2:エラトステネスのふるい

目的

配列を用いて数値の状態(素数か否か)を管理し、古典的なアルゴリズムであるエラトステネスのふるい(素数を効率的に列挙する手法)を実装する。

手順

  1. エラトステネスのふるいの原理を理解する。2から順に、その倍数を消していくことで素数を残す手法である。
  2. サイズ100の整数型配列を宣言する。
  3. 配列の全要素を1で初期化する(すべての数を素数の候補とする)。添字0と添字1は素数ではないため、0を代入しておく。
  4. 2から開始し、その数が素数(配列の値が1)であれば、その倍数すべてに0を代入する。
  5. この操作を10(100の平方根)まで繰り返す。10を超えたら倍数消去の操作を終了してよい。
  6. 配列を走査し、値が1である添字を素数として表示する。
  7. コンパイル・実行し、既知の100以下の素数(2, 3, 5, 7, 11, 13, ...)と照合する。

ヒント


課題3:行列の積

目的

2次元配列(行と列の2つの添字を持つ配列)を用いて行列を表現し、行列の積の計算アルゴリズムを実装する。

手順

  1. 2次元配列の宣言方法と使用方法を理解する。
  2. 3×3の2次元配列を3つ宣言する。2つは入力行列用、1つは結果格納用である。
  3. 2つの入力行列に初期値を設定する。
  4. 行列の積の計算式を確認する。結果行列のi行j列の要素は、1つ目の行列のi行と2つ目の行列のj列の各要素を掛けて合計した値である。
  5. 3重ループ(i, j, kの3つのループ変数)を用いて計算を実装する。外側2つのループで結果行列の各要素の位置を決め、最内ループで積和計算を行う。
  6. 結果を表示する処理を作成する。
  7. コンパイル・実行し、手計算または他のツールでの計算結果と照合する。

ヒント

8. 関数

資料(スライド): cp-8. 関数 [PDF], [パワーポイント], [HTML] 関数

[例題1(棒グラフ)] [例題2(月の日数)] [例題3(1か月分のカレンダー)] [例題4(月初めの曜日)] [例題5(カレンダー)]

演習パート(クリックして展開)

例題1:棒グラフ

目的

関数の基本構造(頭部と本体)、関数呼び出しの流れ、引数と仮引数によるデータの受け渡しを理解する。

手順

  1. 課題内容を確認する。整数lenを受け取り、len個の「*」を横に並べて表示し、最後に改行する関数bar()を作成することが目標である。
  2. プログラムコードを参照し、bar関数の構造を確認する。頭部(void bar( int len ))と本体(中括弧内の処理)の区別を意識する。
  3. bar関数の処理内容を理解する。forループでlen回「*」を出力し、最後にprintf("¥n")で改行している。
  4. main関数の処理を確認する。scanfで整数を読み込み、bar(len)で関数を呼び出している。
  5. プログラム実行順を追跡し、main関数からbar関数への制御の移動とreturnによる戻りを確認する。
  6. 自分でプログラムを入力し、コンパイル・実行する。len=5を入力したとき「*****」と表示されることを確認する。
  7. lenの値を変えて実行し、動作を検証する。

ヒント

引数(関数呼び出し時に渡す値)と仮引数(関数定義で受け取る変数)は名前が同じでも別の変数である。void型の関数はreturn文で値を返さない。

例題2:月の日数

目的

return文を使って関数から値を返す方法、複数の引数を持つ関数の定義と呼び出しを理解する。

手順

  1. 課題内容を確認する。年yと月mを受け取り、その月の日数を返す関数num_of_day()を作成することが目標である。
  2. うるう年の判定条件を確認する。2月かつ(400で割り切れる、または100で割り切れず4で割り切れる)場合は29日となる。
  3. プログラムコードを参照する。配列num_days[]に各月の日数(平年)が格納されている点に注目する。
  4. return文の使い方を確認する。うるう年の2月ならreturn 29;、それ以外ならreturn num_days[m-1];で日数を返している。
  5. main関数でn = num_of_day(y, m);と記述することで、関数の戻り値を変数nに代入している点を確認する。
  6. 自分でプログラムを入力し、コンパイル・実行する。y=2001, m=11で30が表示されることを確認する。
  7. うるう年(y=2000, m=2など)と平年(y=2001, m=2など)で動作を検証する。

ヒント

配列のインデックスは0から始まるため、m月の日数はnum_days[m-1]で取得する。関数の型がintの場合、return文で整数値を返す必要がある。

例題3:1か月分のカレンダー

目的

複数の引数を受け取り、条件分岐とループを組み合わせた処理を関数として実装する方法を理解する。

手順

  1. 課題内容を確認する。日数(28〜31)と曜日(0〜6、0が日曜日)を受け取り、カレンダーを表示する関数print_calendar()を作成することが目標である。
  2. プログラムコードを参照する。処理は大きく3つの部分に分かれている。引数の妥当性チェック、曜日分の空白表示、日付の出力である。
  3. 引数チェックの部分を確認する。num_daysが28未満または31超過、youbiが0未満または6超過の場合は何もせずreturnしている。
  4. 曜日分の空白表示を確認する。youbi * 3個の空白を出力することで、月初めの曜日位置まで空けている。
  5. do-whileループで日付を1から順に出力し、土曜日(x==6)のたびに改行する処理を確認する。
  6. フローチャートを参照し、処理の流れを視覚的に理解する。
  7. 自分でプログラムを入力し、コンパイル・実行する。num_days=30, youbi=4で、実行結果と一致することを確認する。

ヒント

曜日を表す変数xは0〜6の範囲で循環する。土曜日(x==6)の次は日曜日(x=0)にリセットする。日付は2桁幅で表示し(%2d)、間に空白を1つ置く。

例題4:月初めの曜日

目的

ツェラーの公式(Zeller's congruence)を用いて日付から曜日を計算する方法、および関数から別の関数を呼び出す構造を理解する。

手順

  1. 課題内容を確認する。年yと月mから月初め(1日)の曜日を求める関数first_day()を作成することが目標である。
  2. プログラムコードを参照する。zeller関数とfirst_day関数の2つが定義されている。
  3. zeller関数の処理を確認する。1月と2月は前年の13月、14月として扱うため、y = y - 1とm = m + 12の補正を行っている。
  4. ツェラーの公式の計算式を確認する。戻り値は0〜6の整数で、0が日曜日、1が月曜日、…、6が土曜日を表す。
  5. first_day関数はzeller(y, m, 1)を呼び出すことで、その月の1日の曜日を返している。関数が別の関数を呼び出す構造に注目する。
  6. 自分でプログラムを入力し、コンパイル・実行する。既知の日付(例:2001年11月1日は木曜日=4)で検証する。

ヒント

ツェラーの公式では1月・2月を特別扱いする必要がある。first_day関数はzeller関数を利用する形で実装されており、コードの再利用性が高まっている。

例題5:カレンダー

目的

例題2〜4で作成した関数を統合し、複数の関数から構成されるプログラムの設計方法を理解する。

手順

  1. 課題内容を確認する。年と月を入力すると、その月のカレンダーを表示するプログラムを作成することが目標である。
  2. 関数分割の考え方を確認する。本体(main関数)、月の日数(num_of_day)、月初めの曜日(first_day/zeller)、1か月分のカレンダー(print_calendar)の4つの役割に分割されている。
  3. 関数間のデータの流れを理解する。main関数が他の関数を呼び出し、結果を受け取って次の関数に渡す構造になっている。
  4. コメントによる設計方法を確認する。まずコメントで処理の概要を書き、その後に具体的なコードを記述している。
  5. main関数のコードを参照し、例題2〜4の関数がどのように呼び出されているかを確認する。
  6. 例題2、3、4で作成した関数をすべて1つのファイルにまとめ、main関数を追加する。
  7. コンパイル・実行し、年と月を入力してカレンダーが正しく表示されることを確認する。

ヒント

プログラムを作成する際は、まずコメントで処理の流れを書き、次にコードを埋めていく方法が有効である。関数を分割することで、それぞれの関数を独立して開発・テストできる。

9. 再帰関数

資料(スライド): cp-9. 再帰関数 [PDF], [パワーポイント], [HTML]

[例題1(スタック)] [例題2(再帰関数による総和)] [例題3(ハノイの塔のパズル)]

演習パート(クリックして展開)

課題1:スタック操作の例外処理

目的

スタック(データを後入れ先出しで管理するデータ構造)の境界条件(空・満杯)を判定し、適切なエラーハンドリング(例外処理)を実装する技法を習得する。

手順

  1. スタックのプッシュ(データを積む操作)とポップ(データを取り出す操作)の動作原理を確認する
  2. 配列によるスタック実現の仕組みを理解する。特に以下の点を把握する:
    • stack[0]がスタックの底である
    • stack[top-1]がスタックの頂上である
    • topは「空き部分の底」の位置を示す
  3. push関数とpop関数のコードを書き写し、動作を確認する
  4. 以下の条件を理解する:
    • スタックが空のとき:topの値は0である
    • スタックが満杯のとき:topの値は配列のサイズ(この例では100)である
  5. pop関数を修正する:topが0のときは「Stack empty」を表示し、ポップ処理を行わないようにする
  6. push関数を修正する:topが配列のサイズ(100)に等しいときは「Stack full」を表示し、プッシュ処理を行わないようにする
  7. main関数を参考に、例外処理が正しく動作するかテストする

ヒント

  • スタックポインタ(現在の空き部分の底を指す変数)topの値は「スタック内に格納されているデータの個数」と等しい
  • pop関数では、topの値を減らす処理の前に空かどうかの判定を行う
  • push関数では、データを格納する処理の前に満杯かどうかの判定を行う
  • エラー発生時の戻り値をどうするか検討する。pop関数でエラー時に何を返すかは設計上の判断となる

エラー発生時の戻り値に関する補足

課題1では、エラー発生時にpop関数が何を返すべきかという設計上の判断が求められる。主なアプローチとして以下の3つがある。

第一に、特定の値(例:-1)を返す方法がある。実装が簡単であるが、-1が有効なデータである場合に区別できないという欠点がある。

第二に、グローバル変数(大域変数)やフラグ変数を用いてエラー状態を別途管理する方法がある。戻り値とエラー状態を分離できるが、呼び出し側でフラグの確認が必要となる。

第三に、戻り値を返さずエラーメッセージの表示のみ行う方法がある。本課題の指示は「エラーメッセージを表示すること」のみを要求しているため、この方法でも課題の要件は満たせる。

10. 末尾再帰関数と多重再帰関数

資料(スライド): cp-10. 末尾再帰関数と多重再帰関数 [PDF], [パワーポイント], [HTML]

[例題1(フィボナッチ数列)] [例題2(McCarthyの91関数)] [例題3(Ackermann関数)] [例題4(総和を求める末尾再帰関数)]

演習パート(クリックして展開)

課題1:フィボナッチ数列の特性

目的

再帰によるフィボナッチ数列の計算において、引数の増大に伴う計算量の爆発的増加を体験し、多重再帰関数(関数本体に2個以上の再帰呼び出しを含む関数)の計算効率について理解する。

手順

  1. fib関数のソースコードをテキストエディタに入力し、Cプログラムとして保存する
  2. プログラムをコンパイルし、実行可能ファイルを生成する
  3. n=5, 10, 15, 20, 25, 30, 35, 40と徐々に値を大きくしながら実行し、出力結果と体感的な計算時間を記録する
  4. 計算が実用的な時間内に終了しなくなる境界を探す
  5. 繰り返し版プログラムについても同様に実行し、計算時間を比較する
  6. 再帰版と繰り返し版で計算時間に差が生じる理由を、実行順の図を参考に考察する

ヒント

再帰版では同じ値のfib(k)が何度も重複して計算される。例えばfib(5)を計算する際、fib(2)は3回、fib(1)は5回計算される。この重複計算が引数の増大とともに指数関数的に増加するため、計算時間が急激に長くなる。

課題2:McCarthyの91関数の特性

目的

McCarthyの91関数の再帰的定義と実際の出力値の関係を実験的に理解し、再帰的定義が必ずしも複雑な出力を生むわけではないことを確認する。

手順

  1. mc91関数のソースコードをテキストエディタに入力し、Cプログラムとして保存する
  2. プログラムをコンパイルし、実行可能ファイルを生成する
  3. 0, 50, 90, 99, 100, 101, 110など、様々な値を入力し、出力結果を記録する
  4. 入力値と出力値の関係について、規則性を考察する
  5. 非再帰関数m91のコードでmc91関数を置き換える
  6. 同じ入力値で実行し、出力結果を比較する
  7. 両者の出力が一致するか確認し、McCarthyの91関数の本質的な意味を考察する

ヒント

入力値を100以下と101以上に分けて結果を整理すると、規則性が見えやすくなる。100以下の入力に対する出力には特徴的なパターンが存在する。

課題3:Ackermann関数の特性

目的

Ackermann関数(原始帰納的でない関数の代表例)の計算量が引数によって急激に増大することを体験し、再帰関数の計算複雑性を理解する。

手順

  1. ack関数のソースコードをテキストエディタに入力し、Cプログラムとして保存する
  2. プログラムをコンパイルし、実行可能ファイルを生成する
  3. m=0, 1, 2, 3の各値について、n=0, 1, 2, 3, 4と変化させながら実行し、出力結果を記録する
  4. 各実行において、体感的な計算時間(即座に終わる、数秒かかる、終わらない等)も記録する
  5. mとnの値を徐々に大きくし、プログラムが実用的な時間内に終了しなくなる境界を探す
  6. 再帰回数の表と自分の実験結果を比較し、計算量の増大パターンを考察する

ヒント

m=4以上では非常に小さなnでも計算が終わらなくなる可能性がある。m=3, n=10程度でも相当な時間がかかるため、実験は小さな値から慎重に進めること。計算が終わらない場合はCtrl+Cでプログラムを強制終了できる。

課題4:戻り値の表示による計算の様子の調査

目的

再帰関数の実行過程を可視化することで、再帰呼び出しがどのような順序で行われ、どのように値が返されるかを具体的に理解する。

手順

  1. フィボナッチ数列のfib関数を修正する。return文の直前にprintf文を追加し、戻り値を表示するようにする
  2. 修正したプログラムをコンパイルし、n=3, 4, 5程度の小さな値で実行する
  3. 表示された戻り値の順序と、実行順の図を比較し、再帰の流れを確認する
  4. 同様に、mc91関数を修正し、n=95程度の値で実行して計算の様子を観察する
  5. ack関数についても同様に修正し、m=1, n=2程度の小さな値で実行する
  6. 各関数の再帰呼び出しのパターンの違いを考察する

ヒント

printf文は例えばprintf("fib returning %d\n", 戻り値);のような形式で、return文の直前に挿入する。Ackermann関数では引数m, nも同時に表示すると、どの呼び出しからの戻りかが分かりやすくなる。大きな引数では出力が膨大になるため、小さな値から始めること。

課題5:階乗関数の末尾再帰への書き換え

目的

繰り返し文(forループ)で書かれた関数を末尾再帰関数(tail recursive function:関数本体の最後でのみ再帰呼び出しを行う形式の関数)に変換する技法を習得する。

手順

  1. 階乗関数factorialの動作を理解する。この関数は1からnまでの整数を順に掛け合わせている
  2. 総和を求める末尾再帰関数sumの構造を確認する。途中結果を引数sとして渡している点に注目する
  3. 階乗関数を末尾再帰形式に書き換える。関数のシグネチャは例えばint factorial_tail(int n, int s)のように、途中結果を保持する引数を追加する
  4. ヒントを参考に、return factorial_tail(n-1, n*s);のような形式で再帰呼び出しを行う
  5. main関数を作成し、factorial_tail関数を呼び出す。初期呼び出し時の途中結果sの値を適切に設定する
  6. n=5, 10などの値で実行し、正しい階乗値(5!=120, 10!=3628800)が得られることを確認する

ヒント

末尾再帰では、再帰呼び出しの戻り値をそのままreturnする形式にする必要がある。return n * factorial(n-1);は末尾再帰ではない(戻り値に対してさらにn倍の演算を行っているため)。途中結果を引数で渡すことで、この掛け算を再帰呼び出しの前に行うようにする。

課題6:フィボナッチ数列の末尾再帰化

目的

複数の前の値を参照する再帰関数(多重再帰関数)を末尾再帰形式に変換する技法を習得する。

手順

  1. 再帰版フィボナッチ関数fibの動作を確認する。fib(n-1)とfib(n-2)の2つの再帰呼び出しを行っている点に注目する
  2. ヒントを確認する。末尾再帰版では、1つ前の値fk-1と2つ前の値fk-2を引数として渡す
  3. 末尾再帰版の関数シグネチャを設計する。例えばint fib_tail(int n, int fn1, int fn2)のように、現在の計算位置nと、直前の2つのフィボナッチ数を引数とする
  4. 基底条件(再帰を終了する条件)と再帰呼び出しの処理を実装する
  5. main関数を作成し、適切な初期値でfib_tail関数を呼び出す
  6. n=5, 10, 20などの値で実行し、正しいフィボナッチ数が得られることを確認する

ヒント

繰り返し版フィボナッチ数列のプログラムが参考になる。繰り返し版ではfn1(1つ前の値)とfn2(2つ前の値)を変数で保持しながら計算を進めている。この2つの値を再帰関数の引数として渡すことで、末尾再帰の形式に変換できる。

11. ポインタ

資料(スライド): cp-11. ポインタ [PDF], [パワーポイント], [HTML]

[例題1(変数のメモリアドレス表示)] [例題2(配列のメモリアドレス)] [例題3(2次元配列のメモリアドレス)] [例題4(棒グラフを表示する関数)] [例題5(2次元配列の受け渡し)] [例題6(局所変数と仮引数のメモリアドレス)] [例題7(関数へのポインタ渡し)]

演習パート(クリックして展開)

課題1:ベクトルの内積

目的

配列を関数に渡す方法を理解し、2つの3次元ベクトルの内積を計算する関数を実装できるようになる。

手順

  1. 配列のメモリアドレスの仕組みを復習する。
  2. 関数への配列の受け渡し方法を理解する。bar_graph関数の実装例を参考にする。
  3. product関数の仕様を確認する。引数として2つの3次元ベクトル(double型配列)を受け取り、内積(double型)を返り値として返す。
  4. main関数で2つの3次元ベクトルを宣言し、product関数を呼び出すコードを記述する。
  5. プログラムをコンパイルし、実行結果が正しいことを手計算と比較して確認する。

ヒント

課題2:2つの行列の和

目的

2次元配列を関数に渡す方法を理解し、行列の和を計算する関数を実装できるようになる。

手順

  1. 2次元配列のメモリ上の配置を復習する。
  2. 2次元配列を関数に渡す方法を理解する。print_matrix関数の実装例を参考にする。
  3. add_matrix関数の仕様を確認する。引数として、和を求める2つの行列、行列の縦横の大きさ、結果を格納する行列を受け取る。
  4. x[i*m+j]という記法で2次元配列の要素にアクセスする方法を理解する。
  5. main関数で3つの2次元配列(入力2つ、結果格納用1つ)を宣言し、add_matrix関数を呼び出すコードを記述する。
  6. プログラムをコンパイルし、実行結果が正しいことを確認する。

ヒント

課題3:スタック

スタックとは

スタックとは、データを「後入れ先出し」(LIFO: Last In, First Out)の順序で管理するデータ構造である。データの追加(push)は常に先頭に行い、データの取り出し(pop)も常に先頭から行う。本や皿を積み重ねる様子に例えられ、最後に積んだものを最初に取り出す仕組みである。

目的

ポインタ変数(メモリアドレスを格納する変数)を使った関数へのデータ受け渡しを理解し、スタックの基本操作を実装できるようになる。

手順

  1. 関数へのポインタ渡しの仕組みを理解する。int_count関数の例を参考にする。
  2. ポインタ変数の宣言方法と操作方法を理解する。
  3. ptr++の意味を理解する。
  4. main関数内で、スタック用の配列とスタックポインタ(現在のスタックの先頭位置を示す変数)を宣言する。大域変数(グローバル変数:関数の外で宣言される変数)は使用しない。
  5. push関数を作成する。スタックにデータを追加し、スタックポインタを増加させる。スタックポインタはポインタ変数を使って渡す。
  6. pop関数を作成する。スタックからデータを取り出し、スタックポインタを減少させる。
  7. スタックの中身を表示する関数を作成する。
  8. main関数でこれらの関数を呼び出し、push、pop、表示が正しく動作することを確認する。

ヒント

12. 文字列

資料(スライド): cp-12. 文字列 [PDF], [パワーポイント], [HTML]

[例題1(文字列と長さの表示)] [例題2(文字列のコピー)] [例題3(文字列の連結)] [例題4(文字列の比較)] [例題5(文字列の検索)] [例題6(文字列のメモリアドレス)] [例題7(曜日の表示)]

演習パート(クリックして展開)

課題1:文字列の逆転(回文の作成)

目的

文字配列の添字を用いて個々の文字にアクセスし、文字列を操作する技術を習得する。

手順

  1. 例題1を読み、文字配列の宣言方法、scanfによる文字列読み込み、文字列末尾を表す特別な文字(\0、ヌル文字と呼ぶ)の役割を確認する。
  2. 半角文字列を格納できる配列を宣言する。回文を格納するため、元の文字列の2倍以上のサイズを確保する。
  3. scanfで文字列を読み込む。
  4. strlen関数で読み込んだ文字列の長さを取得する。
  5. 元の文字列の末尾から先頭に向かって1文字ずつ読み取り、元の文字列の後ろに連結する。添字を使ってx[i] = x[j];の形式で文字をコピーする。
  6. 連結が完了したら、回文の末尾に\0をセットする(例:x[10]='\0';)。
  7. 完成した回文をprintfで表示する。

ヒント

課題2:文字列を扱う関数(辞書順ソート)

目的

文字列を引数として受け取る関数を自作し、strcmp関数を用いた文字列比較の手法を習得する。

手順

  1. 例題4を読み、strcmp関数の戻り値の意味を確認する。戻り値が負なら第1引数が小さく、0なら等しく、正なら第1引数が大きい。
  2. 例題6を読み、関数への文字列の受け渡し方法(char* sの形式)を確認する。
  3. 3つの文字列を受け取る関数のプロトタイプを設計する。例:void sort_and_print(char* s1, char* s2, char* s3)
  4. 関数内でstrcmpを使い、3つの文字列の大小関係を判定する。
  5. 判定結果に基づき、辞書順(アルファベット順)に3つの文字列を表示する。
  6. main関数で3つの文字列を読み込み、作成した関数を呼び出す。
  7. 複数の入力パターンで動作を確認する。

ヒント

課題3:小数部分の抜き出し

目的

文字列検索の技法を応用し、特定の文字(小数点)を基準に文字列を分割する処理を習得する。

手順

  1. 例題5を読み、strstr関数の使用方法を確認する。strstr関数は、検索文字列が見つかった位置のポインタ(メモリアドレス)を返し、見つからない場合はNULLを返す。
  2. 小数付きの数(例:"18.256")を文字列として読み込むため、%sを使用する。
  3. strstr関数を使って小数点"."の位置を検索する。
  4. 小数点が見つかった場合、その次の位置から文字列の末尾までが小数部分となる。ポインタに1を加えると次の文字を指す。
  5. 小数部分をstrcpyで別の配列にコピーするか、直接printfで表示する。
  6. 小数点が含まれない入力への対応も検討する。

ヒント

13. 構造体

資料(スライド): cp-13. 構造体 [PDF], [パワーポイント], [HTML]

[例題1(住所録)] [例題2(構造体と関数)] [例題3(構造体のリスト)]

演習パート(クリックして展開)

課題1:住所録の条件検索

目的

構造体の配列とポインタ渡しを用いて、条件に基づくデータの選択表示を実装する技術を習得する。

手順

  1. 例題2のプログラムを理解する。構造体Personの定義、read_person関数、print_person関数の仕組みを確認する。
  2. 新たに「20歳以上のデータだけを表示する関数」を設計する。関数名は任意とし、引数として構造体の配列とその要素数を受け取る方式、または構造体へのポインタを渡す方式を検討する。
  3. 関数内部では、構造体のageメンバ(年齢を格納するメンバ)を参照し、条件判定(age >= 20)を行う。
  4. main関数から3人分の住所録を読み込み、作成した関数を呼び出す。
  5. 20歳未満のデータと20歳以上のデータを混在させて入力し、20歳以上のデータのみが表示されることを確認する。

ヒント

  • 関数への引数としてポインタを渡す場合、呼び出し側では「&」を付けてメモリアドレスを渡す。
  • 配列の全要素を走査するにはforループを使用する。条件判定にはif文を組み合わせる。
  • 表示にはprint_person関数を再利用できる。

課題2:日付

目的

構造体を用いて日付データを表現し、入力データに基づく処理(カレンダー表示)を実装する技術を習得する。

手順

  1. 日付を表す構造体を設計する。年(int型)、月(int型)、日(int型)の3つのメンバを持つ構造体を定義する。
  2. 日付を読み込む関数を作成する。scanf関数を用いて年、月、日を入力させる。
  3. 入力された年月に対応する1か月分のカレンダーを表示する関数を作成する。
  4. カレンダー表示には、該当月の日数と、月初日の曜日の計算が必要となる。うるう年の判定(2月の日数が28日か29日か)も考慮する。
  5. main関数で日付を読み込み、カレンダー表示関数を呼び出す。

ヒント

  • 各月の日数は配列で管理すると便利である(例:int days[] = {31, 28, 31, 30, ...})。
  • うるう年の条件は「4で割り切れる、かつ100で割り切れない、または400で割り切れる」である。
  • 課題では「日」の入力も求められているが、カレンダー表示自体には月初日の曜日が重要となる。

補足:曜日計算

曜日の計算には基準日を用いる方法がある。例えば、2000年1月1日は土曜日である。この基準日から目的の日付までの経過日数を計算し、7で割った余りから曜日を求める。経過日数の計算では、年ごとの日数(平年365日、うるう年366日)と月ごとの日数を累積する。

課題3:住所録(再帰呼び出し)

目的

リスト構造の走査を再帰呼び出し(関数が自分自身を呼び出す手法)で実装する技術を習得する。

補足:再帰呼び出し

再帰呼び出しとは、関数が自分自身を呼び出す手法である。再帰関数には終了条件と再帰ステップの2つの要素が必要となる。終了条件はそれ以上再帰しない場合を定義し、再帰ステップでは問題を小さくして自分自身を呼び出す。リスト走査では、現在のノードがNULLなら終了し、そうでなければ現在のノードを処理して次のノードで再帰呼び出しを行う。

手順

  1. 例題3のプログラムを理解する。特にprint_data関数のdo-whileループによるリスト走査の仕組みを確認する。
  2. ループによる走査を再帰呼び出しに置き換える。新たな表示関数を設計し、引数として現在のノード(PersonNode型へのポインタ)を受け取る。
  3. 再帰関数の構造は次のとおりである。現在のノードがNULLなら何もせずreturnする(終了条件)。NULLでなければ、現在のノードのデータを表示し、次のノード(current->next)を引数として自分自身を呼び出す。
  4. print_data関数から、リストの先頭ノード(a->top)を引数として再帰関数を呼び出すように修正する。
  5. 複数件のデータを入力し、すべてのデータが表示されることを確認する。

ヒント

  • ヒント図を参照する。再帰呼び出しでは、1番目の要素を表示後、2番目以降の表示を再帰呼び出しに委ねる。
  • 終了条件(NULLチェック)を最初に記述しないと無限ループに陥る。
  • 再帰呼び出しの順序を変えると、表示順序が変わる(先に表示してから再帰呼び出しするか、再帰呼び出し後に表示するか)。

14. ファイル処理

資料(スライド): cp-14. ファイル処理 [PDF], [パワーポイント], [HTML]

[例題1(1行単位のファイル読み込み)] [例題2(ファイルからのデータ読み込み)] [例題3(1行単位のファイル書き出し)] [例題4(3行目を2回読み込む)]

演習パート(クリックして展開)

課題1:ファイルのコピー

目的

ファイルの読み込みと書き出しを組み合わせ、ファイルをコピーするプログラムを作成することで、ファイル処理の基本的な流れを習得する。

手順

  1. コピー元となるテキストファイルをZドライブに作成する。ファイル名と内容は任意でよい。
  2. 新規のCプログラムファイルを作成し、#include <stdio.h>を記述する。
  3. main関数内で、2つのファイルポインタ変数を宣言する。1つは読み込み用、もう1つは書き出し用である。ファイルポインタ(FILE型へのポインタ)の宣言方法は例題1および例題2を参照すること。
  4. fopen関数を使用して、コピー元ファイルを読み込みモード("r")でオープンする。オープンモードの詳細は例題3を参照すること。
  5. fopen関数を使用して、コピー先ファイルを書き出しモード("w")でオープンする。
  6. 各fopen関数の戻り値がNULLかどうかを確認し、オープン失敗時のエラー処理を記述する。
  7. while文を使用して、fgets関数でコピー元ファイルから1行ずつ読み込む。fgets関数の使い方は例題1を参照すること。
  8. 読み込んだ1行をfprintf関数でコピー先ファイルに書き出す。fprintf関数の使い方は例題3を参照すること。
  9. ファイルの終わり(EOF:End Of File)に達したらループを終了する。
  10. fclose関数で両方のファイルをクローズする。
  11. プログラムをコンパイルし、実行する。
  12. コピー先ファイルを開き、コピー元ファイルと内容が一致していることを確認する。

ヒント

考察のポイントとして、例題1と例題3のコードを並べて比較すると、読み込みと書き出しの対応関係が理解しやすい。

よくある間違いの回避について、以下の点に注意すること。ファイルポインタ変数は2つ必要であり、1つの変数を使い回すことはできない。オープンモードを間違えると、読み込むべきファイルが空になる、または書き出しができないといった問題が発生する。fgets関数の第2引数には、読み込み先配列のサイズを指定する。配列サイズが100ならば99文字まで読み込める。

15. 疑似乱数とシミュレーション

資料(スライド): cp-15. 疑似乱数とシミュレーション [PDF], [パワーポイント], [HTML]

[例題1(疑似乱数)] [例題2(ランダムウオーク)] [例題3(じゃんけんゲーム)] [例題4(モンテカルロ法による数値積分)]

演習パート(クリックして展開)

課題1:ランダムウオークの統計

目的

ランダムウオークのシミュレーションを多数回実行し、統計的な平均値を算出する方法を学ぶ。疑似乱数を用いた繰り返し実験の基本を理解する。

手順

  1. 例題2のランダムウオークプログラムを理解する。特に、酔っ払いの位置を表す変数nの初期値と終了条件を確認する。
  2. 1回のランダムウオークで「何歩歩いたか」をカウントする変数を追加する。while文のループ内で歩数をカウントする仕組みを考える。
  3. ランダムウオークを1000回繰り返すための外側のループ(for文)を追加する。
  4. 各回の歩数を合計するための変数を用意する。この変数はdouble型を使用する。
  5. 1000回の繰り返しが終了した後、合計歩数を1000で割り、平均歩数を計算する。
  6. 各繰り返しの開始時に「n = 5;」を実行し、酔っ払いを道の中央に戻すことを忘れない。

ヒント

浮動小数(double)を使うこと。平均値は小数になる可能性があるため、合計歩数や平均歩数を格納する変数はdouble型で宣言する。整数同士の除算では小数部分が切り捨てられるため、キャスト(型変換)に注意する。

課題2:じゃんけん結果集計

目的

ユーザの入力パターンを記録・集計する方法を学ぶ。2次元配列を用いた集計処理の実装を理解する。

手順

  1. 例題3のじゃんけんプログラムを理解する。特に、hantei配列による勝敗判定の仕組みと、do-while文による繰り返し処理を確認する。
  2. 「前回の入力」と「今回の入力」の組み合わせを記録するための2次元配列(3×3)を用意する。
  3. ユーザが入力するたびに、「前回の入力」と「今回の入力」の組み合わせに対応する配列要素をインクリメント(+1)する。
  4. 「前回の入力」を保持するための変数を追加する。初回入力時には「前回の入力」が存在しないため、2回目以降から集計を開始する。
  5. ゲーム終了時(x == 3のとき)に、集計結果を表示する処理を追加する。9パターンすべてを出力する。

ヒント

「パーの次にパー」「パーの次にグー」など9パターンがある。これは3×3の2次元配列で管理できる。配列のインデックスは、パー=0、グー=1、チョキ=2に対応する。初回入力時に「前回の入力」が未定義となる点に注意し、フラグ変数や初期値の工夫で対処する。

課題3:円周率の計算

目的

モンテカルロ法(Monte Carlo method:疑似乱数を用いて数値計算を行う手法)を用いて円周率を近似的に求める方法を学ぶ。確率と面積の関係を理解する。

手順

  1. 例題4のモンテカルロ法プログラムを理解する。特に、ランダムな座標を発生させ、関数以下かどうかを判定する仕組みを確認する。
  2. ヒント図を確認する。1×1の正方形内にランダムな座標(x, y)を発生させる。
  3. 発生させた座標が、原点を中心とする半径1の四分円(quarter circle)の内部にあるかどうかを判定する。判定条件は「x² + y² < 1」である。
  4. 多数の点を発生させ、四分円内に入った点の割合を求める。この割合はπ/4に近似する。
  5. 求めた割合を4倍することで、円周率πの近似値を得る。

ヒント

円内である確率はπ/4である。これは、正方形の面積が1×1=1、四分円の面積がπ×1²/4=π/4であり、ランダムな点が四分円内に入る確率が「四分円の面積/正方形の面積=π/4」となることに基づく。発生させる点の数を増やすほど、近似精度が向上する。

extern の意味

extern の意味 [PDF], [パワーポイント], [HTML]

Visual Studio 2013 の起動とプロジェクトの新規作成

Visual Studio 2013 の起動とプロジェクトの新規作成 [PDF], [パワーポイント], [HTML]

旧バージョンである 2013 を使用しているときは、この資料を活用ください. 2019 を使用しているときは、別の資料を活用ください.

演習パート(クリックして展開)

サイト内の関連ページ