問題① 次のプログラムの間違いを指摘してください。
// main.c
#include "sub.h"
int main(void)
{
();
get_string();
put_string}
// sub.c
#include <stdio.h>
#include "sub.h"
extern char g_str[80];
void get_string()
{
(g_str, sizeof(g_str), stdin);
fgets}
void put_string()
{
(g_str);
puts}
// sub.h
#ifndef SUB_H_INCLUDED
#define SUB_H_INCLUDED
extern char g_str[80];
void get_string();
void put_string();
#endif
グローバル変数g_str は 2箇所で extern を伴った宣言が記述されていますが、肝心の定義がどこにもないことが誤りです。extern 付けて宣言を行ったら、必ずどこかに extern の付かない定義が必要です。
問題② 問題①のプログラムを、extern指定子を生かす形と、static指定子を使う形の2通りに修正してください。
extern を生かすのなら、sub.c の方の extern は消して「定義」に変えてしまい、sub.h の方の extern は残しておけば良いです。
// sub.c
#include <stdio.h>
#include "sub.h"
char g_str[80]; // 定義になった
void get_string()
{
(g_str, sizeof(g_str), stdin);
fgets}
void put_string()
{
(g_str);
puts}
実行結果:
abc
abc
static を使うのなら、sub.c の方に付けます。こうすると内部結合になるので、外部には公開しないため、sub.h の方の宣言は削除します。
// sub.c
#include <stdio.h>
#include "sub.h"
static char g_str[80]; // 内部結合になった
void get_string()
{
(g_str, sizeof(g_str), stdin);
fgets}
void put_string()
{
(g_str);
puts}
// sub.h
#ifndef SUB_H_INCLUDED
#define SUB_H_INCLUDED
void get_string();
void put_string();
#endif
実行結果:
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;
("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", is_divisor(a,b));
printf("DIGIT: %d %d\n", get_digit(a), get_digit(b));
printf("LEAPYEAR: %d %d\n", is_leap_year(a), is_leap_year(b));
printf("CHAR_IS_SIGNED?: %d\n", check_char_is_signed());
printf}
// 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;
}
/* 約数かどうか判定する */
bool is_divisor(int a, int b)
{
if (b == 0) {
/* b が 0 の場合、いかなる数の約数にもならない */
return false;
}
return (a % b == 0);
}
/* 桁数を返す */
int get_digit(int num)
{
int digit = 1;
/* 負数も扱えるように、絶対値を求めておく */
= abs(num);
num
/* 1桁になるまで、10 で割ることを繰り返す */
while (num >= 10) {
/= 10;
num ++;
digit}
return digit;
}
/* 閏年かどうか判定する */
bool is_leap_year(int year)
{
return ((year % 4 == 0) && ((year % 100 != 0) || (year % 400 == 0)));
}
/* char型が符号付きかどうか調べる */
bool check_char_is_signed()
{
return (CHAR_MIN < 0);
}
// utility.h
#ifndef UTILITY_H_INCLUDED
#define UTILITY_H_INCLUDED
#include <stdbool.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 の約数のときには真、それ以外のときには偽を返す。
*/
bool is_divisor(int a, int b);
/*
桁数を返す。
引数:
num: 対象の値。
戻り値:
桁数。
*/
int get_digit(int num);
/*
閏年かどうか判定する。
引数
year: 判定する西暦年。
戻り値
year が閏年ならば真、そうでなければ偽を返す。
*/
bool is_leap_year(int year);
/*
char型が符号付きかどうか調べる。
戻り値
このコンパイラにおいて、
char型が符号付きであれば真、符号無しであれば偽を返す。
*/
bool check_char_is_signed();
#endif
実行結果:
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 を交換して、互いの関数を使ってみてください。さまざまな部品化のやり方があることが分かると思います。
()
の前後の空白の空け方)(
の直後、)
の直前に空白を入れない)return 0;
を削除(C言語編全体でのコードの統一)≪さらに古い更新履歴を展開する≫
Programming Place Plus のトップページへ
はてなブックマーク に保存 | Pocket に保存 | Facebook でシェア |
X で ポスト/フォロー | LINE で送る | noteで書く |
RSS | 管理者情報 | プライバシーポリシー |