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

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

ただいまの
回答率

90.51%

  • C

    4525questions

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

  • C++

    4427questions

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

関数へのポインタを返す関数

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 2
  • VIEW 1,137

strike1217

score 568

正常に動作する関数を載せます。

int funcs(int a, int b) {
    printf("%d", a + b);
    return a + b;
}

int (*se(int b))(int , int) {
    printf("%d\n", b);
    return funcs;
}

int main(){
    int (*sp)() = se(100);
    int i = sp(2,3);
        return 0;
}

次の実験コードです。
返り値がない場合とある場合で対処できるようにします。

void (*sell(void))(int, int) {
    return funcs;
}

int (*pp)() = (int(*)(int, int))sell();
// int (*pp)() = (int(*(void))(int, int))sell();   errorです。
int k = pp(1, 1);


これは、エラーやwarningは出現しませんが、funcs()内のprintfが表示されません。
主にキャストの場所とやり方だと思いますが、どこがいけないのでしょうか?

関数のポインタを返すための関数では、1つ型の関数しか返せないんでしょうか??

次はパラメータの数が異なる関数の場合です。

int func(int a, int b, int c) {
    printf("%d", a + b + c);
    return a + b + c;
}

void (*sell(int c))(int, int) {
   if(c == 0)
        return func;
   else
        return funcs;
}


func()の方はパラメータが3つあります。
パラメータの数が異なる関数をreturnさせたい場合、どのように記述すれば良いのでしょうか?

つまり、
異なる型(返り値がある・ない、パラメータの数が異なる・パラメータなし)の関数のポインタをreturn できるような関数を記述したいです。

どなたか教えて下さい。

実験環境は、Windows 64bit visual studio  cl オプションなし です。

「追記」___________________________
Saitoさんの回答とコメントを元に作ってみました。
こんな感じですかね・・・・

#include<stdio.h>

int func(int a, int b) {
    return a + b;
}

void funcs(void) {
    printf("Hello in funcs\n");
}

int (*set(int c))() {
    if (c)
        return func;
    else
        return (int (*)(void))funcs;   //一度キャストしておく
}

int main() {
    void (*p)() = (void (*)(void))set(0);    // キャストして元に戻す
    int (*r)() = set(1);

    p();
    printf("%d\n", r(1,40));
    return 0;
}

Warning, Error共になく、正常に動作しています。
maisumakunさんの回答を見ると、未定義とされているので怖いのですが・・・・

これぐらいしか方法がなさそうです。

間違いがあったらご指摘ください。
他にも方法があれば是非教えてください。

int main() {
    int (*p)() = set(0);
    int (*r)() = set(1);

    ((void (*)(void))p)();
    printf("%d\n", r(1,40));
    return 0;
}


こちらの方が分かりやすいですかね・・・

「追記2」_________________________________
あ、この場合は未定義ではないのかな?

「型変換されたポインタを関数呼出しに用い,関数の型がポインタが指すものの型と適合しない場合,その動作は未定義とする。 」

となっているようなので、呼び出しの際に元の型へ戻しておけば動作は未定義ではない
ということだと思います。

型を別のものにキャストして呼び出した場合の話なので、

とある型 → 別の型にキャスト → 元の型にキャスト → 呼び出し なら大丈夫!!
とある型 → 別の型にキャスト → 呼び出し これはダメ!!

という事ですかな?

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

+4

C言語の世界では、「引数や返り値の違う関数」は別の型です。関数の引数や返り値は呼び出す側でのコード生成にも影響しますので、「シグネチャの違う関数ポインタにキャストして、そのまま呼び出す」ことは未定義の動作となります(≒やってはいけません)。

ある型の関数へのポインタを,別の型の関数へのポインタに型変換することができる。さらに再び型変換で元の型に戻すことができるが,その結果は元のポインタと比較して等しくなければならない。型変換されたポインタを関数呼出しに用い,関数の型がポインタが指すものの型と適合しない場合,その動作は未定義とする。

「正しい関数型に戻すのは呼んだ側の責任」でいいのであれば、まだvoid *を返したほうが、そのまま使えないだけ親切だと思います。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/08/05 18:50

    未定義なんですか!
    むむ・・・・

    キャンセル

  • 2017/08/05 19:00

    まあそもそもこんなハックをしたくなる時点で何かを間違えているのでプログラムの設計そのものを見直すべきでしょう。

    キャンセル

  • 2017/08/05 19:04 編集

    void func(){}

    void (*p)(void) = (void(*)(void))func;

    キャストしないとwarningかerrorが出ると思いますが、こんな単純な場合でも未定義なんですか?
    (ちなみに、こちらは正常に動作します。)

    キャンセル

  • 2017/08/05 19:24

    ん?それは別にキャストいらないですし型変わっていませんよね?

    関数の型が何のために存在しているか考えてください。
    関数の創世から深淵まで駆け抜ける関数とはなんぞや講座 - Qiita
    http://qiita.com/yumetodo/items/cdfb41781d32d98be1b4

    キャンセル

  • 2017/08/05 20:00

    あら、warning出ませんでしたっけ?

    キャンセル

  • 2017/08/05 20:01

    あ、出ませんね。
    勘違いでした。すいません。

    キャンセル

checkベストアンサー

+2

こういう要領で引数リストの中身を空にすると、引数の内容を不問にする関数ポインタというものを作ることが出来ます。

#include <stdio.h>

int funcs(int a, int b) {
    printf("%d", a + b);
    return a + b;
}

int func(int a, int b, int c) {
    printf("%d", a + b + c);
    return a + b + c;
}

int (*sell(int c))() {
   if(c == 0) return func;
   else return funcs;
}

C で引数が無い関数を宣言するときにわざわざ仮引数リストを (void) という風に書くのは、 () だと「引数の型情報がないものとする」という解釈になってしまうからなのです。

しかし、もちろんその関数ポインタを呼び出すときに引数の型や個数が定義と異なると未定義の挙動となります。 他の方の回答では void* を使うという提案もありますが、厳密に言えば言語仕様上は関数ポインタは void* よりも大きい幅である可能性があるのでお勧めできません。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/08/05 18:55

    あら、できました!!
    先ほどやった時はwarningが出現していたんですが、今やったら消えました。
    (謎ですが・・・)

    定義とは異なる型へキャストした場合は、未定義ということですね!

    キャンセル

  • 2017/08/05 19:00

    返り値の方はどのようにすれば良いのでしょうか?

    キャンセル

  • 2017/08/05 20:33

    返却値の方の型情報を消す方法というのはありませんが、関数ポインタ同士はキャストしても情報が失われないことは保証されています。 もちろん、呼び出すときは元の型にキャストしてからでなければなりませんが。 なので、適当な返却値の関数ポインタを使い、適宜キャストを入れるしか仕方がないと思います。
    なお、 C++ では仮引数リスト `()` は `(void)` と同じ意味に解釈されることになっているという非互換があるということには留意してください。

    キャンセル

  • 2017/08/05 20:49

    ほぉ!!
    そうなんですか!
    わかりました。ありがとうございます。

    キャンセル

  • 2017/08/06 10:26

    > 厳密に言えば言語仕様上は関数ポインタは void* よりも大きい幅である可能性

    ありがとうございます。データポインタと別世界になる(こともある)ものですしね。

    キャンセル

同じタグがついた質問を見る

  • C

    4525questions

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

  • C++

    4427questions

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