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

Легенда заполняется колонками слева направо. Другими словами, если вы обманом поймете, что есть еще одна строка (без текста или цвета строки в легенде), вы можете заполнить пространство под «plot 3».

import numpy as np
import matplotlib.pyplot as plt
from pylab import *

X = np.linspace(0,100,11)

plt.plot(X,-X, label='plot 1', color='red')
plt.plot(X,-2*X, label='plot 2', color='green')
plt.plot(X,-3*X, label='plot 3', color='blue')


line1 = Line2D(range(10), range(10), marker='', color="red")
line2 = Line2D(range(10), range(10), marker='',color="green")
line3 = Line2D(range(10), range(10), marker='', color="blue")
line4 = Line2D(range(10), range(10), marker='', color="white")
plt.legend((line1,line4, line3,line2),('plot1','','plot3','plot2'),numpoints=1, loc=4,ncol=2)

plt.show()
13
задан Tool 10 June 2015 в 17:50
поделиться

5 ответов

Во-первых, 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] в (*++argv)[0]) этой строки и проверяем, не является ли он '-', так как это обозначает начало опции.

Для второй конструкции, на самом деле, мы хотим пройти по строке, на которую указывает текущий указатель argv[0]. Итак, нам нужно трактовать argv[0] как указатель, игнорировать его первый символ (то есть '-', как мы только что тестировали), и смотреть на другие символы:

++(argv[0]) увеличит argv[0], чтобы получить указатель на первый не - символ, и разыменование даст нам значение этого символа. Итак, мы получаем *++(argv[0]). Но так как в языке Си [] связывает более плотно, чем ++, то на самом деле мы можем избавиться от скобок и получить наше выражение в виде *++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
                 +---+

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

.
40
ответ дан 1 December 2019 в 17:27
поделиться

да, вы правы.

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

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

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

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

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

myApp -t -n

, так и

myApp -tn

и то, и другое и понимается как имеющие опции t и n.

.
5
ответ дан 1 December 2019 в 17:27
поделиться

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

Для ответа на вопрос ++argv увеличивает указатель. Затем к нему применяется индендикация для получения первого символа.

5
ответ дан 1 December 2019 в 17:27
поделиться

Да, оба выражения различаются (хотя и незначительно). 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) будет сложно разобраться, что он на самом деле делает.

.
2
ответ дан 1 December 2019 в 17:27
поделиться

Круглые скобки изменяют порядок, в котором оцениваются выражения.

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

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

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

  1. ++argv увеличивает указатель argv для указания на следующий аргумент.
  2. * присваивает ему значение, чтобы получить указатель на символьные данные.
  3. [0] получает первый символ в символьном массиве.
4
ответ дан 1 December 2019 в 17:27
поделиться
Другие вопросы по тегам:

Похожие вопросы: