С++ для начинающих

       

Инструкция if


Инструкция if обеспечивает выполнение или пропуск инструкции или блока в зависимости от условия. Ее синтаксис таков:

if ( условие )

    инструкция

условие заключается в круглые скобки. Оно может быть выражением, как в этом примере:

if(a+b>c) { ... }

или инструкцией объявления с инициализацией:

if ( int ival = compute_value() ){...}

Область видимости объекта, объявленного в условной части, ограничивается ассоциированной с if инструкцией или блоком. Например, такой код вызывает ошибку компиляции:

if ( int ival = compute_value() ) {

    // область видимости ival

    // ограничена этим блоком

}

// ошибка: ival невидим

if ( ! ival ) ...

Попробуем для иллюстрации применения инструкции if реализовать функцию min(), возвращающую наименьший элемент вектора. Заодно наша функция будет подсчитывать число элементов, равных минимуму. Для каждого элемента вектора мы должны проделать следующее:

1. Сравнить элемент с текущим значением минимума.

2.      Если элемент меньше, присвоить текущему минимуму значение элемента и сбросить счетчик в 1.

3.      Если элемент равен текущему минимуму, увеличить счетчик на 1.

4.      В противном случае ничего не делать.

5.      После проверки последнего элемента вернуть значение минимума и счетчика.

Необходимо использовать две инструкции if:

if ( minVal > ivec[ i ] )...// новое значение minVal

if ( minVal == ivec[ i ] )...// одинаковые значения

Довольно часто программист забывает использовать фигурные скобки, если нужно выполнить несколько инструкций в зависимости от условия:

if ( minVal > ivec[ i ] )

    minVal = ivec[ i ];

    occurs = 1; // не относится к if!

Такую ошибку трудно увидеть, поскольку отступы в записи подразумевают, что и minVal=ivec[i], и occurs=1 входят в одну инструкцию if. На самом же деле инструкция

occurs = 1;

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


if ( minVal > ivec[ i ] )

{

    minVal = ivec[ i ];

    occurs = 1;

}

Вторая инструкция if выглядит так:

if ( minVal == ivec [ i ] )

    ++occurs;

Заметим, что порядок следования инструкций в этом примере крайне важен. Если мы будем сравнивать minVal именно в такой последовательности, наша функция всегда будет ошибаться на 1:

if ( minVal > ivec[ i ] ) {

    minVal = ivec[ i ];

    occurs = 1;

}

//         если minVal только что получила новое значение,

//         то occurs будет на единицу больше, чем нужно

if         ( minVal == ivec[ i ] )    

    ++occurs;

Выполнение второго сравнения не обязательно: один и тот же элемент не может одновременно быть и меньше и равен minVal. Поэтому появляется необходимость выбора одного из двух блоков в зависимости от условия, что реализуется инструкцией if-else, второй формой if-инструкции. Ее синтаксис выглядит таким образом:

if ( условие )

    инструкция1

else

    инструкция2

инструкция1 выполняется, если условие истинно, иначе переходим к инструкция2. Например:

if ( minVal == ivec[ i ] )

    ++occurs;

else

if ( minVal > ivec[ i ] ) {

    minVal = ivec[ i ];

    occurs = 1;

}

Здесь инструкция2 сама является if-инструкцией. Если minVal меньше ivec[i], никаких действий не производится.

В следующем примере выполняется одна из трех инструкций:

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). Например:

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 в фигурные скобки:

if ( minVal <= ivec[ i ] ) {

    if ( minVal == ivec[ i ] )

        ++occurs;

}

else {

    minVal = ivec[ i ];

    occurs = 1;

}

В некоторых стилях программирования рекомендуется всегда употреблять фигурные скобки при использовании инструкций if-else, чтобы не допустить возможности неправильной интерпретации кода.

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

#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 изменяет значение переменной, на которую она ссылается:

int main()

{

    int occur_cnt = 0;

    vector< int > ivec;

    // occur_cnt получает значение occurs

    // из функции min()

    int minval = min( ivec, occur_cnt );

    // ...

}

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



// альтернативная реализация

// с помощью пары

#include <uti1ity>

#include <vector>

typedef pair<int,int> min_va1_pair;

min_va1_pair

min( const vector<int> &ivec )

{

    int minVal = 0;

    int occurs = 0;

    // то же самое ...

    return make_pair( minVal, occurs );

}

К сожалению, и эта реализация содержит ошибку. Где же она? Правильно: мы инициализировали minVal нулем, поэтому, если минимальный элемент вектора больше нуля, наша реализация вернет нулевое значение минимума и нулевое значение количества вхождений.

Программу можно изменить, инициализировав minVal первым элементом вектора:

int minVal = ivec[0];

Теперь функция работает правильно. Однако в ней выполняются некоторые лишние действия, снижающие ее эффективность.

// исправленная версия 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 единицей и избежать ненужного выполнения первой итерации. Однако при оптимизации кода мы допустили другую ошибку (наверное, стоило все оставить как было!). Сможете ли вы ее обнаружить?

// оптимизированная версия 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. Конечно, исправить это очень просто, но сначала надо найти ошибку:

int minVal = ivec[0];

occurs = 1;

К сожалению, подобного рода недосмотры встречаются не так уж редко: программисты тоже люди и могут ошибаться. Важно понимать, что это неизбежно, и быть готовым тщательно тестировать и анализировать свои программы.



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

#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;

}

Результат работы программы:

Минимальное значение: 1 встречается: 5 раз.

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

template <class valueType>

inline const valueType&

min( valueType &vall, valueType &va12 )

{

    if ( vall < va12 )

        return vall;

    return va12;

}

можно переписать так:

template <class valueType>

inline const valueType&

min( valueType &vall, valueType &va12 )

{

    return ( vall < va12 ) ?    vall : va12;

}

Длинные цепочки инструкций if-else, подобные приведенной ниже, трудны для восприятия и, таким образом, являются потенциальным источником ошибок.

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. Это тема следующего раздела.

Упражнение 5.3

Исправьте ошибки в примерах:

(a) if ( ivall != iva12 )

        ivall = iva12

    else  

        ivall = iva12 = 0;

(b) if ( ivat < minval )       

        minvat = ival;

        occurs = 1;

(c) if ( int ival = get_value())

        cout << "ival = "

             << ival << endl;

    if ( ! ival )

        cout << "ival = 0\n";

(d) if ( ival = 0 )

        ival = get_value();

(e) if ( iva1 == 0 )

    else ival = 0;

Упражнение 5.4

Преобразуйте тип параметра occurs функции min(), сделав его не ссылкой, а простым объектом. Запустите программу. Как изменилось ее поведение?


Содержание раздела