web-dev-qa-db-ja.com

POSIXシステムでargcをゼロにできますか?

メインプログラムの標準定義が与えられた場合:

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

POSIXシステムではどのような状況でargcをゼロにできますか?

72
Sylvain Leroux

はい、可能です。次のようにプログラムを呼び出す場合:

execl("./myprog", NULL, (char *)NULL);

または交互に:

char *args[] = { NULL };
execv("./myprog", args);

「myprog」では、argcは0になります。

標準 は、ホスト環境でのプログラムの起動に関するセクション5.1.2.2.1に記載されているように、0 argcも具体的に許可します。

1プログラムの起動時に呼び出される関数の名前はmainです。実装は、この関数のプロトタイプを宣言しません。 intの戻り値の型で、パラメーターなしで定義されます。

int main(void) { /* ... */ } 

または2つのパラメーター(ここではargcおよびargvと呼ばれますが、それらは宣言されている関数に対してローカルであるため、任意の名前を使用できます):

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

または同等;または他の実装定義の方法で。

2宣言されている場合、main関数のパラメーターは次の制約に従う必要があります。

  • argcの値は非負でなければなりません。
  • argv[argc]はNULLポインターでなければなりません。

...

また、これはargcが0の場合、argv[0]がNULLになることが保証されることにも注意してください。ただし、printf%s指定子の引数として使用された場合にNULLポインターを処理する方法は、標準では詳しく説明されていません。この場合、多くの実装は「(null)」を出力しますが、保証されているとは思いません。

89
dbush

他の答えに追加するために、Cには何も(POSIXであるかどうかに関係なく)main()がプログラム内の関数として呼び出されるのを妨げるものはありません。

int main(int argc, int argv[]) {
    if (argc == 0) printf("Hey!\n");
    else main(0,NULL);

    return 0;
}
22
Possum

はい、ゼロにすることができます。つまり、argv[0] == NULLです。

argv[0]がプログラムの名前であるという規則です。 execve familyのようにバイナリを起動し、引数を指定しない場合は、argc == 0を使用できます。プログラム名に近くない文字列を指定することもできます。 argv[0]を使用してプログラムの名前を取得することが完全に信頼できるわけではないのはそのためです。

通常、コマンドラインを入力するシェルは常にプログラム名を最初の引数として追加しますが、これも慣習です。 argv[0] == "--help"で getopt を使用してオプションを解析する場合、optindは1に初期化されるため、検出されませんが、optindを0に、getoptを使用すると、「help」の長いオプションが表示されます。

長い話:argc == 0を持つことは完全に可能です(argv[0]はそれ自体特別ではありません)。ランチャーがまったく引数を与えない場合に発生します。

19
Tom's

初期の提案では、main()に渡されるargcの値は「​​1つ以上」である必要がありました。これは、ISO C規格のドラフトでの同じ要件によって推進されました。実際、exec関数の呼び出し元に引数が指定されていない場合、歴史的な実装ではゼロの値が渡されています。この要件はISO C規格から削除され、その後、POSIX.1-2017のこのボリュームからも削除されました。言葉遣い、特にWordの使用には、厳密に準拠するPOSIXアプリケーションが少なくとも1つの引数をexec関数に渡す必要があるため、そのようなアプリケーションによって呼び出されたときにargcが1以上になることが保証されます。実際、多くの既存のアプリケーションは、最初にargcの値をチェックせずにargv [0]を参照するため、これは良い習慣です。

厳密に準拠するPOSIXアプリケーションの要件では、最初の引数として渡される値は、開始されるプロセスに関連付けられたファイル名文字列であることも明記されています。既存のアプリケーションの中には、状況によってはファイル名文字列ではなくパス名を渡すものもありますが、argv [0]の一般的な使用方法は診断の印刷であるため、ファイル名文字列の方が一般に便利です。場合によっては、渡されるファイル名はファイルの実際のファイル名ではありません。たとえば、ログインユーティリティの多くの実装では、実際のファイル名の前に( '-')を付けるという規則を使用します。これは、呼び出されるコマンドインタープリターに「ログインシェル」であることを示します。

また、テストおよび[ユーティリティでは、すべての実装で決定的な動作を行うために、argv [0]引数に特定の文字列が必要です。

ソース


POSIXシステムでargcをゼロにすることはできますか?

はい。ただし、POSIXに厳密には準拠していません。

15
Stargateur

./a.outのような実行可能ファイルを実行する場合は常に、program nameである1つの引数があります。ただし、empty argument listを指定してargcを呼び出す別のプログラムから実行することにより、Linuxでzeroを使用してプログラムをexecvとして実行することができます。

例えば

int main() {
    char *buf[] = { NULL };
    execv("./exe", buf); /* exe is binary which it run with 0 argument */
    return 0;
}
3
Achal

TL; DR:はい、argv[0]はNULLにできますが、私が知っている正当な理由のためではありません。ただし、argv[0]がNULLであるかどうかを気にせず、プロセスがクラッシュした場合にプロセスがクラッシュすることを明確に許可する理由があります。


はい、argv[0]は、引数なしで実行された場合にのみ、POSIXシステムではNULLにできます。

より興味深い実用的な質問は、プログラムを気にする必要がありますです。

それに対する答えは"いいえ、あなたのプログラムは仮定することができますargv[0]is not NULL"、いくつかのシステムユーティリティ(コマンドラインユーティリティ)が動作しないためですまたはargv[0] == NULLの場合に非決定的な方法で動作しますが、さらに重要なのは、プロセスがそれを行う正当な理由(愚かさまたは悪意のある目的以外)がないことです。 ( getopt() の標準的な使用も失敗するかどうかはわかりませんが、動作するとは思われません。)

多くのコード、そして実際に私が書いたほとんどの例とユーティリティは、

int main(int argc, char *argv[])
{
    if (argc < 2 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
        printf("Usage: %s [ -h | --help ]\n", argv[0]);
        /* ... print usage ... */
        return EXIT_SUCCESS;
    }

そして、これはreasonableで受け入れ可能です。少なくとも、実行中のコマンドパス、つまりexeclp(cmd, cmd, NULL)を提供せずにプロセスが別のプロセスを実行する正当な理由がないためです。 execlp(cmd, NULL)ではなく。

(ただし、パイプまたはソケットコマンドに関連するタイミングレースウィンドウを悪用するなど、いくつかの悪意のある理由を考えることができます:悪のプロセスは確立されたUnixドメインソケットを介して悪の要求を送信し、すぐに承認された被害者のコマンドに置き換えます(実行最小の起動時間を確保するために引数なしで)、要求を取得するサービスがピアの資格情報をチェックするときに、元の悪のプロセスの代わりに犠牲者コマンドを見るようにします。悪意のあるプロセスにより大きな時間枠を与えるために、「きちんと」動作しようとするのではなく、激しくクラッシュするコマンド(SIGSEGV、NULLポインターの逆参照)。

言い換えると、プロセスがそれ自体を別のプロセスと置き換えるが、引数を使用せずにargcがゼロになるのはpossibleですが、厳密な意味では、そのような動作は無理です。そうするための既知の非悪名高い理由。

このため、そして、私が不道徳で思いやりのないプログラマーとそのプログラムのために人生を難しくするのが大好きであるという事実のために、私は個人的に些細なチェックを決して追加しません、

static int usage(const char *argv0)
{
    /* Print usage using argv0 as if it was argv[0] */
    return EXIT_SUCCESS;
}

int main(int argc, char *argv[])
{
    if (argc < 1)
        return usage("(this)");
    if (argc < 2 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))
        return usage(argv[0]);

    /* argv[0] and argv[1] are non-NULL, argc >= 2 */

特定の既存のユースケースを念頭に置いて誰かから要求された場合を除きます。そして、それでも私は少し疑って、最初にユースケースを検証したいと思います。

3
Nominal Animal