none
関数での文字列の受け渡し RRS feed

  • 質問

  • 例えば

    int main(int argc, char *argv[])
    {

    printf("%s\n", eigyo_bunrui("34"));
    }

    char *eigyo_bunrui( char inp[] )
    {
    if( strncmp( inp, "1", 1 ) == 0 ) return("1;東京");
    if( strncmp( inp, "3", 1 ) == 0 ) return("2;名古屋");
    if( strncmp( inp, "2", 1 ) == 0 ) return("3;大阪");
    return("5;その他");

    }
    にてコンパイラーで 
    aaa.c(11): warning C4477: 'printf' : 書式文字列 '%s' には、型 'char *' の引数が必要ですが、可変個引数 1 は型 'int' です
    aaa.c(11): warning C4313: 'printf': 書式文字列内の '%s' が引数 1 ('int' 型) と競合しています。
    aaa.c(14): error C2040: 'eigyo_bunrui': 'char *(char *)' は 'int ()' と間接操作のレベルが異なります。
    というエラーが出ます。これまで使っていた VC++6.0では 全く問題なかったのですが 何がおかしいのでしょうか? 

    2020年6月27日 12:41

すべての返信

  • 関数eigyo_bunruiはmainより前に宣言されていますか?

    char *eigyo_bunrui(char inp[]); // mainより前にこのシグネチャを持った関数が存在することを宣言する必要がある

    int main(...) 以下略

    // リテラルとはいえ返値としてchar*を返すのは気持ち悪いなぁ…。

    2020年6月27日 12:49
  • 詳しい事は確認していませんが、

    最近の VC++では、printf()での引数の型チェックがはいるようになったようです。
    VC++6.0 の頃は、printf()での引数は(不定のため)、入っていませんでした。

    また、errorは、 eigyo_bunrui()の関数宣言が事前にされていないため、 printf()での参照が最初となり、暗黙の宣言として、int が仮定されたが、その後の関数宣言で、 char * となっているので、異なるとみなされます。

    どちらにしても、最近の VC++の方が、チェックが厳しくなった結果と考えます。

    2020年6月27日 22:43
  • コンパイルオプション/Za 言語拡張機能の無効化を指定するとコンパイルできます。2010年頃に同様の質問が投稿されていることから、最近変更されたわけでもなさそうです。ちなみにこのオプションはVC++ 6.0にも存在しデフォルトもオフのままなので「VC++6.0では 全く問題なかった」のがなぜなのかはわかりません。

    なお、コンパイルはできますが、次のリンクエラーが発生します。

    >aaa.obj : error LNK2001: unresolved external symbol _printf

    これは、Visual Studio 2015でstdio.hが全面的に書き換えられ

        _Check_return_opt_
        _CRT_STDIO_INLINE int __CRTDECL printf(
            _In_z_ _Printf_format_string_ char const* const _Format,
            ...)
        #if defined _NO_CRT_STDIO_INLINE
        ;
        #else
        {
            int _Result;
            va_list _ArgList;
            __crt_va_start(_ArgList, _Format);
            _Result = _vfprintf_l(stdout, _Format, NULL, _ArgList);
            __crt_va_end(_ArgList);
            return _Result;
        }
        #endif
    

    このようにインライン関数となり、実際にリンクされるべき関数名が_vfprintf_lとなるためです。これに関しては、legacy_stdio_definitions.libをリンクすることで解消され、正常に実行できるようになります。もちろん素直に #include <stdio.h> と記述することをお勧めします。

    2020年6月28日 0:22
  • 回答いただきた皆様、ありがとうございました。確かに Lattice Cや 初期Unixの Cでは Mainより前に 関数の宣言が必要でした。MS-Cでは 省略できていたので もう 20年近く こんな 書き方に なってました。これまで MS-Cから 作り続けていた 各種数値解析のプログラムが リコンパイルできるようになり 大変助かりました。

    2020年6月28日 7:09
  • 佐祐理さん、私の不勉強かも知れませんが、 printf()は、 (ほとんど、C言語ですが、、)

    printf(char *, ...) という宣言で、最初の引数が (char *) (あ、正確には const ?) で、それ以外は、可変引数の扱いだったかと思っています。 その前提からすると、可変引数の部分は、型がなんでも良く、事前定義されていない eigyo_bunrui()は、戻り値 int の関数と見なされたと考えています。(以前のコンパイラの処理)

    それに対し、最近(?)のVC++は、最初の引数文字列の中身を見て、"%s"指定があるのに、なぜ、intの関数があるのだ、という警告になったと理解してますが、違うのでしょうか?

    あ、これは、C++  の仕様だという事でしたら、完全に私の理解が間違いで、ゴメンナサイですが。



    2020年6月28日 9:36
  • まず、SALという機能があり、その中でもprintfは書式文字列パラメーターに従い、%sに対応する引数が文字列となっているかのチェックが行われています。

    それとは別に、C言語は大昔の仕様との互換で、宣言も定義されていない関数は引数が未定で戻り値がintであると仮定されます。eigyo_bunruiは下で定義されていますが、printf引数で登場した時点では宣言されていないため int eigyo_bunrui() と解釈されたわけです。
    そのために、%sに対応する一の引数が文字列でなくintだという警告が出たわけです。しかし、これはあくまで警告でありエラーではありません。

    実際にコンパイルエラーを引き起こしているのはその後で、int eigyo_bunrui() と仮定したものの実際の定義は char* eigyo_bunrui() だったためにエラーとしていました。これについては先に回答した通り、 /Za オプションを付けた場合はエラーとして扱われなくなります。

    蛇足ですが、 int eigyo_bunrui() で表されるプロトタイプ宣言は引数無しではなく引数未定です。C言語において引数無しは int eigyo_bunrui(void) と書きます。これも過去との互換が理由です。C++言語ではこのような仕様はないため int eigyo_bunrui() /  int eigyo_bunrui(void) どちらも引数無しと解釈します。

    2020年6月28日 11:54
  • 佐祐理さん、解説ありがとうございます

    ちょっと見てる視点が違ったみたいです。>%sに対応する引数が文字列となっているかのチェックが行われています。
    は、いつからなのかは気になるところですが、そろそろ余分な話ですね。

    2020年6月28日 12:23
  • 情報ありがとうございます。やはり 2015からですか、なので これまで使っていたVC++(6.0?)や もっと昔 MS-DOS時代につかっていたMS-Cでは コンパイルが 通っていたんですね。 疑問が 解けました。

    2020年6月30日 0:35
  • Ht.Hiroさん、こんにちは。フォーラムオペレーターのクモです。
    MSDNフォーラムにご投稿くださいましてありがとうございます。

    本件、皆様より参考になる投稿が寄せられたようでなによりです。

    [回答としてマーク]機能は設定された投稿が後から参照しやすくなりますので、
    同じ問題でお困りの方のためにも参考になった投稿に設定いただけますと幸いです。

    お手数ですが、ご協力の程どうかよろしくお願いいたします。

    引き続きMSDNフォーラムをご利用いただけますようお願い申し上げます。

    MSDN/ TechNet Community Support Kumo ~参考になった投稿には「回答としてマーク」をご設定ください。なかった場合は「回答としてマークされていない」も設定できます。同じ問題で後から参照した方が、情報を見つけやすくなりますので、 ご協力くださいますようお願いいたします。また、MSDNサポートに賛辞や苦情がある場合は、MSDNFSF@microsoft.comまでお気軽にお問い合わせください。~

    2020年6月30日 1:27
    モデレータ
  • > このオプションはVC++ 6.0にも存在しデフォルトもオフのままなので「VC++6.0では 全く問題なかった」のがなぜなのかはわかりません。

    > これはあくまで警告でありエラーではありません。

    > 実際にコンパイルエラーを引き起こしているのはその後で

    と説明していますが、日本語は理解できますか?

    2020年6月30日 11:53