web-dev-qa-db-ja.com

argvを変更することは可能ですか、それとも調整されたコピーを作成する必要がありますか?

私のアプリケーションには潜在的に膨大な数の引数が渡されており、引数をフィルターされたリストに複製するヒットのメモリを避けたいと思います。それらを適切にフィルタリングしたいのですが、argv配列自体、またはそれが指すデータをいじることはおそらくお勧めできません。助言がありますか?

29
ojblass

Argvがmainメソッドに渡されたら、他のC配列と同じように扱うことができます。必要に応じて変更し、何をしているのかを知っておいてください。配列の内容は、コードで明示的に行う以外は、戻りコードやプログラムの実行に影響を与えません。特別に扱うことが「お勧めできない」理由は考えられません。

もちろん、argvの範囲を超えて誤ってメモリにアクセスすることに注意する必要があります。通常のC配列のようにアクセスできることの裏側は、他の通常のC配列と同じようにエラーにアクセスする傾向があることです。 (コメントやその他の回答でこれを指摘してくれたすべての人に感謝します!)

24
Tim

C99標準では、argv(およびargc)の変更について次のように述べています。

パラメータargcとargv、およびargv配列が指す文字列は、プログラムによって変更可能であり、プログラムの起動から終了までの間、最後に格納された値を保持する必要があります。

67
Michael Burr

C標準の最新ドラフト(N1256)は、main関数には2つの許可された形式があると述べています。

int main (void);
int main (int argc, char* argv[]);

しかし、核心は「または他の実装定義の方法で」という節です。これは、セミトレーラーを運転するのに十分な大きさの標準の抜け穴のように思えます。

argv"const char *"を使用して、引数の変更を禁止する人もいます。メイン関数がそのように定義されている場合、次のプログラムで証明されているように、argv[]が指す文字を変更することは許可されていません。

pax> cat qq.c
#include <stdio.h>
int main (int c, const char *v[]) {
    *v[1] = 'X';
    printf ("[%s]\n", v[1]);
    return 0;
}

pax> gcc -o qq qq.c
qq.c: In function `main':
qq.c:3: error: assignment of read-only location

ただし、"const"を削除すると、正常に機能します。

pax> cat qq2.c
#include <stdio.h>
int main (int c, char *v[]) {
    *v[1] = 'X';
    printf ("[%s]\n", v[1]);
    return 0;
}

pax> gcc -o qq2 qq2.c ; ./qq2
[Xello]

これはC++にも当てはまると思います。現在のドラフトは次のように述べています。

All implementations shall allow both of the following definitions of main:
    int main();
    int main(int argc, char* argv[]);

ただし、他のバリアントを明確に禁止しているわけではないため、おそらくC++でも"const"バージョンを受け入れることができます(実際、g ++も受け入れます)。

注意する必要があるのは、要素のサイズを大きくしようとすることだけです。標準はnot保存方法を義務付けているため、1つの引数を拡張すると、他の引数やその他の無関係なデータに影響を与える可能性があります(おそらく影響します)。

6
paxdiablo

経験的に、GNU getopt()などの関数は、問題を引き起こすことなく引数リストを並べ替えます。@ Timが言うように、賢明にプレイする限り、ポインターの配列、さらには個々の文字列を操作できます。 。暗黙の配列境界をオーバーランしないでください。

4

オペレーティングシステムargvとargcを実行する前にアプリケーションスタックにプッシュすると、他のスタック変数と同じように扱うことができます。

2
ZelluX

一部のライブラリはこれを行います!

Glut openglライブラリによって提供される初期化メソッド (GlutInit) glut関連の引数をスキャンし、argv内の後続の要素を前方に移動することによってそれらをクリアします(実際の文字列ではなくポインタを移動します) )および引数のデクリメント

2.1

glutInit glutInitは、GLUTライブラリを初期化するために使用されます。

使用法

void glutInit(int * argcp、char ** argv);

argcp

Mainからのプログラムの変更されていないargc変数へのポインター。 glutInitはGLUTライブラリを対象としたコマンドラインオプションを抽出するため、戻ると、argcpが指す値が更新されます。

argv

Mainからのプログラムの変更されていないargv変数。 argcpと同様に、glutInitはGLUTライブラリによって理解されるコマンドラインオプションを抽出するため、argvのデータが更新されます。

1
Lanting

Argvを直接操作するのはbadのアイデアであると私が言うのは、アプリケーションがargv [0]の内容に応じて動作を変更するときだけです。

ただし、argv [0]に応じてプログラムの動作を変更すること自体は、移植性が懸念される非常にbadの考えです。

それ以外は、他のアレイと同じように扱うことができます。ジョナサンが言ったように、GNU getopt()は引数リストを非破壊的に並べ替えますが、引数のシリアル化やハッシュさえも行う他のgetopt()実装を見てきました(プログラムの場合に便利です) ARG_MAXに近づきます)。

ポインタ演算には注意してください。

1
Tim Post

argvの元の割り当ては、コンパイラ/ランタイムの選択として残されます。したがって、それはmayそれを意地悪に変更するのは安全ではありません。多くのシステムがスタック上に構築するため、mainが戻ると自動的に割り当てが解除されます。他の人はそれをヒープ上に構築し、メインが戻ったときにそれを解放します(または解放しません)。

引数の値を長くしようとしない限り、引数の値を変更しても安全です(バッファオーバーランエラー)。引数の順序を入れ替えても安全です。

remove引数を前処理すると、次のように機能します。

(チェックされていない多くのエラー条件、最初の引数がチェックされていない「-special」など。これは結局のところ、単なる概念のデモです。)

int main(int argc, char** argv)
{
    bool doSpecial = false; // an assumption
    if (0 == strcmp(argv[1], "--special"))
    {
        doSpecial = true; // no longer an assumption
        // remove the "--special" argument
        //  but do copy the NULL at the end.
        for(int i=1; i<argc; ++i)
            argv[i]  = argv[i+1];
        --argc;
    }
    // all normal processing with "--special" removed.
    // the doSpecial flag is available if wanted.
    return 0;
}

ただし、完全な操作については、これを参照してください:( argvスタイルのベクトルを操作するために使用されるlibibertyライブラリの一部)

http://www.opensource.Apple.com/source/gcc/gcc-5666.3/libiberty/argv.c

ライセンス供与されているGNU LGPL。

0
Jesse Chisholm