Разница между (* ++ argv) [0] и while (c = * ++ argv [0]) - PullRequest
13 голосов
/ 07 января 2010

У меня есть следующий фрагмент кода:

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] означает указатель на следующий символ в текущем массиве символов, на который указывает указатель?

Ответы [ 5 ]

40 голосов
/ 07 января 2010

Во-первых, у K & R есть ошибки в этом конкретном фрагменте:

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. Наконец, мы берем первый символ ([0] в (*++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
                 +---+

Помогает ли это?

5 голосов
/ 07 января 2010

Инкремент argv - очень плохая идея, так как после того, как вы это сделали, трудно вернуть исходное значение. Проще, понятнее и лучше использовать целочисленный индекс - ведь argv - это массив!

Чтобы ответить на ваш вопрос ++ argv увеличивает указатель. Затем к нему применяется косвенное обращение, чтобы получить первый символ.

5 голосов
/ 07 января 2010

да, вы правы.

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

сканирует массив (длины argc) аргументов командной строки один за другим, ища те, которые начинаются с префикса опции -. Для каждого из них:

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

сканирует набор символов-переключателей, следующих за первым - в текущем аргументе (т. Е. t и n в -tn, до тех пор, пока не достигнет нулевого ограничителя строки \0, который завершает цикл while, поскольку он оценивается как ложный.

Эта конструкция допускает как

myApp -t -n

и

myApp -tn

и работает, и понимается как наличие опций t и n.

4 голосов
/ 07 января 2010

Скобки изменяют порядок вычисления выражений.

без скобок *++argv[0]:

  1. argv[0] получает указатель на символьные данные, на которые в данный момент указывает argv.
  2. ++ увеличивает этот указатель на следующий символ в массиве символов.
  3. * получает персонажа.

с круглыми скобками (*++argv)[0]:

  1. ++argv увеличивает указатель argv для указания следующего аргумента.
  2. * разыменовывает его, чтобы получить указатель на символьные данные.
  3. [0] получает первый символ в массиве символов.
2 голосов
/ 07 января 2010

Да, два выражения отличаются (хотя и незначительно). ИМО, этот код немного слишком умный. Вам было бы лучше с чем-то вроде этого:

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) будет трудно понять, что он на самом деле делает.

...