問題① 階乗を計算する関数を再帰呼び出しを利用して作成してください。また、再帰呼び出ししない方法でも作成してください。
まずは再帰版から。
#include <stdio.h>
#include <assert.h>
int fact(int n);
int main(void)
{
int num = 5;
("%d! = %d\n", num, fact(num));
printf}
/*
階乗を計算する
引数:
n: 階乗n! の n にあたる数。
戻り値:
階乗n! の結果。
*/
int fact(int n)
{
(n >= 0);
assert
if (n == 0) {
return 1;
}
return fact(n - 1) * n;
}
実行結果:
5! = 120
階乗を再帰呼び出しで計算するプログラムは、再帰呼び出しを学ぶ際の通過儀礼のようなものですが、実用性は皆無と言えます。なぜなら、次のように素直に繰り返しで実装した方が効率的だからです。
#include <stdio.h>
#include <assert.h>
int fact(int n);
int main(void)
{
int num = 5;
("%d! = %d\n", num, fact(num));
printf}
/*
階乗を計算する
引数:
n: 階乗n! の n にあたる数。
戻り値:
階乗n! の結果。
*/
int fact(int n)
{
(n >= 0);
assert
int ans = 1;
for (int i = 1; i <= n; ++i) {
*= i;
ans }
return ans;
}
実行結果:
5! = 120
再帰呼び出しの方は、関数の呼び出しにかかる処理時間や、スタック領域の使用量の増加につながりますから、この程度の処理に再帰呼び出しを持ち出すのは、一般的には無駄です。それでも、再帰呼び出しのプログラムを書く練習としては良い題材なので、経験しておくのは良いことでしょう。
問題② 次のようなプログラムがあります。
#include <stdio.h>
struct Data_tag {
int num;
struct Data_tag* next;
};
static struct Data_tag g_head;
int main(void)
{
struct Data_tag data00;
struct Data_tag data01;
struct Data_tag data02;
.num = 0;
g_head.next = &data00;
g_head
.num = 15;
data00.next = &data01;
data00
.num = 30;
data01.next = &data02;
data01
.num = 45;
data02.next = NULL;
data02}
Data_tag構造体のメモリアドレスを渡すと、ヌルポインタに行き着くまで nextメンバをたどっていき、その過程で numメンバの内容を出力する関数を作成してください。再帰呼び出しを使ったものと、使わないものをそれぞれ作成してください。
ポインタによって、次のデータを結び付けています。これは、線形リストと呼ばれるデータ構造の一種で、実用上にも非常に価値があるものです。詳細は、アルゴリズムとデータ構造編【データ構造】第3章を参照してください。
データの終端を表すために、最後のポインタには NULL を入れてありますから、ここまでたどっていけばいいということになります。
まずは再帰版です。
#include <stdio.h>
#include <assert.h>
struct Data_tag {
int num;
struct Data_tag* next;
};
static struct Data_tag g_head;
void print_data(const struct Data_tag* data);
int main(void)
{
struct Data_tag data00;
struct Data_tag data01;
struct Data_tag data02;
.num = 0;
g_head.next = &data00;
g_head
.num = 15;
data00.next = &data01;
data00
.num = 30;
data01.next = &data02;
data01
.num = 45;
data02.next = NULL;
data02
("start by gData");
puts(&g_head);
print_data
("start by data01");
puts(&data01);
print_data}
void print_data(const struct Data_tag* data)
{
(data != NULL);
assert
if (data->next == NULL) {
return;
}
("%d\n", data->num);
printf(data->next);
print_data}
実行結果:
start by gData
0
15
30
start by data01
30
nextメンバに格納されているポインタをたどっていかないといけないことに注意してください。++演算子でインクリメントしていくのではありません(++ で次に進むことができるのは、配列のようにメモリ上で連続的に並んでいる場合だけです)。再帰呼び出しを終えるタイミングは、これ以上たどれなくなったときなので、nextポインタがヌルポインタになったときです。
再帰呼び出しをしない方法でも簡単に作れます。関数の部分だけ取り上げます(他はまったく同じです)
void print_data(const struct Data_tag* data)
{
(data != NULL);
assert
const struct Data_tag* p = data;
while (p->next != NULL) {
("%d\n", p->num);
printf= p->next;
p }
}
()
の前後の空白の空け方)(
の直後、)
の直前に空白を入れない)return 0;
を削除(C言語編全体でのコードの統一)「NULL」よりも「ヌルポインタ」が適切な箇所について、「ヌルポインタ」に修正。
全面的に文章を見直し、修正を行った。
章のタイトルを変更(再帰 -> 再帰呼び出し)
新規作成。
Programming Place Plus のトップページへ
はてなブックマーク に保存 | Pocket に保存 | Facebook でシェア |
X で ポスト/フォロー | LINE で送る | noteで書く |
RSS | 管理者情報 | プライバシーポリシー |