Значение, возвращаемое size()
, равно std::size_t
, что является целым типом без знака.Это означает, что он может представлять только неотрицательные числа, и если вы выполняете операцию, которая приводит к получению отрицательного числа, он будет округлен до максимально возможного значения, как в модульной арифметике.
Здесь, 2 - 1
равно -1, что означает 2^32 - 1
в 32-битной системе.Когда вы пытаетесь вычесть 2^32 - 1
из 10, вы вызываете переполнение целого числа со знаком, поскольку минимальное значение 32-разрядного целого числа равно -2^31
.Целочисленное переполнение / недополнение со знаком равно неопределенное поведение , поэтому может произойти все что угодно.
В этом случае кажется, что переполнение обернуто до максимального значения, как целое число со знаком.Таким образом, результатом будет 10 - (2^32 - 1) + 2^32
, что равно 11. Мы добавляем 2^32
для имитации обтекания нижнего потока.Другими словами, после 2^31 + 10
-ой итерации цикла n
является минимально возможным значением в 32-разрядном целом числе.Следующая итерация вызывает обтекание, поэтому n
теперь равно 2^31 - 1
.Затем оставшиеся 2^31 - 12
итераций уменьшаются n
до 11.
Опять переполнение / недополнение со знаком является неопределенным поведением, поэтому не удивляйтесь, если из-за этого произойдет что-то странное, особенно с современным компиляторомоптимизаций.Например, вся ваша программа может быть «оптимизирована», чтобы абсолютно ничего не делать, поскольку она всегда будет вызывать UB.Вы даже не гарантированно видите вывод std::cout<<m<<endl;
, даже если UB вызывается после выполнения этой строки.