C言語編 第23章 複数ファイルによるプログラム 解答ページ

先頭へ戻る

問題①

問題① 次のプログラムの間違いを指摘して下さい。

/* main.c */
#include "sub.h"

int main(void)
{
    getString();
    putString();

    return 0;
}
/* sub.c */
#include <stdio.h>
#include "sub.h"

extern char gStr[80];


void getString()
{
    fgets( gStr, sizeof(gStr), stdin );
}

void putString()
{
    puts( gStr );
}
/* sub.h */
extern char gStr[80];

void getString();
void putString();


グローバル変数gStr は 2箇所で extern を伴った宣言が記述されていますが、肝心の定義がどこにも存在していないことが誤りです。 extern 付けて宣言を行ったら、必ずどこかに extern の付かない定義が必要です。

問題②

問題② 問題①のプログラムを、externキーワードを生かす形と、staticキーワードを使う形の2通りに修正して下さい。


extern を生かすのなら、sub.c の方の extern は消して「定義」に変えてしまい、sub.h の方の extern は残しておけば良いです。

/* sub.c */
#include <stdio.h>
#include "sub.h"

char gStr[80];  /* 定義になった */


void getString()
{
	fgets( gStr, sizeof(gStr), stdin );
}

void putString()
{
	puts( gStr );
}

実行結果:

abc
abc

static を使うのなら、sub.c の方に付けます。 こうすると内部結合になるので、外部には公開しないため、sub.h の方の宣言は削除します。

/* sub.c */
#include <stdio.h>
#include "sub.h"

static char gStr[80];  /* 内部結合になった */


void getString()
{
    fgets( gStr, sizeof(gStr), stdin );
}

void putString()
{
    puts( gStr );
}
/* sub.h */

void getString();
void putString();

実行結果:

abc
abc

問題③

問題③ この章の最初の方で、max関数と min関数を持った utility.c と utility.h を作成しました。 同じように、汎用的に使えそうな関数をこれらのファイルに追加し、便利な関数群を作って下さい。


/* main.c */
#include <stdio.h>
#include "utility.h"

int main(void)
{
    int a = -50;
    int b = 400;

    printf( "MAX: %d\n", max(a,b) );
    printf( "MIN: %d\n", min(a,b) );
    printf( "ABS: %d, %d\n", abs(a), abs(b) );
    printf( "DIVISOR: %d\n", isDivisor(a,b) );
    printf( "DIGIT: %d %d\n", getDigit(a), getDigit(b) );
    printf( "LEAPYEAR: %d %d\n", isLeapYear(a), isLeapYear(b) );
    printf( "CHAR_IS_SIGNED?: %d\n", checkCharIsSigned() );

    return 0;
}
/* utility.c */
#include <limits.h>
#include "utility.h"


/* 大きい方の値を返す */
int max(int a, int b)
{
    if( a >= b ) {
        return a;
    }
    return b;
}

/* 小さい方の値を返す */
int min(int a, int b)
{
    if( a <= b ) {
        return a;
    }
    return b;
}

/* 絶対値を返す */
int abs(int num)
{
    if( num < 0 ){
        return -num;
    }
    return num;
}

/* 約数かどうか判定する */
int isDivisor(int a, int b)
{
    if( b == 0 ){
        /* b が 0 の場合、いかなる数の約数にもならない */
        return 0;
    }

    if( a % b == 0 ){
        return 1;
    }
    return 0;
}

/* 桁数を返す */
int getDigit(int num)
{
    int digit = 1;

    /* 負数も扱えるように、絶対値を求めておく */
    num = abs( num );

    /* 1桁になるまで、10 で割ることを繰り返す */
    while( num >= 10 ){
        num /= 10;
        digit++;
    }

    return digit;
}

/* 閏年かどうか判定する */
int isLeapYear(int year)
{
    if( (year % 4 == 0) && ((year % 100 != 0) || (year % 400 == 0)) ){
        return 1;
    }
    return 0;
}

/* char型が符号付きかどうか調べる */
int checkCharIsSigned()
{
    if( CHAR_MIN < 0 ){
        return 1;
    }
    return 0;
}
/* utility.h */

/*
    大きい方の値を返す。
    戻り値
        引数a と b のうちの大きい方の値を返す。
*/
int max(int a, int b);

/*
    小さい方の値を返す。
    戻り値
        引数a と b のうちの小さい方の値を返す。
*/
int min(int a, int b);

/*
    絶対値を返す。
    引数
        num:	元の値。
    戻り値
        numの絶対値。
*/
int abs(int num);

/*
    約数かどうか判定する。
    引数
        a:	元の値。
        b:	元の値。
    戻り値
        b が a の約数のときには 1、それ以外のときには 0 を返す。
*/
int isDivisor(int a, int b);

/*
    桁数を返す。
    引数:
        num:	対象の値。
    戻り値:
        桁数。
*/
int getDigit(int num);

/*
    閏年かどうか判定する。
    引数
        year: 判定する西暦年。
    戻り値
        year が閏年ならば 1 、そうでなければ 0 を返す。
*/
int isLeapYear(int year);

/*
    char型が符号付きかどうか調べる。
    戻り値
        このコンパイラにおいて、
        char型が符号付きであれば 1、符号無しであれば 0 を返す。
*/
int checkCharIsSigned();

実行結果:

MAX: 400
MIN: -50
ABS: 50, 400
DIVISOR: 0
DIGIT: 2 3
LEAPYEAR: 0 1
CHAR_IS_SIGNED?: 1

ここで挙げた関数群は、これまでの章で登場したものからの抜粋です。現段階の知識だけでも、これ以上の種類の汎用的な関数を作成できるはずです。また、max関数や min関数に関しても、double型バージョンを作るといったことも考えられます。

abs関数に関しては、実は標準ライブラリ関数に存在しています(⇒リファレンス

また、コメントに関してですが、関数の詳しい説明はヘッダファイル側に書くべきで、逆にソースファイル側のコメントは最小限のもので十分です。 ヘッダファイルは、言ってみればカタログですから、使い方や何をしてくれるのかといったことは、こちらに書いてあるべきです。 また、使う側からすれば、#include のときにヘッダの名前を指定するだけであって、 ソースファイル側の具体的な部分には感知しないのが普通ですから、使う側が見ているのはヘッダファイルだけと思った方が良いです。 これは、標準ヘッダでも同様です。

※もし学校の授業等で、このページを見ていたら、是非、周囲の人と utility.c と utility.h を交換して、互いの関数を使ってみて下さい。 様々な部品化のやり方があることが分かると思います。 更に、互いの感想を伝えあえれば最高の成果です。



参考リンク

更新履歴

'2018/2/26 全面的に文章を見直し、修正を行った。

'2009/7/25 新規作成。





第23章のメインページへ

C言語編のトップページへ

Programming Place Plus のトップページへ


このエントリーをはてなブックマークに追加
rss1.0 取得ボタン RSS