Поведение с примитивными значениями типов данных вне диапазона и макросами C99 PRI * - PullRequest
3 голосов
/ 13 марта 2010

Скажем, у нас есть 8-битное целое число без знака n (UINT8_MAX=255); Каково поведение компилятора для n=256? Где я могу найти таблицу поведения по умолчанию, когда значение типа данных выходит за пределы диапазона для разных типов данных? Существует ли закономерность того, как они ведут себя, когда находятся вне диапазона?

#include <stdio.h>
#include <inttypes.h>

uint8_t n = UINT8_MAX;
int main() {
  printf("%hhu ",n++);
  printf("%hhu",n);
  return 0;
}

Компиляция с gcc -std=c99 -Wall *.c, это печатает: 255 0

Кроме того, допустимо ли использовать макросы C99 PRI *? Как они названы?

Ответы [ 2 ]

5 голосов
/ 13 марта 2010

n=256; преобразует целое значение со знаком 256 в uint8_t, а затем присваивает его n.Это преобразование определено стандартом для принятия значения по модулю 256, поэтому в результате n устанавливается на 0.

Не уверен, где можно найти таблицу, но правила для целых чиселпреобразования производятся в 6.3.1.3:

1 Когда значение с целочисленным типом преобразуется в другой целочисленный тип, отличный от _Bool, если значение может быть представлено новым типом, оно не изменяется.

2 В противном случае, если новый тип является беззнаковым, значение преобразуется путем многократного сложения или вычитания более одного максимального значения, которое может быть представлено в новом типе, до тех пор, пока значение не окажется в диапазоне нового типа.49)

3 В противном случае новый тип подписывается и значение не может быть представлено в нем;либо результат определяется реализацией, либо определяется сигнал реализации

Как указывает AndreyT, это не охватывает то, что происходит, когда во время вычисления возникает значение вне допустимого диапазона, так какв отличие от во время преобразования.Для типов без знака, описанных в разделе 6.2.5 / 9:

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

Для типов со знаком 3.4.3 / 3 говорит:

ПРИМЕР Пример неопределенного поведенияэто поведение при целочисленном переполнении.

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

Также обратите внимание, что в C правила целочисленного продвижения довольно хитры.Арифметические операции всегда выполняются над операндами одного и того же типа, поэтому, если ваше выражение включает в себя разные типы, существует список правил, чтобы решить, как выбрать тип, в котором будет выполняться операция. Оба операнда повышаются до этого общего типа.Это всегда, по крайней мере, int, поэтому для маленького типа, такого как uint8_t, арифметика будет выполнена в int и преобразована обратно в uint8_t при присвоении результата.Следовательно, например:

uint8_t x = 100;
uint8_t y = 100;
unsigned int z = x * y;

приводит к 10000, а не к 16, что было бы результатом, если бы z тоже было uint8_t.

Также допустимо ли использование C99PRI * макросы?Как они названы?

Приемлемо для кого?Я не против, но вы можете проверить, поддерживает ли ваш компилятор их или нет.GCC делает в самой ранней версии, которую я имею, 3.4.4.Они определены в 7.8.1.

Если у вас нет копии стандарта C, используйте это: http://www.open -std.org / jtc1 / sc22 / WG14 / www / docs/n1256.pdf.Это «черновик» стандарта, выпущенный через некоторое время после опубликования стандарта и включающий некоторые исправления.

2 голосов
/ 13 марта 2010

Поведение описано в стандарте.

(*) Целые типы без знака реализуют арифметику по модулю. Модуль по модулю равен 2 ^ N, где N - число формирующих значение битов в типе. Это означает, что неподписанные типы «оборачиваются» при переполнении (на обоих концах). Если максимальное значение типа составляет 255, то 256 превратится в следующее значение после переноса, которое равно 0.

Единственное исключение из этого поведения для неподписанных типов - это когда вы конвертируете значение с плавающей запятой в неподписанный тип. В случае переполнения поведение не определено.

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

(*) Типы с плавающей точкой вызывают неопределенное поведение при переполнении во время преобразования.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...