Блог пользователя Konstantin.Zakharov

Автор Konstantin.Zakharov, 11 лет назад, По-русски

Может это и банально, но я ничего понять не могу. Для ввода начальных значений типа N использую функцию NextInt вида

int NextInt()
{
    int x;
    scanf("%d",&x);
    return x;
}

Да, пусть глупо, или еще как-то, но мне вот просто нравится грешить штучками типа

int N = NextInt(); vector<int> a; a.push_back(NextInt());

Ну так вот, загадка. На вводе четыре числа 1 2 3 4. Я пишу

cout << NextInt() << " " NextInt() << " " << NextInt() << " " NextInt() << endl;

И мне выводится 4 3 2 1 ???? Думаю что же это такое, пишу

printf("%d %d %d %d\n",NextInt(),NextInt(),NextInt(),NextInt());

и опять то же самое 4 3 2 1. Да блин. Промелькнула мысль о скором конце света, но потом все же решил что я вообще ничего не знаю, и функции вычисляются в каком-то неведомом порядке. Ну, думаю, проверим.

int sum(int a, int b)
{
    return a + b;
}
cout << sum(1,2) << " " << sum(3,4) << endl;
printf("%d %d\n",sum(5,6),sum(7,8));

И что? Да нет, все по старому.

3 7
11 15

MS VS 2012, спасайте)

Теги c++, wtf, vs
  • Проголосовать: нравится
  • +9
  • Проголосовать: не нравится

»
11 лет назад, # |
  Проголосовать: нравится +35 Проголосовать: не нравится

Стандарт языка C++ не гарантирует ничего насчет того, что аргументы функций будут вычисляться в каком-то определенном порядке, если я не ошибаюсь.

  • »
    »
    11 лет назад, # ^ |
      Проголосовать: нравится -22 Проголосовать: не нравится

    Но некоторый порядок все же прослеживается, не так ли? Значит либо на больших количествах вызовов начнется разброс, либо тут все же что-то более простое, но непонятное.

    • »
      »
      »
      11 лет назад, # ^ |
        Проголосовать: нравится +28 Проголосовать: не нравится

      Зависит от компилятора, его опций, поведения оптимизатора и фазы луны. Т.е. оптимизатор может при одних опциях вычислять в одном порядке, а при других опциях — в другом. Не стоит так делать. Даже если несколько раз сработает, не факт, что на другой системе (да хотя бы на CF) тоже будет работать.

      • »
        »
        »
        »
        11 лет назад, # ^ |
          Проголосовать: нравится +2 Проголосовать: не нравится

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

        • »
          »
          »
          »
          »
          11 лет назад, # ^ |
            Проголосовать: нравится +19 Проголосовать: не нравится

          Для начала, это прямо указано в стандарте. К олимпиадам обычно не относится, но если будете использовать такой код в production и при обновлении компилятора/портировании на новую платформу упадёт работающий кусок кода, будет очень нехорошо.

          Легко гуглится обсуждение на StackOverflow с примерами (где-то в середине страницы). В другом обсуждении указывается, что уже достаточно давно почти все компиляторы вычисляют справа налево по историческим причинами. Но никто не запрещает такой умной вещи, как оптимизатор, это поменять по каким-то внутренним мотивам.

          • »
            »
            »
            »
            »
            »
            11 лет назад, # ^ |
              Проголосовать: нравится +8 Проголосовать: не нравится

            Спасибо за ответ. Пора бы мне уже привыкать что языки — лишь технология (со своими оговорками), а не догма.

        • »
          »
          »
          »
          »
          11 лет назад, # ^ |
            Проголосовать: нравится +8 Проголосовать: не нравится

          Как другой пример: например, на платформе PowerPC, на которой, в частности, работает мой старый Mac Mini (вполне себе компьютер), используется big-endian порядок байт, в связи с чем возникают спецэффекты при сборке, например, драйверов, который делают что-то из соображений "младший байт — меньший номер", что в общем случае неверно. Раздражает и не позволяет пользоваться таким софтом.

»
11 лет назад, # |
  Проголосовать: нравится +16 Проголосовать: не нравится

А что должен был показать последний пример с суммами? Там все как и надо выводится по порядку. Было бы странно, если бы перемешался первый и второй аргументы. Вот в каком порядке вызываются функции, если они не влияют друг на друга — загадка.

»
11 лет назад, # |
Rev. 2   Проголосовать: нравится +2 Проголосовать: не нравится

В первом примере все понятно. Перегруженный оператор вывода выглядит так:

ostream& operator<< (ostream& out, const SomeType& a)
{
    ...
}

Поэтому происходит следующее: сначала вызывается оператор вывода для operator<< (cout, nextInt()) который возвращает ostream& (в данном случае cout). Теперь вызывается operator<< (operator<< (cout, nextInt()), nextInt()) и т.д. Поэтому все идет с конца.

Со вторым примером видимо стандарт С++ ничего не гарантирует.

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

  • »
    »
    11 лет назад, # ^ |
    Rev. 2   Проголосовать: нравится +19 Проголосовать: не нравится

    Поэтому все идет с конца.

    Для краткости обозначим operator << буквой f. А nextInt() буквой x

    Получается, мы считаем f(f(f(f(cout, x), x), x), x)

    Вспоминаем народную мудрость "никто не гарантирует порядок вычисления аргументов функции". Получаем, что в теории возможен произвольный порядок (зависит от компилятора и оптимизатора). Разве нет?

    • »
      »
      »
      11 лет назад, # ^ |
        Проголосовать: нравится +8 Проголосовать: не нравится

      Да похоже, что тут тоже не определено. Challenge succeeded :-)

»
11 лет назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится

Воспользуйтесь дебагером вот в этом коде:

int f1(){ return 1; }
int f2(){ return 2; }
int main(){
	cout<<f1()<<f2();
}

Ну хотя в вашем случае тоже можно воспользоваться дебагером и заметить, что просто всё выполняется в обратном порядке (но как заметили выше на это не стоит полагаться — лучше считать что в рандомном), а потом выводится в соответствии с установленным порядком вывода.