Параметры командной строки




Когда программа калькулятора уже была написана и отлажена, выяснилось,что неудобно вначале запускать ее, вводить выражение, а затем выходитьиз калькулятора. Тем более, что обычно нужно просто вычислить одновыражение. Если это выражение задать как параметр командной строкизапуска калькулятора, то можно сэкономить несколько нажатий клавиши. Как уже было сказано, выполнение программы начинается вызовом main().При этом вызове main() получает два параметра: число параметров (обычноназываемый argc) и массив строк параметров (обычно называемый argv).Параметры - это символьные строки, поэтому argv имеет тип char*[argc+1].Имя программы (в том виде, как оно было задано в командной строке)передается в argv[0], поэтому argc всегда не меньше единицы. Например,для командной строки dc 150/1.1934 параметры имеют значения: argc 2 argv[0] "dc" argv[1] "150/1.1934" argv[2] 0 Добраться до параметров командной строки просто; проблема в том, какиспользовать их так, чтобы не менять саму программу. В данном случае этооказывается совсем просто, поскольку входной поток может быть настроенна символьную строку вместо файла ($$10.5.2). Например, можно определитьcin так, чтобы символы читались из строки, а не из стандартноговходного потока: int main(int argc, char* argv[]) { switch(argc) { case 1: // считывать из стандартного входного потока break; case 2: // считывать из строки параметров cin = *new istream(argv[1],strlen(argv[1])); break; default: error("слишком много параметров"); return 1; } // дальше прежний вариант main } При этом istrstream - это функция istream, которая считываетсимволы из строки, являющейся ее первым параметром. Чтобы использоватьistrstream нужно включить в программу файл <strstream.h>, а необычный <iostream.h>. В остальном же программа осталась без изменений,кроме добавления параметров в функцию main() и использования ихв операторе switch. Можно легко изменить функцию main() так, чтобы онамогла принимать несколько параметров из командной строки. Однакоэто не слишком нужно, тем более, что можно нескольких выраженийпередать как один параметр: dc "rate=1.1934;150/rate;19.75/rate;217/rate" Кавычки необходимы потому, что символ ';' служит в системе UNIXразделителем команд. В других системах могут быть свои соглашения опараметрах командной строки.

Сводка операций

Полное и подробное описание операций языка С++ дано в $$R.7. Советуемпрочитать этот раздел. Здесь же приводится краткая сводка операций инесколько примеров. Каждая операция сопровождается одним илинесколькими характерными для нее именами и примером ее использования.В этих примерах class_name обозначает имя класса, member - имя члена,object - выражение, задающее объект класса, pointer - выражение, задающееуказатель, expr - просто выражение, а lvalue (адрес) - выражение,обозначающее не являющийся константой объект. Обозначение (type) задаетимя типа в общем виде (с возможным добавлением *, () и т.д.).Если оно указано без скобок, существуют ограничения. Порядок применения унарных операций и операций присваивания"справа налево", а всех остальных операций - "слева направо".То есть, a=b=c означает a=(b=c), a+b+c означает (a+b)+c, и *p++ означает*(p++), а не (*p)++. ____________________________________________________________ Операции С++============================================================:: Разрешение области видимости class_name:: member:: Глобальное:: name____________________________________________________________. Выбор члена object. member-> Выбор члена pointer -> member[] Индексирование pointer [ expr ]() Вызов функции expr (expr_list)() Структурное значение type (expr_list)sizeof Размер объекта sizeof exprsizeof Размер типа sizeof (type)____________________________________________________________++ Постфиксный инкремент lvalue ++++ Префиксный инкремент ++ lvalue-- Постфиксный декремент lvalue ---- Префиксный декремент -- lvalue~ Дополнение ~ expr! Логическое НЕ! expr- Унарный минус - expr+ Унарный плюс + expr& Взятие адреса & lvalue* Косвенность * exprnew Создание (размещение) new typedelete Уничтожение (освобождение) delete pointerdelete[] Уничтожение массива delete[] pointer() Приведение(преобразование)типа (type) expr____________________________________________________________. * Выбор члена косвенный object. pointer-to-member->* Выбор члена косвенный pointer -> pointer-to-member____________________________________________________________* Умножение expr * expr/ Деление expr / expr% Остаток от деления expr % expr____________________________________________________________+ Сложение (плюс) expr + expr- Вычитание (минус) expr - expr____________________________________________________________ Все операции таблицы, находящиеся между двумя ближайшими другк другу горизонтальными чертами,имеют одинаковый приоритет. Приоритет операций уменьшается придвижении "сверху вниз". Например, a+b*c означает a+(b*c), так как *имеет приоритет выше, чем +; а выражение a+b-c означает (a+b)-c,поскольку + и - имеют одинаковый приоритет, и операции + и -применяются "слева направо". Э____________________________________________________________ Операции С++ (продолжение)============================================================<< Сдвиг влево expr << expr>> Сдвиг вправо expr >> expr____________________________________________________________< Меньше expr < expr<= Меньше или равно expr <= expr> Больше expr > expr>= Больше или равно expr >= expr____________________________________________________________== Равно expr == expr!= Не равно expr!= expr____________________________________________________________& Поразрядное И expr & expr____________________________________________________________^ Поразрядное исключающее ИЛИ expr ^ expr____________________________________________________________| Поразрядное включающее ИЛИ expr | expr____________________________________________________________&& Логическое И expr && expr____________________________________________________________|| Логическое ИЛИ expr || expr____________________________________________________________?: Операция условия expr? expr: expr____________________________________________________________= Простое присваивание lvalue = expr*= Присваивание с умножением lvalue *= expr/= Присваивание с делением lvalue /= expr%= Присваивание с взятием lvalue %= expr остатка от деления+= Присваивание со сложением lvalue += expr-= Присваивание с вычитанием lvalue -= expr<<= Присваивание со сдвигом влево lvalue <<= expr>>= Присваивание со сдвигом вправо lvalue >>= expr&= Присваивание с поразрядным И lvalue &= expr|= Присваивание с поразрядным lvalue |= expr включающим ИЛИ^= Присваивание с поразрядным lvalue ^= expr исключающим ИЛИ____________________________________________________________ Запятая (последовательность) expr, expr____________________________________________________________

Скобки

Синтаксис языка С++ перегружен скобками, и разнообразие их примененийспособно сбить с толку. Они выделяют фактические параметры привызове функций, имена типов, задающих функции, а также служат дляразрешения конфликтов между операциями с одинаковым приоритетом.К счастью, последнее встречается не слишком часто, поскольку приоритетыи порядок применения операций определены так, чтобы выражения вычислялись"естественным образом" (т.е. наиболее распространенным образом).Например, выражение if (i<=0 || max<i) //... означает следующее: "Если i меньше или равно нулю, или если max меньше i".То есть, оно эквивалентно if ((i<=0) || (max<i)) //... но не эквивалентно допустимому, хотя и бессмысленному выражению if (i <= (0||max) < i) //... Тем не менее, если программист не уверен в указанных правилах,следует использовать скобки, причем некоторые предпочитают длянадежности писать более длинные и менее элегантные выражения, как: if ((i<=0) || (max<i)) //... При усложнении подвыражений скобки используются чаще. Не надо, однако,забывать, что сложные выражения являются источником ошибок. Поэтому,если у вас появится ощущение, что в этом выражении нужны скобки,лучше разбейте его на части и введите дополнительную переменную.Бывают случаи, когда приоритеты операций не приводят к "естественному"порядку вычислений. Например, в выражении if (i&mask == 0) // ловушка! & применяется после == не происходит маскирование i (i&mask), а затем проверка результата на 0. Поскольку у == приоритет выше, чем у &, это выражение эквивалентно i&(mask==0). В этом случае скобки играют важную роль: if ((i&mask) == 0) //... Имеет смысл привести еще одно выражение, которое вычисляется совсем не так, как мог бы ожидать неискушенный пользователь: if (0 <= a <= 99) //... Оно допустимо, но интерпретируется как (0<=a)<=99, и результат первогосравнения равен или 0, или 1, но не значению a (если, конечно,a не есть 1). Проверить, попадает ли a в диапазон 0...99, можно так: if (0<=a && a<=99) //... Среди новичков распространена ошибка, когда в условии вместо ==(равно) используют = (присвоить): if (a = 7) // ошибка: присваивание константы в условии //... Она вполне объяснима, поскольку в большинстве языков "=" означает "равно".Для транслятора не составит труда сообщать об ошибках подобного рода.

Порядок вычислений

Порядок вычисления подвыражений, входящих в выражение, не всегдаопределен. Например: int i = 1; v[i] = i++; Здесь выражение может вычисляться или как v[1]=1, или как v[2]=1.Если нет ограничений на порядок вычисления подвыражений, то трансляторполучает возможность создавать более оптимальный код. Трансляторуследовало бы предупреждать о двусмысленных выражениях, но к сожалениюбольшинство из них не делает этого. Для операций && ||, гарантируется, что их левый операнд вычисляется раньше правого операнда.Например, в выражении b=(a=2,a+1) b присвоится значение 3. Примероперации || был дан в $$3.2.1, а пример операции && есть в $$3.3.1.Отметим, что операция запятая отличается по смыслу от той запятой, котораяиспользуется для разделения параметров при вызове функций. Пусть естьвыражения: f1(v[i],i++); // два параметра f2((v[i],i++)) // один параметр Вызов функции f1 происходит с двумя параметрами: v[i] и i++, нопорядок вычисления выражений параметров неопределен. Зависимостьвычисления значений фактических параметров от порядка вычислений- далеко не лучший стиль программирования. К тому же программастановится непереносимой.Вызов f2 происходит с одним параметром, являющимся выражением,содержащим операцию запятая: (v[i], i++). Оно эквивалентно i++. Скобки могут принудительно задать порядок вычисления. Например,a*(b/c) может вычисляться как (a*b)/c (если только пользовательвидит в этом какое-то различие). Заметим, что для значений с плавающейточкой результаты вычисления выражений a*(b/c) и (a*b)/ могутразличаться весьма значительно.

Инкремент и декремент

Операция ++ явно задает инкремент в отличие от неявного его заданияс помощью сложения и присваивания. По определению ++lvalue означаетlvalue+=1, что, в свою очередь означает lvalue=lvalue+1 при условии,что содержимое lvalue не вызывает побочных эффектов. Выражение,обозначающее операнд инкремента, вычисляется только один раз. Аналогичнообозначается операция декремента (--). Операции ++ и -- могутиспользоваться как префиксные и постфиксные операции. Значением ++xявляется новое (т. е. увеличенное на 1) значение x. Например, y=++xэквивалентно y=(x+=1). Напротив, значение x++ равно прежнему значению x.Например, y=x++ эквивалентно y=(t=x,x+=1,t), где t - переменная тогоже типа, что и x. Напомним, что операции инкремента и декремента указателяэквивалентны сложению 1 с указателем или вычитанию 1 из указателя, причемвычисление происходит в элементах массива, на который настроенуказатель. Так, результатом p++ будет указатель на следующий элемент.Для указателя p типа T* следующее соотношение верно по определению: long(p+1) == long(p) + sizeof(T); Чаще всего операции инкремента и декремента используются дляизменения переменных в цикле. Например, копирование строки,оканчивающейся нулевым символом, задается следующим образом: inline void cpy(char* p, const char* q) { while (*p++ = *q++); } Язык С++ (подобно С) имеет как сторонников, так и противников именноиз-за такого сжатого, использующего сложные выражения стиляпрограммирования. Оператор while (*p++ = *q++); вероятнее всего, покажется невразумительным для незнакомых с С.Имеет смысл повнимательнее посмотреть на такие конструкции, посколькудля C и C++ они не является редкостью. Сначала рассмотрим более традиционный способ копирования массивасимволов: int length = strlen(q) for (int i = 0; i<=length; i++) p[i] = q[i]; Это неэффективное решение: строка оканчивается нулем; единственныйспособ найти ее длину - это прочитать ее всю до нулевого символа;в результате строка читается и для установления ее длины, и длякопирования, то есть дважды. Поэтому попробуем такой вариант: for (int i = 0; q[i]!=0; i++) p[i] = q[i]; p[i] = 0; // запись нулевого символа Поскольку p и q - указатели, можно обойтись без переменной i,используемой для индексации: while (*q!=0) { *p = *q; p++; // указатель на следующий символ q++; // указатель на следующий символ } *p = 0; // запись нулевого символа Поскольку операция постфиксного инкремента позволяет сначала использоватьзначение, а затем уже увеличить его, можно переписать цикл так: while (*q!= 0) { *p++ = *q++; } *p = 0; // запись нулевого символа Отметим, что результат выражения *p++ = *q++ равен *q. Следовательно,можно переписать наш пример и так: while ((*p++ = *q++)!= 0) { } В этом варианте учитывается, что *q равно нулю только тогда, когда*q уже скопировано в *p, поэтому можно исключить завершающееприсваивание нулевого символа. Наконец, можно еще более сократитьзапись этого примера, если учесть, что пустой блок не нужен, аоперация "!= 0" избыточна, т.к. результат условного выражения и таквсегда сравнивается с нулем. В результате мы приходим кпервоначальному варианту, который вызывал недоумение: while (*p++ = *q++); Неужели этот вариант труднее понять, чем приведенные выше? Тольконеопытным программистам на С++ или С! Будет ли последний вариантнаиболее эффективным по затратам времени и памяти? Если не считатьпервого варианта с функцией strlen(), то это неочевидно. Какой извариантов окажется эффективнее, определяется как спецификой системыкоманд, так и возможностями транслятора. Наиболее эффективный алгоритмкопирования для вашей машины можно найти в стандартной функции копированиястрок из файла <string.h>: int strcpy(char*, const char*);


Поделиться:




Поиск по сайту

©2015-2024 poisk-ru.ru
Все права принадлежать их авторам. Данный сайт не претендует на авторства, а предоставляет бесплатное использование.
Дата создания страницы: 2016-02-16 Нарушение авторских прав и Нарушение персональных данных


Поиск по сайту: