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):在find示例中,程序递增argv[0]. 这没有特别禁止,但也没有特别允许。

现在解释一下。

假设您的程序名为prog,并且您使用以下命令执行它:prog -ab -c Hello World。您希望能够解析参数以说明 options a, bandc被指定,并且HelloandWorld是非选项参数。

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"

In (*++argv)[0],因为有括号,argv首先递增,然后取消引用。增量的效果是将该argv ---------->箭头“向下移动一个块”,指向1. 取消引用的效果是获得指向第一个命令行参数的指针,-ab. 最后,我们取这个字符串的第一个字符 ( [0]in (*++argv)[0]),并测试它是否是'-',因为它表示选项的开始。

对于第二个构造,我们实际上想要遍历当前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

增加 argv 是一个非常糟糕的主意,因为一旦你这样做了,就很难恢复原来的值。使用整数索引更简单、更清晰、更好——毕竟 argv 是一个数组!

要回答您的问题 ++argv 会增加指针。然后对其应用间接以获取第一个字符。

于 2010-01-07T14:34:24.743 回答
5

是的,你是对的。

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

正在逐一扫描命令行参数的数组(长度为 argc),以查找以-选项前缀开头的参数。对于其中的每一个:

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

正在扫描-当前参数中第一个后面的开关字符集(即tnin -tn,直到它遇到字符串 null 终止符\0,这将终止 while 循环,因为它的计算结果为 false。

这种设计允许两者

myApp -t -n

myApp -tn

既能工作又能被理解为拥有选择权tn.

于 2010-01-07T14:32:52.530 回答
4

括号改变了表达式的计算顺序。

没有括号*++argv[0]

  1. argv[0]获取指向当前指向的字符数据的指针argv
  2. ++将该指针递增到字符数组中的下一个字符。
  3. *得到角色。

带括号(*++argv)[0]

  1. ++argv递增 argv 指针以指向下一个参数。
  2. *取消引用它以获得指向字符数据的指针。
  3. [0]获取字符数组中的第一个字符。
于 2010-01-07T15:00:55.283 回答
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 回答