13

次のコードスニペットがあります。

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

     char line[MAXLINE];
     long lineno = 0;
     int c, except = 0, number = 0, found = 0;

     while(--argc > 0 && (*++argv)[0] == '-') //These two lines
        while(c = *++argv[0])                 //These two lines
          switch(c) {
             case 'x':
                  except = 1;
                  break;
             case 'n':
                  number = 1;
                  break;
             default:
                  printf("find: illegal option %c\n", c);
                  argc = 0;
                  found = -1;
                  break;
          }

     ...
}

次の式が含まれています。

while(--argc > 0 && (*++argv)[0] == '-')

括弧内のこの表現は、括弧なしとは(*++argv)[0]異なりwhile(c = *++argv[0])ますか?

もしそうなら、どのように?(*++argv)次の引数へのポインタを*++argv[0]意味し、ポイントされている現在のchar配列内の次の文字へのポインタを意味しますか?

4

5 に答える 5

39

まず、K&Rはこの特定のスニペットに正誤表があります。

117(§5.10):検索の例では、プログラムは増分しますargv[0]。これは特に禁止されていませんが、特に許可されていません。

さて、説明のために。

プログラムの名前progがで、次のように実行するとしますprog -ab -c Hello Worlda引数を解析して、オプション、bおよびcが指定され、およびHelloWorld非オプション引数であると言うことができるようにする必要があります。

argvは型char **です—関数の配列パラメータはポインタと同じであることに注意してください。プログラムの呼び出しでは、次のようになります。

                 +---+         +---+---+---+---+---+
 argv ---------->| 0 |-------->| p | r | o | g | 0 |
                 +---+         +---+---+---+---+---+
                 | 1 |-------->| - | a | b | 0 |
                 +---+         +---+---+---+---+
                 | 2 |-------->| - | c | 0 |
                 +---+         +---+---+---+---+---+---+
                 | 3 |-------->| H | e | l | l | o | 0 |
                 +---+         +---+---+---+---+---+---+
                 | 4 |-------->| W | o | r | l | d | 0 |
                 +---+         +---+---+---+---+---+---+
                 | 5 |-------->NULL
                 +---+

ここで、argcは5、argv[argc]はですNULL。最初はargv[0]char *文字列を含む"prog"です。

では(*++argv)[0]、括弧があるため、argv最初にインクリメントされ、次に逆参照されます。増分の効果は、そのargv ---------->矢印を「1ブロック下」に移動して、を指すようにすること1です。間接参照の効果は、最初のコマンドライン引数へのポインターを取得すること-abです。最後に、この文字列の最初の文字([0]in (*++argv)[0])を取得し、それがオプションであるかどうかをテストし'-'ます。これは、オプションの開始を示しているためです。

2番目の構成では、実際には、現在のargv[0]ポインターが指す文字列をたどります。したがって、argv[0]ポインタとして扱い、最初の文字を無視して(つまり'-'、テストしたばかりのように)、他の文字を確認する必要があります。

++(argv[0])はインクリメントargv[0]し、最初の非-文字へのポインタを取得し、それを逆参照すると、その文字の値が得られます。だから私たちは得る*++(argv[0])。ただし、Cでは、[]よりも緊密にバインドされるため++、実際には括弧を削除して、式をとして取得できます*++argv[0]0この文字が(上の画像の各行の最後の文字ボックス)になるまで処理を続けたいと思います。

表現

c = *++argv[0]

c現在のオプションの値に割り当て、cは。 while(c)はの省略形でwhile(c != 0)あるため、このwhile(c = *++argv[0])行は基本的に現在のオプションの値をに割り当ててcテストし、現在のコマンドライン引数の最後に到達したかどうかを確認します。

このループの終わりに、argvは最初の非オプション引数を指します。

                 +---+         +---+---+---+---+---+
                 | 0 |-------->| p | r | o | g | 0 |
                 +---+         +---+---+---+---+---+
                 | 1 |-------->| - | a | b | 0 |
                 +---+         +---+---+---+---+
                 | 2 |-------->| - | c | 0 |
                 +---+         +---+---+---+---+---+---+
 argv ---------->| 3 |-------->| H | e | l | l | o | 0 |
                 +---+         +---+---+---+---+---+---+
                 | 4 |-------->| W | o | r | l | d | 0 |
                 +---+         +---+---+---+---+---+---+
                 | 5 |-------->NULL
                 +---+

これは役に立ちますか?

于 2010-01-07T15:22:18.453 に答える
5

はい。それで合っています。

while(--argc > 0 && (*++argv)[0] == '-')

-コマンドライン引数の配列(長さargc)を1つずつスキャンして、オプションプレフィックスで始まるものを探します。それらのそれぞれについて:

while(c = *++argv[0])

-は、現在の引数の最初に続くスイッチ文字のセットをスキャンします(つまりt、文字列nullターミネータに到達するまでn、whileループを終了します。これは、falseと評価されるためです。-tn\0

この設計により、両方が可能になります

myApp -t -n

myApp -tn

t両方の仕事をし、オプションとを持っていると理解されますn

于 2010-01-07T14:32:52.530 に答える
5

argvをインクリメントすることは非常に悪い考えです。一度インクリメントすると、元の値を取り戻すのが困難になるためです。整数インデックスを使用する方が簡単で、明確で、優れています-結局のところ、argvは配列です!

あなたの質問に答えるために、++argvはポインタをインクリメントします。次に、これに間接参照が適用され、最初の文字が取得されます。

于 2010-01-07T14:34:24.743 に答える
4

括弧は、式が評価される順序を変更します。

括弧なし*++argv[0]

  1. argv[0]が現在指している文字データへのポインタを取得しますargv
  2. ++文字配列内の次の文字へのポインタをインクリメントします。
  3. *キャラクターを取得します。

括弧付き(*++argv)[0]

  1. ++argv次の引数を指すようにargvポインタをインクリメントします。
  2. *文字データへのポインタを取得するためにそれをdefereferenceします。
  3. [0]文字配列の最初の文字を取得します。
于 2010-01-07T15:00:55.283 に答える
2

はい、2つの式は異なります(わずかですが)。IMO、このコードは少し賢い側にあります。次のようなものを使用したほうがよいでしょう。

for (int i=1; i<argc; i++)
    if (argv[i][0] == '-') {
       size_t len = strlen(argv[i]);
       for (int j=0; j<len; ++j)
           switch(argv[i][j]) {
               case 'x':
               // ...

これは上記のコードとほとんど同じですが、(Cをまったく知っている)誰もが実際に何をしているのかを理解するのに苦労することはないと思います。

于 2010-01-07T14:34:41.653 に答える