Подтвердить что ты не робот

Разница между (* ++ argv) [0] и while (c = * ++ argv [0])

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

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, который указывает на?

4b9b3361

Ответ 1

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

117 (§5.10): В примере поиска программа увеличивает 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] 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
                 +---+

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

Ответ 2

да, вы правы.

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

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

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

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

Эта конструкция позволяет как

myApp -t -n

и

myApp -tn

чтобы оба работали и понимались как имеющие параметры t и n.

Ответ 3

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

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

Ответ 4

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

Без круглых скобок *++argv[0]:

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

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

  • ++argv увеличивает указатель argv на следующий аргумент.
  • * отменяет его, чтобы получить указатель на данные символа.
  • [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 вообще) будет трудно понять, что он на самом деле делает.