はじめての C プログラミング

この Notebook は,

のプログラミング部分に相当する内容を C kernel for Jupyter という Juypter kernel を利用して書いてみたものです。

はじめてのプログラミング

エンゲル係数とは,家計の消費支出に占める飲食費(食料費,食費)の割合(パーセント単位)です。

消費支出 110,000円,飲食費 37,000円の人のエンゲル係数を求めるプログラム例。

本稿のように Jupyter Notebook 環境では,セル内で Enter キー(Return キー)を押すと改行され,最後の行を書いたあとに,Shift キーを押しながら Enter キー(Return キー)を押して実行します。

  • 1行目は C 言語のお約束。プログラム分の先頭には必ず #include <stdio.h> と書く,とまずは覚えましょう。
  • 3行目も C 言語のお約束。C プログラムには1個の main() 関数が必要です。とりあえず int main(void) と書く,と覚えましょう。
  • 4 行目の { と 7行目の } で囲まれた部分が main() 関数の内容です。
  • 5 行目が実際の実行内容で,エンゲル係数の計算結果を "%f" つまり浮動小数点数で表示し,"\n" つまり改行する,という内容です。文の最後には,セミコロン ; を忘れずにつけます。
  • 6 行目の return 0; は,int main() 関数の定義の最後につける,と覚えておきます。
In [1]:
#include <stdio.h>

int main(void)
{
    printf("%f\n", 37000 * 100. / 110000);
    return 0;
}
33.636364

割り算を先にしたりして計算すると不思議な答えが表示されたりしますが,それについては後ほど解説します。

In [2]:
#include <stdio.h>

int main(void)
{
    printf("%f\n", 37000 * 100. / 110000);
    printf("%f\n", 37000 / 110000 * 100. );
    return 0;
}
33.636364
0.000000

ここからは,単に電卓的に使うだけではなく,応用・発展も見据えて以下のようなプログラム文を作成して実行することにします。

以下の例では,

  • 5 行目で変数 s1 に消費支出である 110000(円)という数値を代入し,
  • 6 行目で変数 s2 に飲食費である 37000(円)という数値を代入し,
  • 7 行目でエンゲル係数を計算して,変数 x1 に代入し,
  • 8 行目でエンゲル係数の値 x1 を表示させています。

変数 s1, s2int(整数), x1double(浮動小数点型の実数)としています。

In [3]:
#include <stdio.h>

int main(void)
{
    int s1 = 110000;
    int s2 = 37000;
    double x1 = s2 * 100. / s1;
    printf("%f\n", x1);
    return 0;
}
33.636364

エラーとデバグ

意に反して,プログラムがうまく動作しない場合があります。たとえば,上記の 3行目を以下のように書いた場合。

掛け算を示す演算子は *,割り算を示す演算子は / であるところを誤って,×÷ と書くと,これらの記号は Python では演算子として定義されていないのでエラーとなります。Jupyter Notebook では,以下のように誤りやその箇所を指摘してくれます。

In [4]:
#include <stdio.h>

int main(void)
{
    int s1 = 110000;
    int s2 = 37000;
    double x1 = s2 × 100. ÷ s1;
    printf("%f\n", x1);
    return 0;
}
/var/folders/pt/2hm5g18j5r13rhf__bx8k6km0000gn/T/tmpde4jtwfh.c:7:20: error: non-ASCII characters are not allowed outside of literals and identifiers
    double x1 = s2 × 100. ÷ s1;
                   ^~
/var/folders/pt/2hm5g18j5r13rhf__bx8k6km0000gn/T/tmpde4jtwfh.c:7:19: error: expected ';' at end of declaration
    double x1 = s2 × 100. ÷ s1;
                  ^
                  ;
/var/folders/pt/2hm5g18j5r13rhf__bx8k6km0000gn/T/tmpde4jtwfh.c:7:28: error: non-ASCII characters are not allowed outside of literals and identifiers
    double x1 = s2 × 100. ÷ s1;
                          ^~
3 errors generated.
[C kernel] GCC exited with code 1, the executable will not be executed

別の種類の誤りもあります。たとえば,7 行目の「100.」(パーセントで表すときは 100 をかけるのでした)を「1000.」と入力した場合...

In [5]:
#include <stdio.h>

int main(void)
{
    int s1 = 110000;
    int s2 = 37000;
    double x1 = s2 * 1000. / s1;
    printf("%f\n", x1);
    return 0;
}
336.363636

上記の例ではエラーが出ませんが,答えは明らかに変です。(エンゲル係数の最大値は100だから。)

このような場合はコンピュータにとって誤りではなく,指示通りに計算し,その結果を表示します。 このようなプログラム作成者の不注意や勘違いによって生じるエラーには注意が必要です。

プログラムが正しく動作しない場合は,誤りの箇所を探し,修正して再度動作を確認します。正しく動作するようになるまで,プログラムを修正し,実行(Jupyter Notebook 環境では Shift + Enter)します。この作業をデバグといいます。

なお,Jupyter Notebook の Fortran kernel では,バグのためか,1回 Shift キーを押しながら Enter キーを押しても実行結果が表示されない場合があります。そんなときは,同じセルを選択し,もう一度,Shift + Enter してみてください。

データの型と変数

上記の例の 5 行目では,s1 という名前の変数に 1100000 という数値を代入しています。変数名はアルファベットで始まる英数字列にします。大文字と小文字は区別されます。以下のプログラム例を参照。

In [6]:
#include <stdio.h>

int main(void)
{
    int s1 = 110000;
    int S1 = 37000;
    printf("%d\n", S1);
    printf("%d\n", s1);
    return 0;
}
37000
110000

変数 ss に文字列を代入する場合は,以下のようにします。文字列を扱う型は,char です。

ここでは,char ss[80] として最大80文字格納できる変数 ss を宣言し,文字列 エンゲル係数 を代入しています。

6 行目の puts() 関数は文字列を出力します。printf() 関数と違い,書式を設定する必要がありませんし,出力後,自動的に改行します。

printf() 関数を使って文字列を出力する場合は,7 行目のように "%s\n" と指定して文字列変数 ss (と改行 \n)を出力します。

In [7]:
#include <stdio.h>

int main(void)
{
    char ss[80] = "エンゲル係数";
    puts(ss);
    printf("%s\n", ss);
    return 0;
}
エンゲル係数
エンゲル係数

式と演算

掛け算を示す演算子は *,割り算を示す演算子は /。足し算は +,引き算は - です。

文字列の連結演算は + ではなく,strcat() 関数を使います。strcat() 関数をはじめとした文字列処理関数を使うときは,

#include <string.h>

が必要です。 以下の例を参照。

In [8]:
#include <stdio.h>
#include <string.h>

int main(void)
{
    char x0[80] = "エンゲル";
    char y0[80] = "係数";
    puts(strcat(x0, y0));
    return 0;
}
エンゲル係数

文字列と数値を直接連結することはできません。 そんなときは,数値を文字列に変換する関数... なんてことを考えずに,printf() 関数で以下のように書きます。

%s は文字列としての出力,%f は浮動小数点数としての出力の指定です。

In [9]:
#include <stdio.h>
#include <string.h>

int main(void)
{
    char ss[80] = "エンゲル係数は ";
    int s1 = 110000;
    int s2 = 37000;
    double x1 = s2 * 100. / s1;
    printf("%s%f\n", ss, x1);
    return 0;
}
エンゲル係数は 33.636364

主な演算子

加減乗除の演算子 +, -, *, /

優先的に計算したい箇所は丸括弧 ( )で グループ化します。

In [10]:
#include <stdio.h>

int main(void)
{
    int x = 2 + (50 - 5 * 6)/4;
    printf("%d\n", x);
    return 0;
}
7

除算 / の例を以下に示します。

  • 最初の出力では,整数を整数で割るので,小数分を切り捨てた整数値を返します。

  • 次の出力では,17. と最後に小数点をつけたので,double 型の値を返します。

In [11]:
#include <stdio.h>

int main(void)
{
    printf("%d\n", 7/14);
    printf("%f\n", 7./14);
    return 0;
}
0
0.500000

掛け算と割り算の順序を変えると,以下のように答えが変わってしまいますが,この理由もわかりますね?

In [12]:
#include <stdio.h>

int main(void)
{
    printf("%d\n", 7 / 14 * 5);
    printf("%d\n", 7 * 5 / 14);
    return 0;
}
0
2

べき乗(累乗)を表す演算子は C ではありません。$2^4$ の計算をするには,最初に

#include <math.h>`

して pow() 関数を使います。

In [13]:
#include <stdio.h>
#include <math.h>

int main(void)
{
    printf("%f\n", pow(2, 4));
    return 0;
}
16.000000

対話型プログラム

上記の例では,プログラムの中で消費支出や飲食費を決めていましたが,今度はプログラムを実行する人が入力できるようにしてみます。

以下のプログラム例では,scanf() 関数を使って,キーボードからの入力値を変数 s1s1 に整数として入れます。

scanf() 関数は入力値の格納先をアドレスで指定するので,

scanf("%d", s1);

ではなく,

scanf("%d", &s1);

のように,変数の前に & をつけます。

In [14]:
//%cflags:-Wno-gnu-zero-variadic-macro-arguments
// macOS の場合は上記1行をつけないと警告が出る。
#include <stdio.h>

int main(void)
{
    int s1, s2;
    
    printf("消費支出? "); 
    scanf("%d", &s1);
    
    printf("飲食費? "); 
    scanf("%d", &s2);
    
    double x1 = s2 * 100. / s1;
    printf("エンゲル係数は %f\n", x1);
    return 0;
}
消費支出? 110000
飲食費? 37000
エンゲル係数は 33.636364

関数の定義と呼び出し

(これまでの例では1回しか計算していませんが)よく使う計算を「関数」として定義する例です。

以下の例では,エンゲル係数を計算する関数 calc() を定義しています。

// で始まる行はコメント行です。コメント行はそれを読む人間のための注釈であり,プログラムの実行時には無視されます。

In [15]:
//%cflags:-Wno-gnu-zero-variadic-macro-arguments
// macOS の場合は上記1行をつけないと警告が出る。
#include <stdio.h>

double calc(int s1, int s2) {
// ここはコメント行。s1 は消費支出,s2 は飲食費,エンゲル係数を返す。
    return s2 * 100. / s1;
}

int main(void)
{
    int s1 = 110000;
    int s2 = 37000;
    double x1 = calc(s1, s2);
    printf("エンゲル係数は %f\n", x1);
    return 0;
}
エンゲル係数は 33.636364

(練習)

消費支出を入力すると,エンゲル係数が 20 ~ 40 となる飲食費を表示するプログラムをつくりなさい。

組み込み関数を使う

関数は上記の例のように自分で定義することもできますが,あらかじめ用意されている「組み込み関数」を使うこともできます。

以下では,組み込み関数の例として,

  • 小数点以下を切り捨てる floor()
  • 小数点以下を四捨五入する round()

を使用しています。

  • ちなみに,小数点以下を切り上げる関数は ceil() です。

これらの関数を使うときは,最初に

#include <math.h>

の記述が必要です。

In [16]:
#include <stdio.h>
#include <math.h>

double calc(int s1, int s2) {
// ここはコメント行。s1 は消費支出,s2 は飲食費,エンゲル係数を返す。
    return s2 * 100. / s1;
}

int main(void)
{
    int s1 = 110000;
    int s2 = 37000;
    double x1 = calc(s1, s2);
    
    printf("エンゲル係数は %f\n", x1);
    printf("   floor(x1) = %f\n", floor(x1));
    printf("   round(x1) = %f\n", round(x1));
    
    return 0;
}
エンゲル係数は 33.636364
   floor(x1) = 33.000000
   round(x1) = 34.000000

(練習)

小数点以下を切り上げたエンゲル係数の値を表示させるように上記のプログラムを変更しなさい。

条件分岐

条件分岐とは,例えばエンゲル係数の値によって実行文を変えることです。例を示します。

In [17]:
//%cflags:-Wno-gnu-zero-variadic-macro-arguments
// macOS の場合は上記1行をつけないと警告が出る。
#include <stdio.h>

double calc(int s1, int s2) {
// ここはコメント行。s1 は消費支出,s2 は飲食費,エンゲル係数を返す。
    return s2 * 100. / s1;
}

int main(void)
{
    int s1, s2;
    
    printf("消費支出? "); 
    scanf("%d", &s1);
    
    printf("飲食費? "); 
    scanf("%d", &s2);

    double x1 = calc(s1, s2);

    if (x1 >= 40){
        printf("エンゲル係数は %f です。", x1);
        printf("飲食費を使いすぎです。\n");
    }
    else
        printf("エンゲル係数は %f です。", x1);
    return 0;
}
消費支出? 80000
飲食費? 35000
エンゲル係数は 43.750000 です。飲食費を使いすぎです。

条件分岐の if 文の書式は以下のようになっています。

if (条件式1) {
    条件式1 が満たされた場合に実行する文;
    }
else if (条件式2) {
    条件式2 が満たされた場合に実行する文;
    }
else {
    それ以外の場合に実行する文;
    }

(練習)

上記の条件分岐を,エンゲル係数が

  1. 40 以上の場合には「飲食費を使いすぎです。」
  2. 40 未満 20 以上の場合には「正常です。」
  3. 20 未満の場合には「もっと食べましょう。」

と表示させるように変更しなさい。

繰り返し処理

以下の例では,消費支出 s1 に対してエンゲル係数を計算します。

1回だけ計算して表示するのではなく,飲食費の初期値(ここでは 40000 円)から,条件式(ここでは 80000 円以下)が成り立つ場合に, 5000 円ずつ増やしながらエンゲル係数を表示します。

+= は復号代入演算子と呼ばれるものの一つです。この場合の = は「等号」ではなく「代入」の意味で,右辺の値を新たに左辺の変数に代入する,という意味です。

s2 += 5000;

は以下と同等。

s2 = s2 + 5000;

また,プログラム文でよく見かける

i++

は,

i += 1

i = i + 1

と同じ意味です。

while 文による繰り返し

In [18]:
#include <stdio.h>

double calc(int s1, int s2) {
// ここはコメント行。s1 は消費支出,s2 は飲食費,エンゲル係数を返す。
    return s2 * 100. / s1;
}

int main(void)
{
    int s1 = 110000;
    int s2 = 40000;
    double x1;
    
    printf("消費支出 %d 円に対するエンゲル係数の値\n", s1); 

    while (s2 <= 80000) {
        x1 = calc(s1, s2);
        printf("  飲食費  %d 円の場合: %f\n", s2, x1);
        s2 += 5000;
    }
    
    return 0;
}
消費支出 110000 円に対するエンゲル係数の値
  飲食費  40000 円の場合: 36.363636
  飲食費  45000 円の場合: 40.909091
  飲食費  50000 円の場合: 45.454545
  飲食費  55000 円の場合: 50.000000
  飲食費  60000 円の場合: 54.545455
  飲食費  65000 円の場合: 59.090909
  飲食費  70000 円の場合: 63.636364
  飲食費  75000 円の場合: 68.181818
  飲食費  80000 円の場合: 72.727273

while 文の書式は以下のとおりです。

while (条件式) {
    条件式が成り立つ場合に実行される文;
}

for 文による繰り返し

In [19]:
#include <stdio.h>

double calc(int s1, int s2) {
// ここはコメント行。s1 は消費支出,s2 は飲食費,エンゲル係数を返す。
    return s2 * 100. / s1;
}

int main(void)
{
    int s1 = 110000;
    int s2 = 40000;
    double x1;
    
    printf("消費支出 %d 円に対するエンゲル係数の値\n", s1); 

    for (s2 = 40000; s2 <= 80000; s2 += 5000) {
        x1 = calc(s1, s2);
        printf("  飲食費  %d 円の場合: %f\n", s2, x1);
    }
    
    return 0;
}
消費支出 110000 円に対するエンゲル係数の値
  飲食費  40000 円の場合: 36.363636
  飲食費  45000 円の場合: 40.909091
  飲食費  50000 円の場合: 45.454545
  飲食費  55000 円の場合: 50.000000
  飲食費  60000 円の場合: 54.545455
  飲食費  65000 円の場合: 59.090909
  飲食費  70000 円の場合: 63.636364
  飲食費  75000 円の場合: 68.181818
  飲食費  80000 円の場合: 72.727273

for 文の書式は以下のとおりです。

for (int ivar = istart; 条件式; ivar の増減式) {
    整数変数 ivar  istart から条件式が成り立つ間,実行される文;
    実行後は ivar の増減式により ivar の値が変化し,
    条件式が成り立つ間,繰り返される。
}

(練習)

以下の値を求めるプログラムを作成せよ。

$$\sum_{n = 1}^{10} n^2$$