Во-первых, у 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
+---+
Помогает ли это?