Важно Простые и составные инструкции C++

Добро пожаловать!

Зарегистрировавшись у нас, вы сможете обсуждать, делиться и отправлять личные сообщения другим участникам нашего сообщества.

Зарегистрироваться!
  • Если Вы желаете помогать развитию проекта, готовы заполнять раздел(-ы) и подсказывать другим пользователям на портале, есть возможность попасть в команду редакторов. Для этого следует обратиться в техническую поддержку
Статус
В этой теме нельзя размещать новые ответы.
Создатель WayDev
Команда форума
Администратор
Регистрация
13 Мар 2022
Сообщения
881
Простейшей формой является пустая инструкция. Вот как она выглядит:
PHP:
; // пустая инструкция

Пустая инструкция используется там, где синтаксис С++ требует употребления инструкции, а логика программы – нет. Например, в следующем цикле while, копирующем одну строку в другую, все необходимые действия производятся внутри круглых скобок (условной части инструкции). Однако согласно правилам синтаксиса С++ после while должна идти инструкция. Поскольку нам нечего поместить сюда (вся работа уже выполнена), приходится оставить это место пустым:
PHP:
while ( *string++ = inBuf++ )
; // пустая инструкция

Случайное появление лишней пустой инструкции не вызывает ошибки компиляции. Например, такая строка:
PHP:
ival = dval + sval;; // правильно: лишняя пустая инструкция
состоит из двух инструкций – сложения двух величин с присваиванием результата переменной ival и пустой.

Простая инструкция состоит из выражения, за которым следует точка с запятой. Например:
PHP:
// простые инструкции
int ival = 1024; // инструкция определения переменной
ival; // выражение
ival + 5; // еще одно выражение
ival = ival +5; // присваивание

Условные инструкции и инструкции цикла синтаксически требуют употребления единственной инструкции, связанной с ними. Однако, как правило, этого недостаточно. В таких случаях употребляются составные инструкции – последовательность простых, заключенная в фигурные скобки:
PHP:
if ( ival0 > ival1 ) {
   // составная инструкция, состоящая
   // из объявления и двух присваиваний
   int temp = ivalO;   ivalO = ival1;   ival1 = temp;
}

Составная инструкция может употребляться там же, где простая, и не нуждается в завершающей точке с запятой.
Пустая составная инструкция эквивалентна пустой простой. Приведенный выше пример с пустой инструкцией можно переписать так:
PHP:
while ( *string++ = *inBuf++ )
{} // пустая инструкция
Составную инструкцию, содержащую определения переменных, часто называют блоком. Блок задает локальную область видимости в программе – идентификаторы, объявленные внутри блока.
 
Создатель WayDev
Команда форума
Администратор
Регистрация
13 Мар 2022
Сообщения
881
Инструкция if.

Инструкция if обеспечивает выполнение или пропуск инструкции или блока в зависимости от условия. Ее синтаксис таков:
PHP:
if ( условие )
инструкция
условие заключается в круглые скобки.
Оно может быть выражением, как в этом примере:
PHP:
if(a+b>c) { ... }
или инструкцией объявления с инициализацией:
PHP:
if ( int ival = compute_value() ){...}

Область видимости объекта, объявленного в условной части, ограничивается ассоциированной с if инструкцией или блоком. Например, такой код вызывает ошибку компиляции:
PHP:
if ( int ival = compute_value() ) {
   // область видимости ival
   // ограничена этим блоком
}
// ошибка: ival невидимif ( ! ival ) ...

Попробуем для иллюстрации применения инструкции if реализовать функцию min(), возвращающую наименьший элемент вектора. Заодно наша функция будет подсчитывать число элементов, равных минимуму. Для каждого элемента вектора мы должны проделать следующее:
  1. Сравнить элемент с текущим значением минимума.
  2. Если элемент меньше, присвоить текущему минимуму значение элемента и сбросить счетчик в 1.
  3. Если элемент равен текущему минимуму, увеличить счетчик на 1.
  4. В противном случае ничего не делать.
  5. После проверки последнего элемента вернуть значение минимума и счетчика.
Необходимо использовать две инструкции if:
PHP:
if ( minVal > ivec[ i ] )...// новое значение minVal
if ( minVal == ivec[ i ] )...// одинаковые значения

Довольно часто программист забывает использовать фигурные скобки, если нужно выполнить несколько инструкций в зависимости от условия:
PHP:
if ( minVal > ivec[ i ] )
minVal = ivec[ i ];
occurs = 1; // не относится к if!
Такую ошибку трудно увидеть, поскольку отступы в записи подразумевают, что и minVal=ivec, и occurs=1 входят в одну инструкцию if. На самом же деле инструкция
PHP:
occurs = 1;
не является частью if и выполняется безусловно, всегда сбрасывая occurs в 1. Вот как должна быть составлена правильная if-инструкция (точное положение открывающей фигурной скобки является поводом для бесконечных споров):

PHP:
if ( minVal > ivec[ i ] )
{
   minVal = ivec[ i ];
   occurs = 1;
}

Вторая инструкция if выглядит так:
PHP:
if ( minVal == ivec [ i ] )
   ++occurs;
Заметим, что порядок следования инструкций в этом примере крайне важен.
Если мы будем сравнивать minVal именно в такой последовательности, наша функция всегда будет ошибаться на 1:

PHP:
if ( minVal > ivec[ i ] ) {
   minVal = ivec[ i ];
   occurs = 1;
}
// если minVal только что получила новое значение,
// то occurs будет на единицу больше, чем нужно
if ( minVal == ivec[ i ] )
   ++occurs;
Выполнение второго сравнения не обязательно: один и тот же элемент не может одновременно быть и меньше и равен minVal. Поэтому появляется необходимость выбора одного из двух блоков в зависимости от условия, что реализуется инструкцией if-else, второй формой if-инструкции. Ее синтаксис выглядит таким образом:
PHP:
if ( условие )
   инструкция1
else
   инструкция2
инструкция1 выполняется, если условие истинно, иначе переходим к инструкция2. Например:
PHP:
if ( minVal == ivec[ i ] )
   ++occurs;
else
   if ( minVal > ivec[ i ] ) {
      minVal = ivec[ i ];
      occurs = 1;
   }
Здесь инструкция2 сама является if-инструкцией. Если minVal меньше ivec, никаких действий не производится.
В следующем примере выполняется одна из трех инструкций:

PHP:
if ( minVal < ivec[ i ] )
{} // пустая инструкция
else
if ( minVal > ivec[ i ] ) {
   minVal = ivec[ i ];
   occurs = 1;
}
else // minVal == ivec[ i ]
++occurs;
Составные инструкции if-else могут служить источником неоднозначного толкования, если частей else больше, чем частей if. К какому из if отнести данную часть else? (Эту проблему иногда называют проблемой висячего else). Например:
PHP:
if ( minVal <= ivec[ i ] )
   if ( minVal == ivec[ i ] )
      ++occurs;
else {
   minVal = ivec[ i ];
   occurs = 1;
}
Судя по отступам, программист предполагает, что else относится к самому первому, внешнему if. Однако в С++ неоднозначность висячих else разрешается соотнесением их с последним встретившимся if. Таким образом, в действительности предыдущий фрагмент означает следующее:
Код:
if ( minVal <= ivec[ i ] ) {
   if ( minVal == ivec[ i ] )
      ++occurs;
   else {
      minVal = ivec[ i ];
      occurs = 1;
   }
}

Одним из способов разрешения данной проблемы является заключение внутреннего if в фигурные скобки:
PHP:
if ( minVal <= ivec[ i ] ) {
   if ( minVal == ivec[ i ] )
      ++occurs;
}
else {
   minVal = ivec[ i ];
   occurs = 1;
}
В некоторых стилях программирования рекомендуется всегда употреблять фигурные скобки при использовании инструкций if-else, чтобы не допустить возможности неправильной интерпретации кода.

Вот первый вариант функции min(). Второй аргумент функции будет возвращать количество вхождений минимального значения в вектор. Для перебора элементов массива используется цикл for. Но мы допустили ошибку в логике программы. Сможете ли вы заметить ее?

PHP:
#include <vector>
int min( const vector<int> &ivec, int &occurs )
{
       int minVal = 0;
       occurs = 0;
       int size = ivec.size();
       for ( int ix = 0; ix < size; ++ix ) {
        if ( minVal == ivec[ ix ] ) ++occurs;     
        else if ( minVal > ivec[ ix ] ) {
            minVal = ivec[ ix ];         
            occurs = 1;     
        }   
    } 
    return minVal;
}
Обычно функция возвращает только одно значение. Однако согласно нашей спецификации в точке вызова должно быть известно не только само минимальное значение, но и количество его вхождений в вектор.

Для возврата второго значения мы использовали параметр типа ссылка. (Параметры-ссылки рассматриваются в разделе 7.3.) Любое присваивание значения ссылке occurs изменяет значение переменной, на которую она ссылается:

PHP:
int main()
{
   int occur_cnt = 0;
   vector< int > ivec;
   // occur_cnt получает значение occurs   // из функции min()   int minval = min( ivec, occur_cnt );
   // ...}


Альтернативой использованию параметра-ссылки является применение объекта класса pair, представленного в разделе 3.14. Функция min() могла бы возвращать два значения в одной паре:
PHP:
// альтернативная реализация
// с помощью пары

#include <uti1ity>
#include <vector>
typedef pair<int,int> min_va1_pair;

min_va1_pairmin( const vector<int> &ivec ) {   
    int minVal = 0;   
    int occurs = 0;
       // то же самое ...
       return make_pair( minVal, occurs );
}
К сожалению, и эта реализация содержит ошибку. Где же она? Правильно: мы инициализировали minVal нулем, поэтому, если минимальный элемент вектора больше нуля, наша реализация вернет нулевое значение минимума и нулевое значение количества вхождений.

Программу можно изменить, инициализировав minVal первым элементом вектора:
PHP:
int minVal = ivec[0];
Теперь функция работает правильно. Однако в ней выполняются некоторые лишние действия, снижающие ее эффективность.


PHP:
// исправленная версия min()
// оставляющая возможность для оптимизации ...
int minVal = ivec[0];occurs = 0;
int size = ivec.size();
for ( int ix = 0; ix < size; ++ix ) {   
    if ( minVal == ivec[ ix ] ) ++occurs;
   // ...
Поскольку ix инициализируется нулем, на первой итерации цикла значение первого элемента сравнивается с самим собой. Можно инициализировать ix единицей и избежать ненужного выполнения первой итерации. Однако при оптимизации кода мы допустили другую ошибку (наверное, стоило все оставить как было!). Сможете ли вы ее обнаружить?
PHP:
// оптимизированная версия min(),
// к сожалению, содержащая ошибку...
int minVal = ivec[0];occurs = 0;
int size = ivec.size();
for ( int ix = 1; ix < size; ++ix ) { 
    if ( minVal == ivec[ ix ] ) ++occurs;
   // ...

Если ivec[0] окажется минимальным элементом, переменная occurs не получит значения 1. Конечно, исправить это очень просто, но сначала надо найти ошибку:
PHP:
int minVal = ivec[0];
occurs = 1;
К сожалению, подобного рода недосмотры встречаются не так уж редко: программисты тоже люди и могут ошибаться. Важно понимать, что это неизбежно, и быть готовым тщательно тестировать и анализировать свои программы.

Вот окончательная версия функции min() и программа main(), проверяющая ее работу:

PHP:
#include <iostream>
#include <vector>
int min( const vector< int > &ivec, int &occurs ) {
       int minVal = ivec[ 0 ];   
    occurs = 1;
       int size = ivec.size();   
    for ( int ix = 1; ix < size; ++ix ) {
        if ( minVal == ivec[ ix ] ) ++occurs;     
        else if ( minVal > ivec[ ix ] ) {           
            minVal = ivec[ ix ];           
            occurs = 1;         
        }   
    }   
    return minVal;
}

int main() {   
    int ia[] = { 9,1,7,1,4,8,1,3,7,2,6,1,5,1 };   
    vector<int> ivec( ia, ia+14 );
       int occurs = 0;   
    int minVal = min( ivec, occurs );
       cout << "Минимальное значение: " << minVal << " встречается: " << occurs << " раз.\n";
       return 0;
}

Результат работы программы:
PHP:
Минимальное значение: 1 встречается: 5 раз.

В некоторых случаях вместо инструкции if-else можно использовать более краткое и выразительное условное выражение. Например, следующую реализацию функции min():

PHP:
template <class valueType>
inline const valueType&
min( valueType &vall, valueType &va12 )
{
       if ( vall < va12 )
          return vall;
       return va12;
}
можно переписать так:
PHP:
template <class valueType>
inline const valueType&
min( valueType &vall, valueType &va12 )
{
       return ( vall < va12 ) ? vall : va12;
}
Длинные цепочки инструкций if-else, подобные приведенной ниже, трудны для восприятия и, таким образом, являются потенциальным источником ошибок.
PHP:
if ( ch == 'a' ||
   ch == 'A' )
      ++aCnt;
else
if ( ch == 'e' ||
   ch == 'E' )
      ++eCnt;
else
   if ( ch == 'i' ||
   ch == 'I' )
         ++iCnt;
else
   if ( ch == 'o' ||
      ch == '0' )
         ++oCnt;
else
   if ( ch == 'u' ||
      ch == 'U' )
         ++uCnt;
В качестве альтернативы таким цепочкам С++ предоставляет инструкцию switch.
 
Создатель WayDev
Команда форума
Администратор
Регистрация
13 Мар 2022
Сообщения
881
Инструкция switch.
Длинные цепочки инструкций if-else, наподобие приведенной в конце предыдущего раздела, трудны для восприятия и потому являются потенциальным источником ошибок. Модифицируя такой код, легко сопоставить, например, разные else и if. Альтернативный метод выбора одного их взаимоисключающих условий предлагает инструкция switch.
Для иллюстрации инструкции switch рассмотрим следующую задачу. Нам надо подсчитать, сколько раз встречается каждая из гласных букв в указанном отрывке текста. (Общеизвестно, что буква e – наиболее часто встречающаяся гласная в английском языке.) Вот алгоритм программы:
  1. Считывать по одному символу из входного потока, пока они не кончатся.
  2. Сравнить каждый символ с набором гласных.
  3. Если символ равен одной из гласных, прибавить 1 к ее счетчику.
  4. Напечатать результат.
Написанная программа была запущена, в качестве контрольного текста использовался раздел из оригинала данной книги. Результаты подтвердили, что буква e действительно самая частая:
PHP:
aCnt: 394
eCnt: 721
iCnt: 461
oCnt: 349
uCnt: 186

Инструкция switch состоит из следующих частей:
1, Ключевого слова switch, за которым в круглых скобках идет выражение, являющееся условием:
PHP:
char ch;
while ( cm >> ch )
  switch( ch )

2. Набора меток case, состоящих из ключевого слова case и константного выражения, с которым сравнивается условие. В данном случае каждая метка представляет одну из гласных латинского алфавита:
PHP:
case 'a':
case 'e':
case 'i':
case 'o':
case 'u':

3. Последовательности инструкций, соотносимых с метками case. В нашем примере с каждой меткой будет сопоставлена инструкция, увеличивающая значение соответствующего счетчика;
4. Необязательной метки default, которая является аналогом части else инструкции if-else. Инструкции, соответствующие этой метке, выполняются, если условие не отвечает ни одной из меток case. Например, мы можем подсчитать суммарное количество встретившихся символов, не являющихся гласными буквами:
PHP:
default: // любой символ, не являющийся гласной
++non_vowe1_cnt;

Константное выражение в метке case должно принадлежать к целому типу, поэтому следующие строки ошибочны:
PHP:
// неверные значения меток
case 3.14: // не целое
case ival: // не константа

Кроме того, две разные метки не могут иметь одинаковое значение.
Выражение условия в инструкции switch может быть сколь угодно сложным, в том числе включать вызовы функций. Результат вычисления условия сравнивается с метками case, пока не будет найдено равное значение или не выяснится, что такого значения нет. Если метка обнаружена, выполнение будет продолжено с первой инструкции после нее, если же нет, то с первой инструкции после метки default (при ее наличии) или после всей составной инструкции switch.
В отличие от if-else инструкции, следующие за найденной меткой, выполняются друг за другом, проходя все нижестоящие метки case и метку default. Об этом часто забывают. Например, данная реализация нашей программы выполняется совершенно не так, как хотелось бы:
PHP:
#include <iostream>
int main() { 
    char ch; 
    int aCnt=0, eCnt=0, iCnt=0, oCnt=0, uCnt=0;
      while ( cin >> ch ) {// Внимание! неверная реализация!
        switch ( ch ) {     
            case 'a': ++aCnt;     
            case 'e': ++eCnt;     
            case 'i': ++iCnt;     
            case 'o': ++oCnt;     
            case 'u': ++uCnt;   
        }
    }
    cout << "Встретилась a: \t" << aCnt << '\n' << "Встретилась e: \t" << eCnt << '\n' << "Встретилась i: \t" << iCnt << '\n' << "Встретилась o: \t" << oCnt << '\n' << "Встретилась u: \t" << uCnt << '\n';
}


Если значение ch равно i, выполнение начинается с инструкции после case 'i' и iCnt возрастет на 1. Однако следующие ниже инструкции, ++oCnt и ++uCnt, также выполняются, увеличивая значения и этих переменных. Если же переменная ch равна a, изменятся все пять счетчиков.
Программист должен явно дать указание компьютеру прервать последовательное выполнение инструкций в определенном месте switch, вставив break. В абсолютном большинстве случаев за каждой метке case должен следовать соответствующий break.

break прерывает выполнение switch и передает управление инструкции, следующей за закрывающей фигурной скобкой, – в данном случае производится вывод. Вот как это должно выглядеть:
PHP:
switch ( ch ) {
   case 'a':
       ++aCnt;
       break;
   case 'e':
       ++eCnt;
       break;
   case 'i':
       ++iCnt;
       break;
   case 'o':
       ++oCnt;
       break;
   case 'u':
       ++uCnt;
       break;
}

Если почему-либо нужно, чтобы одна из секций не заканчивалась инструкцией break, то желательно написать в этом месте разумный комментарий. Программа создается не только для машин, но и для людей, и необходимо сделать ее как можно более понятной для читателя. Программист, изучающий чужой текст, не должен сомневаться, было ли нестандартное использование языка намеренным или ошибочным.
При каком условии программист может отказаться от инструкции break и позволить программе провалиться сквозь несколько меток case? Одним из таких случаев является необходимость выполнить одни и те же действия для двух или более меток. Это может понадобиться потому, что с case всегда связано только одно значение. Предположим, мы не хотим подсчитывать, сколько раз встретилась каждая гласная в отдельности, нас интересует только суммарное количество всех встретившихся гласных. Это можно сделать так:
PHP:
int vowelCnt = 0;
// ...
switch ( ch )
{
   // любой из символов a,e,1,o,u
   // увеличит значение vowelCnt
   case 'a':
   case 'e':
   case 'i':
   case 'o':
   case 'u':
   ++vowe1Cnt;
   break;
}

Некоторые программисты подчеркивают осознанность своих действий тем, что предпочитают в таком случае писать метки на одной строке:
PHP:
switch ( ch )
{
   // допустимый синтаксис
   case 'a': case 'e':
   case 'i': case 'o': case 'u':
   ++vowe1Cnt;
   break;
}
В данной реализации все еще осталась одна проблема: как будут восприняты слова типа
PHP:
UNIX

Наша программа не понимает заглавных букв, поэтому заглавные U и I не будут отнесены к гласным. Исправить ситуацию можно следующим образом:
PHP:
switch ( ch ) {
   case 'a': case 'A':
       ++aCnt;
       break;
   case 'e': case 'E':
       ++eCnt;
       break;
   case 'i': case 'I':
       ++iCnt;
       break;
   case 'o': case 'O':
       ++oCnt;
       break;
   case 'u': case 'U':
       ++uCnt;
       break;
}

Метка default является аналогом части else инструкции if-else. Инструкции, соответствующие default, выполняются, если условие не отвечает ни одной из меток case. Например, добавим к нашей программе подсчет суммарного количества согласных:
PHP:
#include <iostream>
#include <ctype.h>

int main() {   
    char ch;   
    int aCnt=0, eCnt=0, iCnt=0, oCnt=0, uCnt=0, consonantCount=0;

    while ( cin >> ch ) {
        switch ( ch ) {           
            case 'a': case 'A':               
                ++aCnt;               
                break;           
            case 'e': case 'E':               
                ++eCnt;               
                break;           
            case 'i': case 'I':               
                ++iCnt;               
                break;           
            case 'o': case 'O':               
                ++oCnt;               
                break;           
            case 'u': case 'U':               
                ++uCnt;               
                break;           
            default:               
                if ( isa1pha( ch ) )                   
                    ++consonantCnt;               
                break;       
        }
    }
    cout << "Встретилась a: \t" << aCnt << '\n' << "Встретилась e: \t" << eCnt << '\n' << "Встретилась i: \t" << iCnt << '\n' << "Встретилась o: \t" << oCnt << '\n' << "Встретилась u: \t" << uCnt << '\n' << "Встретилось согласных: \t" << consonantCnt << '\n';
}


isalpha() – функция стандартной библиотеки С; она возвращает true, если ее аргумент является буквой. isalpha() объявлена в заголовочном файле ctype.h. (Функции из ctype.h мы будем рассматривать в главе 6.)
Хотя оператор break функционально не нужен после последней метки в инструкции switch, лучше его все-таки ставить. Причина проста: если мы впоследствии захотим добавить еще одну метку после case, то с большой вероятностью забудем вписать недостающий break.
Условная часть инструкции switch может содержать объявление, как в следующем примере:
switch( int ival = get_response() )
ival инициализируется значением, получаемым от get_response(), и это значение сравнивается со значениями меток case. Переменная ival видна внутри блока switch, но не вне его.
Помещать же инструкцию объявления внутри тела блока switch не разрешается. Данный фрагмент кода не будет пропущен компилятором:
PHP:
case illegal_definition:
// ошибка: объявление не может
// употребляться в этом месте
 string file_name = get_file_name();
// ...
break;

Если бы разрешалось объявлять переменную таким образом, то ее было бы видно во всем блоке switch, однако инициализируется она только в том случае, если выполнение прошло через данную метку case.
Мы можем употребить в этом месте составную инструкцию, тогда объявление переменной file_name будет синтаксически правильным. Использование блока гарантирует, что объявленная переменная видна только внутри него, а в этом контексте она заведомо инициализирована. Вот как выглядит правильный текст:
PHP:
case ok:
{
    // ок
    string file_name = get_file_name();   
    // ...   
    break;
}
 
Статус
В этой теме нельзя размещать новые ответы.
Сверху