web-dev-qa-db-ja.com

C関数のオプションの引数

C関数で、入力引数(私の場合は「値」)が表示されているかどうかを確認したいと思います。

例:

_void Console(char string[], int32_t value)
{
    // write string here
    // write value here, if it exists
}
_

if(value != NULL)ステートメントを使用すると、私のConsole()関数は4096を送信します

引数の存在に基づいて、どのように確認して行動できますか?

10
Eray CANLI

オプションの引数は通常Cでは許可されていません(ただし、それらは C++Ocaml などに存在します)。唯一の例外は 多様な関数printfのような)です。

歴史的に、POSIXの open(2) 関数は、オプションの3番目の引数(それが定義されたとき-1970年代と1980年代-) 呼び出し規約 を受け入れる場合がありました呼び出しスタックに実際にプッシュされた引数なので、その引数を無視するのは簡単に実装できました)。 musl-libc など、Linuxのフリーソフトウェアlibc実装でのopen関数の最近の実装を今日見ると、その src/fcntl/open .c_<stdarg.h>_可変機能 (コンパイラ組み込みとして よく実装される を使用)を使用すること。

ところで、「欠けている」引数を埋めるためにいくつかのマクロを定義することができます。

_  void console(const char*, int32_t);
_

あなたもかもしれない

_  #define console_plain(Msg) console((Msg),0)
_

そして、それは代わりに、いくつかのヘッダー内のいくつかのインライン関数である可能性があります。

_  static void inline console_plain (const char*msg) 
  { console(msg, 0); }
_

次に、console_plain("hello here")を他の場所で使用します

次に、可変個引数関数は、(fixed引数の空でないシーケンスの後)許可される引数と引数を定義する必要があります。そして stdarg(3) を使用して、これらの可変(実際の)引数を取得します。

実際の引数は、実行時ではなく、主にコンパイル時に認識されます。したがって、必要な固定引数から許可される可変引数を定義する規則が必要です。特に、引数が存在することをテストする方法はありません(その情報は実行時に失われます)。

ところで、可変個関数を使用すると、通常、ほとんどのCコンパイラが提供する型チェックが失われます(少なくとも_gcc -Wall -Wextra_などのすべての警告を有効にすると)。 [〜#〜] gcc [〜#〜] を使用している場合、いくつかの function ___attribute___- sformatsentinel、....)それを支援するプロトタイプで。廃止された [〜#〜] melt [〜#〜] を使用してgccをカスタマイズしたり、2019年に GCCプラグイン を使用して、独自の型チェックを行う独自の属性。

引数の存在に基づいて、どのように確認して行動できますか?

現在の通常の呼び出し規約(例: x86-64 ABI の調査)では、一般にそれを行うことはできません(可変関数を使用しないと)。

11

これは、ハッカーを使用して自分またはソースコードを「書き込みモード」で開いた人を潜在的に容易にしようとしているように聞こえます。必要な魔法のマクロがあるため、すべての隠しコードがあるため、そのようなコードは非常に読みにくいため、「書き込みモード」(読み取り/書き込みではない)と言います。 Cはスマート言語ではありません。コンパイラは賢いかもしれませんが、言語のセマンティクスはどちらかと言えば平凡です。ルールを厳密に遵守する必要があります。遵守しないと、悪いソフトウェアを作成したり、さらに悪化させる危険性があります-まったく機能しません。


まったく新しいプログラミング言語を作成せずにこれを行う「正しい」方法は、2つの引数を提供することです。2番目の引数を使用しない場合は、それがNULLとして渡される必要があります。数値型の場合、-1などの数値を除外します。

もう1つの方法は、consoleconsole_fullなどのヒント名を付けて2つの個別の関数を作成することです。それぞれ1つと2つの引数を使用します。

ただし、前述の方法にまだ慣れていない場合は、stdarg.hを含めて実行できます。

void Console (char *string, ...) /* Note the ... */
{
    va_list param;
    int32_t optValue = (-1); /* -1 would indicate optValue is to be ignored */

    // write string here

    va_start(param, string);

    optValue = va_arg(param, int32_t);

    if(optValue != (-1))
    {
        /* Work with `optValue` */
    }

    va_end(param);
}

追加の引数のタイプがわからないため、どちらが適切かはわかりません。また、引数の数もわかりません。これらのことを知るには、printfのような関数と同様に、引数が存在することを示唆する文字列内の特定のトークンを解析し、引数の種類を確認するか、少なくとも引数のconst変数カウンターを使用する必要があります。引数のカウントが自動であることをさらにマクロ化できます。

4
Edenia

1つまたは2つの引数を取る関数呼び出しを区別したい場合は、マクロを使用できます。

希望する動作を再現できますが、注意点がいくつかあります。

  • マクロ実装は、Consoleがマクロであることを認識できないコードのカジュアルな読者に過負荷を隠します。 Cは詳細を見ることを主眼としているため、2つの異なる関数がある場合、おそらくcons_strcons_str_intのように、異なる名前が付けられるはずです。

  • 3つ以上の引数を渡す場合、または引数が必要なタイプのC文字列および整数と互換性がない場合、マクロはコンパイラエラーを生成します。これは実際には良いことです。

  • <stdarg.h>からのインターフェースを使用するprintfのような実際の可変関数は、可変引数の型と数を導出できなければなりません。 printfでは、これは%書式指定子を介して行われます。マクロは、引数の数のみに基づいて、異なる実装間を切り替えることができます。

とにかく、ここに実装があります。注意して続行してください。

#include <stdlib.h>
#include <stdio.h>

#define NARGS(...) NARGS_(__VA_ARGS__, 5, 4, 3, 2, 1, 0)
#define NARGS_(_5, _4, _3, _2, _1, N, ...) N

#define CONC(A, B) CONC_(A, B)
#define CONC_(A, B) A##B

#define Console(...) CONC(Console, NARGS(__VA_ARGS__))(__VA_ARGS__)



void Console1(const char string[])
{
    printf("%s\n", string);
}

void Console2(const char string[], int32_t value)
{
    printf("%s: %d\n", string, value);
}

int main()
{
    Console("Hello");
    Console("Today's number is", 712);

    return 0;
}
3
M Oehm