Знают ли C99 целочисленные типы, определенные в stdint.h, проявлять четко определенное поведение в случае переполнения?

Все операции над «стандартными» знаками целочисленных типов в C (short, int, long и т. Д.) Демонстрируют неопределенное поведение, если они дают результат вне интервала [TYPE_MIN, TYPE_MAX] (где TYPE_MIN, TYPE_MAX – это минимальное и максимальное целочисленное значение соответственно, которые могут быть сохранены конкретным целым типом.

Однако, согласно стандарту C99, для всех типов intN_t требуется представление двух дополнений:

7.8.11.1 Точные целые числа
1. Имя typedef intN_t обозначает знаковый целочисленный тип с шириной N, без битов заполнения и представление двух дополнений. Таким образом, int8_t обозначает знаковый целочисленный тип с шириной ровно 8 бит.

Означает ли это, что типы intN_t на C99 демонстрируют хорошо определенное поведение в случае переполнения целого числа? Например, хорошо ли этот код?

 #include  #include  #include  int main(void) { printf("Minimum 32-bit representable number: %" PRId32 "\n", INT32_MAX + 1); return 0; } 

Нет, нет.

Требование представления 2’s-дополнения для значений в пределах диапазона типа не означает ничего о поведении при переполнении.

Типы в – это просто typedefs (aliases) для существующих типов. Добавление typedef не изменяет поведение типа.

Раздел 6.5, пункт 5 стандарта C (как C99, так и C11) по-прежнему применяется:

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

Это не влияет на неподписанные типы, поскольку неподписанные операции не переполняются; они определены для получения завернутого результата, уменьшенного по модулю TYPE _MAX + 1. За исключением того, что беззнаковые типы, более узкие, чем int , продвигаются к (подписанному) int и могут поэтому сталкиваться с теми же проблемами. Например, это:

 unsigned short x = USHRT_MAX; unsigned short y = USHRT_MAX; unsigned short z = x * y; 

вызывает неопределенное поведение, если short уже, чем int . (Если short и int 16 и 32 бит соответственно, то 65535 * 65535 дает 4294836225 , что превышает INT_MAX .)

Хотя сохранение значения вне диапазона для подписанного типа, хранящегося в памяти , как правило, сохраняет нижние бит значения, а перезагрузка значения из памяти будет расширять его подписью, многие оптимизации компиляторов могут предполагать, что подписанная арифметика не будет переполнение, а эффекты переполнения могут быть непредсказуемыми во многих реальных сценариях. В качестве простого примера, на 16-разрядном DSP, который использует один 32-разрядный аккумулятор для возвращаемых значений (например, TMS3205X), int16_t foo(int16_t bar) { return bar+1;} компилятор мог бы свободно загружать bar , подписывать -расширен, в аккумулятор, добавьте его к нему и верните. Если вызывающий код был, например, long z = foo(32767) , код может очень хорошо установить z на 32768, а не на -32768.