Целочисленное переполнение в промежуточном арифметическом выражении - PullRequest
0 голосов
/ 05 мая 2018

Это может быть очень простой вопрос программирования, но это то, что я хотел понять в течение некоторого времени.

Рассмотрим этот простой пример:

int main(void) 
{
  unsigned char a = 5;
  unsigned char b = 20;
  unsigned char m = 0xFF;

  unsigned char s1 = m + a - b;
  unsigned char s2 = m - b + a;
  printf("s1 %d s2 %d", s1, s2);
  return 0;
}

Учитывая, что арифметические операторы вычисляются слева направо в C, первое вычисление здесь должно переполниться при m + a. Однако запуск этой программы возвращает одинаковый ответ для s1 и s2. Мой вопрос здесь: приводит ли первое выражение к неопределенному поведению из-за переполнения? Второе выражение должно избегать переполнения, но я хотел понять, почему два выражения возвращают один и тот же ответ.

Ответы [ 4 ]

0 голосов
/ 07 мая 2018

Операции над типами, меньшими int, выполняются путем преобразования результата в int, выполнения вычислений, а затем преобразования результата обратно в исходный тип. Для небольших типов без знака при условии, что результат вычисления соответствует типу int, это приведет к тому, что старшие биты результата будут игнорироваться. Опубликованное обоснование стандарта предполагает, что авторы ожидали, что неархаичные реализации будут игнорировать старшие биты при сохранении значения в тип без знака, который не превышает int, независимо от того, подойдет ли вычисление для типа int, но для "современных" компиляторов уже не модно надежно вести себя таким образом. В системе с 16-разрядным коротким и 32-разрядным int, например, функция

unsigned mulMod65536(unsigned short x, unsigned short y)
{ return (x*y) & 0xFFFFu; }

будет обычно вести себя так же, как:

unsigned mulMod65536(unsigned short x, unsigned short y)
{ return (1u*x*y) & 0xFFFFu; }

но в некоторых случаях gcc будет выполнять "умные" оптимизации, основываясь на том факте, что разрешается вести себя произвольным образом, если x*y превышает 2147483647, хотя нет никаких причин, по которым старшие биты должны когда-либо влиять на результат.

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

0 голосов
/ 05 мая 2018

Согласно спецификации ISO C §6.2.5.9

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

Это означает, что как потенциальные положительные , так и отрицательные переполнения, которые, по-видимому, происходят при сложении и вычитании соответственно, фактически выполняются как подписанные int, поэтому они оба четко определены. После вычисления выражения результат усекается обратно до unsigned char, поскольку это тип результата слева.

0 голосов
/ 05 мая 2018

Благодаря целочисленному продвижению C вычисление s1 эффективно выполняется как:

unsigned char s1 = (unsigned char)( (int)m + (int)a - (int)b );

А временного переполнения нет.

0 голосов
/ 05 мая 2018

(исправлено) При выполнении арифметики с целочисленными типами все типы, меньшие, чем int, повышаются до int во время вычисления, а затем усекаются обратно, если результирующий тип меньше.

См .:

https://wiki.sei.cmu.edu/confluence/display/c/INT02-C.+Understand+integer+conversion+rules

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