hit   今日   昨日

C言語のプログラミングコードの書き方でわからないところがあります><

フォーラムルール
フォーラムルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
Name: ほいしょー
[URL]
Date: 2014年5月05日(月) 17:07
No: 1
(OFFLINE)

 C言語のプログラミングコードの書き方でわからないところがあります><

C言語のプログラミング
以下の関数を呼び出すmain関数を記述しろという問題です。試行錯誤しましたがコンパイルすら通りません。
main文で②の関数から返ってくる値を受け取る変数ってvoid (**x)();は違いますか?
②の行は何がなんだかわかりません。
どなたか取り敢えず②の行が何をしているか教えてください。。。><


void hoge(void) {
 printf("hoge\n");
}

void (*a(void))(void){ //①
 printf("a\n");
 return hoge;
}

void (*(*b(void))(void))(void){ //②
 printf("b\n");
 return a;
}

void (*(*(*c(void))(void))(void))(void){ //③
 printf ("c\n");
 return b;
}

Name: Mana
[URL]
Date: 2014年5月05日(月) 17:57
No: 2
(OFFLINE)

 Re: C言語のプログラミングコードの書き方でわからないところがあります><

コード[C]: 全て選択
1
2
3
4
5
6
7
int main(void)
{
    a();
    b();
    c();
    return 0;
}

呼び出すだけならこれでおしまいだがそうじゃないっぽい?

ほいしょー さんが書きました:②の行は何がなんだかわかりません。
どなたか取り敢えず②の行が何をしているか教えてください。。。><

まずはヒント。
bが関数名。
bの直後にある()の中が引数を表す列。
それ以外のすべてが戻り値の型。

Name: かずま
[URL]
Date: 2014年5月05日(月) 18:20
No: 3
(OFFLINE)

 Re: C言語のプログラミングコードの書き方でわからないところがあります><

ほいしょー さんが書きました:②の行は何がなんだかわかりません。
どなたか取り敢えず②の行が何をしているか教えてください。。。><

なぜ、(1) について質問しないのですか?
次のプログラムの意味は分かっているのですか?
コード[C]: 全て選択
1
2
3
4
5
int main(void)
{
    a()();
    return 0;
}

Name: ほいしょー
[URL]
Date: 2014年5月05日(月) 18:36
No: 4
(OFFLINE)

 Re: C言語のプログラミングコードの書き方でわからないところがあります><

Manaさん返信ありがとうございます!
ということは、例えば①を
コード[C++]: 全て選択
1
2
typedef void (*A)(void);
A  a(void);


のように置き換えたら,②は,
コード[C++]: 全て選択
1
2
3
typedef void (*B1)(void);
typedef B1* (**B2)(void);
B2  b(void);


のようになるということですかね?分かりにくくてすいません。。。



>呼び出すだけならこれでおしまいだがそうじゃないっぽい?

多分、関数それぞれ(a,b,c)に戻り値を入れる変数がいるのかなと思います。
最初は、以下のようにやってみたのですが駄目でした。
コード[C++]: 全て選択
1
2
3
4
5
6
7
8
9
int main(){
  void (*x)();
  void (**y)();
  void (***z)();
  x=a();
  y=b();
  z=c();
  return 0;
}

Name: ほいしょー
[URL]
Date: 2014年5月05日(月) 18:49
No: 5
(OFFLINE)

 Re: C言語のプログラミングコードの書き方でわからないところがあります><

かずまさん返信ありがとうございます!

void1 (*a(void2))(void3){  //①

①に関しては、上のようにvoidに番号を付けると、
引数がvoid2で、戻り値が "引数がvoid3で、戻り値がvoid1の関数" へのポインタである関数aの宣言と定義。と認識しています。
分かりにくくてすいません。。。

>次のプログラムの意味は分かっているのですか?
すいませんよくわかりません。。。
関数aの引数と、関数aの戻り値のポインタ先の関数の引数にそれぞれvoidを渡している?

Name: るるら
[URL]
Date: 2014年5月05日(月) 18:59
No: 6
(OFFLINE)

 Re: C言語のプログラミングコードの書き方でわからないところがあります><

おそらくvoidの意味を知らないのではないのでしょうか?
どこから分からないのでしょうか?

Name: ほいしょー
[URL]
Date: 2014年5月05日(月) 19:58
No: 7
(OFFLINE)

 Re: C言語のプログラミングコードの書き方でわからないところがあります><

>るるらさん
いやvoidの意味は分かります。。。
コード内②の行で何をしているかいまいちわからないです。

Name: かずま
[URL]
Date: 2014年5月05日(月) 22:13
No: 8
(OFFLINE)

 Re: C言語のプログラミングコードの書き方でわからないところがあります><

これは分かりますよね。
コード[C]: 全て選択
1
2
3
4
5
int main(void)
{
    hoge();
    return 0;
}

これはどうですか?
コード[C]: 全て選択
1
2
3
4
5
6
7
int main(void)
{
    void (*p)(void);
    p = hoge;
    p();
    return 0;
}

その次が (1) の a です。
コード[C]: 全て選択
1
2
3
4
5
6
7
int main(void)
{
    void (*x)(void);
    x = a();
    x();
    return 0;
}

a() は hoge を返しますから、x は hoge。x() は hoge()。

変数 x を使わないと、
コード[C]: 全て選択
1
2
3
4
5
int main(void)
{
    a()();
    return 0;
}

a()() は、a(); hoge();

その次が (2) の b です。
コード[C]: 全て選択
1
2
3
4
5
6
7
int main(void)
{
    void (*(*y(void))(void);
    y = b();
    y();
    return 0;
}

b() は a を返しますから、y は a。y() は a()。

変数 y を使わないと、
コード[C]: 全て選択
1
2
3
4
5
int main(void)
{
    b()();
    return 0;
}

b() は a を返しますから、b()() は、a()。

b()()(); は b(); a(); hoge();

Name: かずま
[URL]
Date: 2014年5月05日(月) 22:16
No: 9
(OFFLINE)

Name: Mana
[URL]
Date: 2014年5月05日(月) 22:20
No: 10
(OFFLINE)

 Re: C言語のプログラミングコードの書き方でわからないところがあります><

こういうお遊びのような問題は関数プロトタイプだけ見て悩んでも意味ないですよ。
残りの部分が戻り値の型というのが分からないのですかね。
ヒントを順番に考えてみました?

b関数の戻り値の型は
void (*(*b(void))(void))(void)
から関数名とその直後にある括弧の引数並びを除いた部分ですよ。
voidじゃないですよ。
void (*(*)(void))(void)ですよ。
日本語で書くと「引数がvoidで戻り値がvoidの関数へのポインタを返す引数がvoidの関数へのポインタ」型ですよ。

日本語で説明するのも難しいので次のコードを見て理解してください。
かずま氏の関数呼び出し演算子の重ね掛けを理解するにはこちらを先に理解したほうがいいかもね。
コード[C]: 全て選択
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
int main(void)
{
    { /* 戻り値の型を順に継承 */
        typedef void (*fnx_t)(void);
        typedef fnx_t (*fny_t)(void);
        typedef fny_t (*fnz_t)(void);
        fnx_t x = a();
        fny_t y = b();
        fnz_t z = c();
    }
    { /* 関数プロトタイプから直に */
        typedef void (*fnx_t)(void);
        typedef void (*(*fny_t)(void))(void);
        typedef void (*(*(*fnz_t)(void))(void))(void);
        fnx_t x = a();
        fny_t y = b();
        fnz_t z = c();
    }
    return 0;
}

Name: ほいしょー
[URL]
Date: 2014年5月06日(火) 00:26
No: 11
(OFFLINE)

 Re: C言語のプログラミングコードの書き方でわからないところがあります><

>Manaさん
継承していくやり方を見て、関数bの宣言の意味がようやくわかりました。関数a,b,cの戻り値だけに着目するとわかりやすくなるんですね。
ありがとうございました!

>かずまさん
二重括弧がどうしてもしっくりきません。
二重括弧について自分なりの解釈なんですが間違っていたら教えてください。
コード[C++]: 全て選択
1
2
3
4
int *k;
int l;
k = &l;
*k;

普通の変数のポインタに変数のアドレスを代入することでその変数として振る舞うことができる上記では" *k "は" l "として振る舞う。
これと同様にa()はhogeとして振る舞うので、a()()はhoge()と同じ意味を指す。

また、ここにきて初歩的な質問で申し訳ないのですが、関数aは戻り値がありますよね?main文ではその戻り値を入れる変数はなくて良いのですか?

Name: box
[URL]
ハッカー(224,262 ポイント)
Date: 2014年5月06日(火) 00:44
No: 12
(OFFLINE)

 Re: C言語のプログラミングコードの書き方でわからないところがあります><

ほいしょー さんが書きました:また、ここにきて初歩的な質問で申し訳ないのですが、関数aは戻り値がありますよね?main文ではその戻り値を入れる変数はなくて良いのですか?

誤:main文
正:main関数

普通は、関数からの戻り値は呼び出し元で使うためにあります。だって、使わないんだったらvoidでいいじゃん、って話です。
ただ、例外的に、わざと捨てていることがあります。今回のケースがこれに相当しているのかもしれません。
バグのないプログラムはない。
プログラムは思ったとおりには動かない。書いたとおりに動く。

Name: かずま
[URL]
Date: 2014年5月06日(火) 01:19
No: 13
(OFFLINE)

 Re: C言語のプログラミングコードの書き方でわからないところがあります><

ほいしょー さんが書きました:また、ここにきて初歩的な質問で申し訳ないのですが、関数aは戻り値がありますよね?main文ではその戻り値を入れる変数はなくて良いのですか?

コード[C]: 全て選択
1
2
3
4
5
6
7
8
9
#include <stdio.h>
 
int a(void) { return 3; }
 
int main(void)
{
    printf("%d\n", a());
    return 0;
}

関数 a は戻り値がありますよね。
main関数では、次のプログラムのように
その戻り値を入れる変数 x はなくてよいのですか?
コード[C]: 全て選択
1
2
3
4
5
6
7
8
9
10
11
#include <stdio.h>
 
int a(void) { return 3; }
 
int main(void)
{
    int x;
    x = a();
    printf("%d\n", x);
    return 0;
}

という質問と同じですが、ご理解いただけますか?

Name: Mana
[URL]
Date: 2014年5月06日(火) 16:53
No: 14
(OFFLINE)

 Re: C言語のプログラミングコードの書き方でわからないところがあります><

関数へのポインタの理解が十分ではないのかな?
No.4でなんとなく関数へのポインタ型変数の宣言しているようだけども。

関数名に関数呼び出し演算子を付けて関数を呼び出せるように、関数へのポインタに関数呼び出し演算子を付けて関数を呼び出すこともできる。
かずま氏のコードに続けるとこうゆうこと。

コード[C]: 全て選択
1
2
3
4
5
6
7
8
9
10
11
#include <stdio.h>
 
int a(void) { return 3; }
 
int main(void)
{
    int (*x)(void); /* 関数へのポインタ型の変数xを宣言 */
    x = a; /* a関数へのポインタを代入 &演算子は不要で付けても付いてないと同じ */
    printf("%d\n", x()); /* 関数へのポインタで関数呼び出し *演算子は不要で付けても付いてないと同じ */
    return 0;
}


関数の戻り値が関数へのポインタだったらそこからさらに続けて呼び出すこともできるというわけ。

Name: 三郎
[URL]
かけだし(1,141 ポイント)
Date: 2014年5月06日(火) 21:26
No: 15
(OFFLINE)

 Re: C言語のプログラミングコードの書き方でわからないところがあります><

外野席からのボヤキ

c言語とはこんなにも難しいものなのでしょうか??!。
もちろん私は質問に答える能力はありません。
以前に聞いたことがあります。
C言語も高級言語だと、、、それなら誰にでもソースが分かるような記述がいいような気がしますが、
これは私の偏見でしょうか???
初心者にも分かるような記述をするのがこの言語の本来の姿と思っていました。

そうかといっても、私には分かりやすく書く方法も知りませんが。

Name: softya(ソフト屋)
(副管理人)
[URL]
ウィザード(1,051,343 ポイント)
Date: 2014年5月06日(火) 21:51
No: 16
(ONLINE)

 Re: C言語のプログラミングコードの書き方でわからないところがあります><

三郎 さんが書きました:外野席からのボヤキ

c言語とはこんなにも難しいものなのでしょうか??!。
もちろん私は質問に答える能力はありません。
以前に聞いたことがあります。
C言語も高級言語だと、、、それなら誰にでもソースが分かるような記述がいいような気がしますが、
これは私の偏見でしょうか???
初心者にも分かるような記述をするのがこの言語の本来の姿と思っていました。

そうかといっても、私には分かりやすく書く方法も知りませんが。


C言語はどちらかと言うと自由度が高い言語なので、読み辛いソースコードは当然のごとく書けます。
初心者だと分かりづらいソースコードになりがちです。なので、分かりやすく書けるのは熟練者と言えます。
そもそも共同作業で分かりづらいソースコードを書くのはご法度ですので、熟練するほど読みやすいソースコードを書くようになります。

言語仕様の自由度と初心者にも書きやすい綺麗なコードを書ける仕様はだいたい矛盾します。
C言語は記述性と自由度と高速性を優先して設計されていますので、「初心者にも分かるような記述をするのがこの言語の本来の姿と思っていました。」と言う意図は本来ありませんね。

読みやすさとかわかり易さを優先したのは、COBOL,BASIC,PASCALなどの言語だと思います。
by softya(ソフト屋)


Return to C言語何でも質問掲示板

オンラインデータ

このフォーラムを閲覧中のユーザー: softya(ソフト屋) & ゲスト[15人]