【问题标题】:Difference between (*++argv)[0] and while(c = *++argv[0])(*++argv)[0] 和 while(c = *++argv[0]) 之间的区别
【发布时间】:2011-01-02 12:27:07
【问题描述】:

我有以下sn-p的代码:

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 数组中被指向的下一个字符的指针?

【问题讨论】:

  • 我还对一件事感兴趣:while(c = *++argv[0]) 这个表达式。这实际上是否意味着:while(c = *++argv[0] != 0),我的意思是如果 *++argv[0] 没有找到一个字符,它是否返回一个指向 c 的空指针?
  • 如我的回答中所述,请参阅 K&R 在此代码上的勘误表条目:cm.bell-labs.com/cm/cs/cbook/2ediffs.html

标签: c string pointers argv


【解决方案1】:

首先,K&R 对此特定 sn-p 有一个勘误表:

117(§5.10):在 find 示例中,程序递增 argv[0]。这没有特别禁止,但也没有特别允许。

现在解释一下。

假设您的程序名为prog,并且您使用:prog -ab -c Hello World 执行它。您希望能够解析参数以说明选项 a、b 和 c 已指定,而 Hello 和 World 是非选项参数。

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。取消引用的效果是获得指向第一个命令行参数-ab 的指针。最后,我们取这个字符串的第一个字符((*++argv)[0] 中的[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
                 +---+

这有帮助吗?

【讨论】:

  • @Alok 你能解释一下这一步吗:: if((strstr(line, *argv) != NULL) != except) ?
  • @AbhimanyuAryan strstr(a, b) 检查字符串 b 是否存在于 a 中。如果b 不在a 中,则返回NULL。所以,strstr(line, *argv) != NULL 检查argv 指向的字符串是否在line 中,如果是,则为1,如果不是,则为0。根据 x 标志的存在,except 之前被设置为 1 或 0。
【解决方案2】:

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

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

【讨论】:

  • 实际上,间接寻址从 - 之后的第一个字符开始,每个循环都移动到下一个字符,以支持单个 - 字符之后的选项标志簇。
  • 我指的是 (*++argv)[0] == '-'
【解决方案3】:

是的,你是对的。

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

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

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

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

这种设计允许两者

myApp -t -n

和

myApp -tn

工作并被理解为拥有t 和n 选项。

【讨论】:

  • 这种设计很简单,而且大部分都是合理的,除了它修改了 argc 和数组 argv 的内容,这是一个糟糕的设计,因为它阻止了进一步使用这些变量。
【解决方案4】:

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

不带括号*++argv[0]:

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

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

  1. ++argv 递增 argv 指针以指向下一个参数。
  2. * 引用它以获得指向字符数据的指针。
  3. [0] 获取字符数组中的第一个字符。

【讨论】:

    【解决方案5】:

    是的,这两种表达方式不同(尽管只是略有不同)。 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)都很难弄清楚它的真正作用。

    【讨论】:

    • 但此代码不会检测选项链 - 您需要另一个迭代器来遍历选项链 -tn
    • @Alex Brown:我相信我已经解决了这个问题——尽管我不确定它是否一定是真正的改进。当典型的终端是电传打字机时,允许 -tn 而不是 -t -n 意味着相当多,但现在几乎不值得了。
    • @Jerry 这完全值得。每个命令行用户都希望能够在一个组中提供单字母选项,而对代码的懒惰意味着违反了那些强烈的期望。这里更深层次的问题是这个功能的自定义编码,而不是使用 getopt 或类似的。
    • @Novelocrat:恐怕我真的不能同意——很多命令行工具要么根本不允许聚集参数,要么对哪些参数可以和可以有特定限制' t 被聚类。没有任何经验丰富的人可以诚实地对这个主题抱有太大的期望。鉴于它只支持两个参数,没有任何关联的参数,我可以看到使用 getopt 可能会使代码更复杂,所以我可以理解不使用它,即使我同意它可能无论如何应该。
    • 你试着把 tar -xvzf a.tar.gz 从我身边拿走,看看会发生什么。或者 ls -laTr,或者 ps -elF,等等等等。
    猜你喜欢
    • 2015-01-10
    • 2014-09-22
    • 2014-12-02
    • 1970-01-01
    • 2021-11-09
    • 1970-01-01
    • 2015-04-30
    • 2023-03-15
    • 1970-01-01
    相关资源
    最近更新 更多