質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

解決済

1回答

795閲覧

関数ポインタを引数で渡すときの記法について

Eki

総合スコア429

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

0グッド

2クリップ

投稿2018/01/30 04:46

次のコードの foo() 関数についてお聞きしたいです。

cpp

1void foo(int cb()) { 2 cout << cb() << endl; 3} 4 5int bar() { return 0; } 6 7int main(void) { 8 foo(bar); 9 10 // ↓コンパイルエラー 11 // void funcptr(int()) = foo; 12}

個人的には void foo(int cb()); という関数の引数 cb は「int 型を返す引数なしの関数」型、になるべきなのではないかと思います。しかし、実際にはこれは関数 ポインタ 型としてパースされるようです。決して int 型の引数が int * に変換されたりはしないのに、なぜこのような挙動になのでしょうか?

無論、関数そのものを受け渡しする、ということがナンセンスというか不可能であって、関数を引数にするならポインタにならざるを得ないことは分かっています。しかし、きちんと関数ポインタであることを明記した表記void foo(int (*cb)());が存在するので、このような表記法は不要なのではないかと思うのですが...

なお、main() 関数内でコメントアウトしたように、変数宣言として使おうとすると (当然関数の前方宣言と解釈され) 想像通りのエラーとなりました。


このようなことを考えたのはクラスの初期化構文の曖昧さについて調べていたからです。コンストラクタ呼び出しのつもりが、関数の前方宣言になってしまうという曖昧さです: Most vexing parse - Wikipedia

引数が関数ポインタとして扱われさえしなければ、そして関数型の引数を禁止すれば、上の Wikipedia のページのコードは問題なくパースできるのではないかと不思議に思っています。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

C言語の場合、式の中に関数が現れると、それは関数ポインタとして解釈されます。

6.3.2.1 (前略)関数指示子が sizeof 演算子(54)又は単項&演算子のオペランドである場合を除いて,型“∼型を返す関数”をもつ関数指示子は,型“∼型を返す関数へのポインタ”をもつ式に変換する。

そして、なんと関数呼び出しのカッコは、関数ポインタに対して付けるものなのです。

呼び出される関数を表す式(中略)は,void を返す関数へのポインタ型,又は配列型以外のオブジェクト型を返す関数へのポインタ型をもたなければならない。(強調は引用者)

ということで、printf("hoge\n");とするときのprintfも関数ポインタなのです。で、逆参照した*printfもポインタに戻るので、(***printf)("hoge\n");なんていう呼び方もできてしまいます。

そんな加減もあって、仮引数に対しては型調整という概念があります。

各仮引数の型は,仮引数型並びに対する 6.7.5.3 の規定に従って型調整する。

6.7.5.3 (前略)仮引数を“∼型の配列”とする宣言は,“∼型への修飾されたポインタ”に型調整する。(中略)仮引数を“∼型を返却する関数”とする宣言は,6.3.2.1 の規定に従い,“∼型を返却する関数へのポインタ”に型調整する。

なお、引用部はすべてJIS X 3010:2003 プログラミング言語Cからの引用です(C++では厳密な事情が違う可能性があります)。

投稿2018/01/30 05:31

maisumakun

総合スコア145121

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Eki

2018/01/30 06:17

回答ありがとうございます。 仮引数内で配列がポインタになるのと同様の特別扱いを受けているということですね。 なるほど、納得です。 なぜこのような型調整を施すのでしょうか (違う質問でしたほうがよいかもしれませんが) 。 C言語では宣言の構文は使う時の形に近づくよう設計されていると聞いたことがありますが、とはいえ、仮引数に配列を要素数付きで指定したときなど、便利さよりもあらぬ誤解を生みそうな危険さのほうが勝る気がします... この型調整がなければ指定し得ない型や複雑になってしまう型があるのでしょうか?あるいは、歴史的な経緯のようなものがあるのでしょうか。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問