Неожиданный результат при зацикливании символов в C ++ - PullRequest
2 голосов
/ 14 августа 2011

Я использую книгу «Принципы и практика программирования на C ++» для изучения программирования, и одним из упражнений является циклическое прохождение символов az с помощью цикла while.

Теперь я программирую на C ++и другие языки раньше, поэтому я решил попытаться использовать как можно меньше строк (при этом используя цикл while).Тем не менее, это связано с тем, что мой вывод был испорчен.

Код:

#include <iostream>
int main(){
    char ch = 'a';
    while(ch <= 'z')
        std::cout << ch << '\t' << (int) ch++ << std::endl;
    return 0;
}

Вывод:

b   97
c   98
d   99
e   100
...
x   119
y   120
z   121
{   122

Теперь я понимаю, что этоможно было бы сделать вместо этого цикл for, и я тоже это сделал (это сработало).Но я до сих пор не понимаю, что не так с этим кодом, и это меня действительно раздражает.

Похоже, что я сказал ему вывести «ch + 1», так как он печатает «b», гдеон должен напечатать «а».Инкремент не выполняется до тех пор, пока целое значение ch не будет помещено в выходной поток (постинкремент).И даже если бы я увеличил его ранее, по крайней мере, напечатанные символы и их целочисленные значения должны соответствовать.

Есть идеи, почему этот код не работает?

Ответы [ 4 ]

5 голосов
/ 14 августа 2011

Порядок вызовов operator<< четко указан, но порядок, в котором оцениваются их операнды, не является.

Приращение ch может произойти до или после того, как вы выведите ch «в первый раз», и простой запуск этой программы в любом случае не определен из-за чередующихся операций чтения / записи:

[2003: 1.9/12]: A полное выражение - это выражение, которое не является подвыражение другого выражения. [..]

[2003: 1.9/16]: Существует точка последовательности при завершении оценка каждого полного выражения .

[2003: 5/4]: За исключением отмеченных случаев, порядок вычисления операндов отдельных операторов и подвыражений отдельных выражений, и порядок, в котором происходят побочные эффекты, не уточняется. Между предыдущей и следующей точкой последовательности скалярный объект должен его сохраненное значение изменяется не более одного раза путем оценки выражение. Кроме того, предварительное значение должно быть доступно только для определить значение для хранения. Требования этого пункта должны выполняться для каждого допустимого порядка подвыражений полное выражение; в противном случае поведение не определено.


Вместо этого, будьте явными:

std::cout << ch << '\t' << int(ch) << std::endl;
++ch;

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

Для GCC используйте -Wall -Wextra -std=c++98 -pedantic (или -Wall -Wextra -std=c++0x -pedantic для C ++ 0x).

2 голосов
/ 14 августа 2011

ch ++ в вашем коде оценивается первым. Более читаемая и правильная версия будет:

#include <iostream>
int main(){
    char ch = 'a';
    while(ch <= 'z') {
        std::cout << ch << '\t' << int(ch) << std::endl;
        ++ch;
    }
    return 0;
}
2 голосов
/ 14 августа 2011

Короткий ответ: у вас немного неопределенного поведения, поскольку вы одновременно изменяете и используете значение переменной ch через отдельные подвыражения в одном выражении.

ПравоДля решения этой задачи нужно использовать наиболее практичный цикл:

#include <iostream>
int main(){
    for(char ch = 'a'; ch <= 'z'; ++a){
        std::cout << ch << '\t' << ch+0 << std::endl;
    }
}
1 голос
/ 14 августа 2011

Когда вы используете одну переменную дважды в одной команде или выражении, один раз с ++ (или -) и один раз без, вы получите неопределенное поведение.

Вместо этого используйте:

while(ch <= 'z')
{
   std::cout << ch << '\t' << (int) ch << std::endl;
   ch++;
}
...