ページ 11

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

Posted: 2014年5月05日(月) 17:07
by ほいしょー
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;
}

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

Posted: 2014年5月05日(月) 17:57
by Mana

コード:

int main(void)
{
	a();
	b();
	c();
	return 0;
}
呼び出すだけならこれでおしまいだがそうじゃないっぽい?
ほいしょー さんが書きました:②の行は何がなんだかわかりません。
どなたか取り敢えず②の行が何をしているか教えてください。。。><
まずはヒント。
bが関数名。
bの直後にある()の中が引数を表す列。
それ以外のすべてが戻り値の型。

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

Posted: 2014年5月05日(月) 18:20
by かずま
ほいしょー さんが書きました: ②の行は何がなんだかわかりません。
どなたか取り敢えず②の行が何をしているか教えてください。。。><
なぜ、(1) について質問しないのですか?
次のプログラムの意味は分かっているのですか?

コード:

int main(void)
{
    a()();
    return 0;
}

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

Posted: 2014年5月05日(月) 18:36
by ほいしょー
Manaさん返信ありがとうございます!
ということは、例えば①を

コード:

typedef void (*A)(void);
A  a(void);
のように置き換えたら,②は,

コード:

typedef void (*B1)(void);
typedef B1* (**B2)(void);
B2  b(void);
のようになるということですかね?分かりにくくてすいません。。。



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

多分、関数それぞれ(a,b,c)に戻り値を入れる変数がいるのかなと思います。
最初は、以下のようにやってみたのですが駄目でした。

コード:

int main(){
  void (*x)();
  void (**y)();
  void (***z)();
  x=a();
  y=b();
  z=c();
  return 0;
}

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

Posted: 2014年5月05日(月) 18:49
by ほいしょー
かずまさん返信ありがとうございます!

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

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

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

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

Posted: 2014年5月05日(月) 18:59
by るるら
おそらくvoidの意味を知らないのではないのでしょうか?
どこから分からないのでしょうか?

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

Posted: 2014年5月05日(月) 19:58
by ほいしょー
>るるらさん
いやvoidの意味は分かります。。。
コード内②の行で何をしているかいまいちわからないです。

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

Posted: 2014年5月05日(月) 22:13
by かずま
これは分かりますよね。

コード:

int main(void)
{
	hoge();
	return 0;
}
これはどうですか?

コード:

int main(void)
{
	void (*p)(void);
	p = hoge;
	p();
	return 0;
}
その次が (1) の a です。

コード:

int main(void)
{
	void (*x)(void);
	x = a();
	x();
	return 0;
}
a() は hoge を返しますから、x は hoge。x() は hoge()。

変数 x を使わないと、

コード:

int main(void)
{
	a()();
	return 0;
}
a()() は、a(); hoge();

その次が (2) の b です。

コード:

int main(void)
{
	void (*(*y(void))(void);
	y = b();
	y();
	return 0;
}
b() は a を返しますから、y は a。y() は a()。

変数 y を使わないと、

コード:

int main(void)
{
	b()();
	return 0;
}
b() は a を返しますから、b()() は、a()。

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

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

Posted: 2014年5月05日(月) 22:16
by かずま
かずま さんが書きました:

コード:

	void (*(*y(void))(void);
訂正

コード:

	void (*(*y)(void))(void);

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

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

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

日本語で説明するのも難しいので次のコードを見て理解してください。
かずま氏の関数呼び出し演算子の重ね掛けを理解するにはこちらを先に理解したほうがいいかもね。

コード:

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;
}

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

Posted: 2014年5月06日(火) 00:26
by ほいしょー
>Manaさん
継承していくやり方を見て、関数bの宣言の意味がようやくわかりました。関数a,b,cの戻り値だけに着目するとわかりやすくなるんですね。
ありがとうございました!

>かずまさん
二重括弧がどうしてもしっくりきません。
二重括弧について自分なりの解釈なんですが間違っていたら教えてください。

コード:

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

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

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

Posted: 2014年5月06日(火) 00:44
by box
ほいしょー さんが書きました: また、ここにきて初歩的な質問で申し訳ないのですが、関数aは戻り値がありますよね?main文ではその戻り値を入れる変数はなくて良いのですか?
誤:main文
正:main関数

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

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

Posted: 2014年5月06日(火) 01:19
by かずま
ほいしょー さんが書きました: また、ここにきて初歩的な質問で申し訳ないのですが、関数aは戻り値がありますよね?main文ではその戻り値を入れる変数はなくて良いのですか?

コード:

#include <stdio.h>
 
int a(void) { return 3; }

int main(void)
{
    printf("%d\n", a());
    return 0;
}
関数 a は戻り値がありますよね。
main関数では、次のプログラムのように
その戻り値を入れる変数 x はなくてよいのですか?

コード:

#include <stdio.h>
 
int a(void) { return 3; }

int main(void)
{
    int x;
    x = a();
    printf("%d\n", x);
    return 0;
}
という質問と同じですが、ご理解いただけますか?

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

Posted: 2014年5月06日(火) 16:53
by Mana
関数へのポインタの理解が十分ではないのかな?
No.4でなんとなく関数へのポインタ型変数の宣言しているようだけども。

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

コード:

#include <stdio.h>
 
int a(void) { return 3; }
 
int main(void)
{
    int (*x)(void); /* 関数へのポインタ型の変数xを宣言 */
    x = a; /* a関数へのポインタを代入 &演算子は不要で付けても付いてないと同じ */
    printf("%d\n", x()); /* 関数へのポインタで関数呼び出し *演算子は不要で付けても付いてないと同じ */
    return 0;
}
関数の戻り値が関数へのポインタだったらそこからさらに続けて呼び出すこともできるというわけ。

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

Posted: 2014年5月06日(火) 21:26
by 三郎
外野席からのボヤキ

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

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

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

Posted: 2014年5月06日(火) 21:51
by softya(ソフト屋)
三郎 さんが書きました:外野席からのボヤキ

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

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

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

読みやすさとかわかり易さを優先したのは、COBOL,BASIC,PASCALなどの言語だと思います。

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

Posted: 2014年5月07日(水) 10:38
by usao
オフトピック
>外野席からのボヤキ

まぁ,「呼び出してみろ」というのが問題になっているくらいだから
わざと わからないように 書いてあるわけで,
実際に自分でコード書くときは わからなくならないように 書けばいいと思いますよ.

私もこんなコード見せられたら「は?」ってなりますし,
実際に書かなきゃならないとしたら例えば↓みたいに自分にわかるように書きますし……

コード:

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

typedef void (*FP)(void);	//hoge()に対応
typedef FP (*FPa)(void);	//a()に対応
typedef FPa (*FPb)(void);	//b()に対応

FP a()
{
	printf( "a\n" );
	return hoge;
}

FPa b()
{
	printf( "b\n" );
	return a;
}

FPb c()
{
	printf( "c\n" );
	return b;
}