Няшная / Говнокод #21607 Ссылка на оригинал

0

  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
  9. 9
  10. 10
  11. 11
  12. 12
  13. 13
  14. 14
  15. 15
  16. 16
  17. 17
  18. 18
  19. 19
  20. 20
  21. 21
void iseq(int a, int b){ assert(a==b); }
void ismt(int a, int b){ assert(a+1==b); }

int main(int argc, char **argv){

	{ int j=100, *jp; jp=&argc; iseq(j++, j); assert(j==101); } //100, 100
	{ int j=100, *jp; iseq(j++, j); jp=&j; assert(j==101); } //100, 100
	{ int j=100, *jp; iseq(j++, j); assert(&j && j==101); } //100, 100
	{ int j=100, *jp; ismt(j++, j); assert(j==101); } //100, 100
	
	{ int j=100, *jp; jp=&j; printf("\n%d, %d\n", j++, j); assert(j==101); } //100, 100
	{ int j=100, *jp; printf("%d, %d\n", j++, j); jp=&j; assert(j==101); } //100, 100
	{ int j=100, *jp; printf("%d, %d\n", j++, j); assert(&j && j==101); } //100, 100
	{ int j=100; printf("%d, %d\n", j++, j); assert(j==101); } //100, 101
	
	{ int j=100, *jp; jp=&j; printf("\n%d, %d\n", ++j, j); assert(j==101); } //101, 100
	{ int j=100, *jp; printf("%d, %d\n", ++j, j); jp=&j; assert(j==101); } //101, 100
	{ int j=100; printf("%d, %d\n", ++j, j); assert(j==101); } //101, 101
	 
	return 0;
}

Кто нибудь знает почему ЭТО работает так? Проверял на нескольких компиляторах везде такой эффект.

Запостил: rst256 rst256, (Updated )

Комментарии (25) RSS

  • судя по всему автор не знал про порядок вычисления аргументов, тот случай, когда лучше макросы, чем функции
    Ответить
    • Автор не знал что операция взятия адреса над переменной в не зависимости была она произведена до вызова функции или после нее будет оказывать такой интересный эффект.на порядок вычисления ее аргументов.
      Ответить
      • > операция взятия адреса
        Когда адрес переменной не нужен - конпелятор может засунуть переменную в регистр и даже не выделять под неё память. Когда берёшь адрес - волей-неволей приходится юзать настоящую переменную. Вот тебе и другие оптимизации с другим поведением.

        З.Ы. И если ты поюзал что-то, что в стандарте обозначено как undefined (а порядок вычисления аргументов как раз таки не определён) - на результат даже фаза луны влиять будет, не то что взятие адреса...
        Ответить
  • Аргументы вычисляются в неопределенной последовательности. Вычисления даже могут пересекаться. Поэтому ответ такой: так работает, потому что так удобнее компилятору.
    Ответить
    • Пересекаться - это когда выгодно переюзать результат промежуточных вычислений?
      Ответить
      • Хз. Может компилятор решит переупорядочить вычисления, чтобы лучше загрузить конвейер.

        Ты знал, что make_shared сделали не только для оптимизации пирфоманса? Вызов
        foo(std::shared_ptr<int>(new 5), bar());

        может течь, если bar() бросает исключения. Потому что код может выполняться в последовательности 1) new 5; 2) bar(); 3) std::shared_ptr(...).
        Ответить
        • Понятно.
          Но при чем здесь shared_ptr? я не знаю что это
          Ответа foo(koko(bar()), baz()) вполне достаточно.
          Ответить
          • > вполне достаточно
            не совсем
            память выделенная на стеке, освободится в том числе в случае исключения, но в примере с shared_ptr - память выделяется через new в куче, её нужно освобождать через delete, это должен сделать сам shared_ptr, но из-за исключения до него дело не дойдёт.
            Ответить
            • проблема конкретно shared_ptr немного в другом. Если создавать его через new, происходит два выделения памяти (одно под нужды shared_ptr), и в случае удачного первого и неудачного второго память утекает. А для базового примера утечки с исключением в конструкторе достаточно foo(new A, new B)
              Ответить
    • tcc 0.9.26 и gcc 4.9.4,
      У gcc как оказалось это предупреждение по умолчанию выключено. С "-Wall" он выдал такие же предупреждения что и clang.
      Ответить
  • Постинкремент/постдекремент в правой части выражений лучше вообще не использовать, потому что увеличение/уменьшение значения откладывается на неопределённое время, зависящее от компилятора и от флагов оптимизации. Т. е. полагаться на значение переменной внутри того же выражения, в котором использовали постинкремент, нельзя. А вот после так называемой точки следования (о которой упомянул 4e1) новое значение уже можно использовать.
    Ответить
    • В сишке точки следования еще не выпилили? В 11 плюсах вот выпилили, и запилили какую-то другую дичь.
      Ответить
      • Собственно точки следования были жестче. Sequenced-before даже больше компилятору позволяет. Правда наткнуться на это можно разве что в многопоточных приложениях.
        Ответить
  • Добро пожаловать в клуб "++i + ++i".
    А ведь уже несколько поколений сишников и крестовиков сталкиваются с подобными UB! Так и представляю картину - камин, кресло-качалка, в нём сидит дедушка и внуку рассказывает: "А вот как-то кодил я на плюсах, да угораздило-ж меня два инкремента в одну точку следования всунуть!..".
    Ответить
    • Есть ещё клуб «i++ + i».

      Можно найти условия, при которых и один инкремент сглючит. Надо всего лишь в том же выражении использовать ту же переменную ещё раз.

      Преинкремент по идее должен быть менее коварным, чем постинкремент, но оптимизация может помочь испортить и его.
      Ответить
      • Шо то UB, шо это UB. Вот это обе UB такие, шо я бля его диск в компайл тайме форматировал.
        Ответить
  • Борманд, задыхась от жадности, полирует купол моего храма.
    Ответить

Добавить комментарий

Где здесь C++, guest?!

    А не использовать ли нам bbcode?


    8