У меня два связанных вопроса:
Что говорит стандарт и что делают разные компиляторы, когда речь идет о сравнении арифметического выражения вида x * y == z (или x + y == z), где x * y слишком велико для любого x или y, чтобы удерживать, но не больше, чем z.
Как насчет сравнения между знаковыми и беззнаковыми целыми числами одинаковой ширины с одним и тем же базовым двоичным значением?
Пример ниже может пояснить, что я имею в виду
#include <stdio.h>
#include <stdint.h>
#include <string.h>
int main (void)
{
uint8_t x = 250;
uint8_t y = 5;
uint16_t z = x*y;
uint8_t w = x*y;
if (x * y == z) // true
puts ("x*y = z");
if ((uint16_t)x * y == z) // true
puts ("(uint16_t)x*y = z");
if (x * y == (uint8_t)z) // false
puts ("x*y = (uint8_t)z");
if (x * y == w) // false
puts ("x*y = w");
if ((uint8_t)(x * y) == w) // true
puts ("(uint8_t)x*y = w");
if (x * y == (uint16_t)w) // false
puts ("x*y = (uint16_t)w");
int8_t X = x;
if (x == X) // false
puts ("x = X");
if (x == (uint8_t)X) // true
puts ("x = (uint8_t)X");
if ((int8_t)x == X) // true
puts ("(int8_t)x = X");
if (memcmp (&x, &X, 1) == 0) // true
puts ("memcmp: x = X");
}
Первая часть меня не удивляет: как объяснено в Какие переменные следует приводить к типу при выполнении математических операций в C/C++? компилятор неявно переводит более короткие целые числа в более длинные во время арифметических операций (и я полагаю, что это относится к операторам сравнения). Это гарантированное стандартное поведение?
Но ответ на этот вопрос, а также ответ на сравнения со знаком/без знака говорят, что целые числа со знаком следует повысить до неподписанного. Я ожидал, что x == X
выше будет правдой, поскольку они содержат одни и те же данные (см. memcmp
). Вместо этого, похоже, происходит то, что оба переводятся в более широкие целые числа, а затем происходит преобразование знакового в беззнаковое (или наоборот).
РЕДАКТИРОВАТЬ 2:
В частности, меня интересуют случаи, когда, скажем, функция возвращает int
, которая будет равна -1 в случае ошибки, в противном случае будет представлять, например, число записанных байтов, которое всегда должно быть положительным. Стандартные функции этого типа возвращают ssize_t
, что, если я не ошибаюсь, на большинстве платформ такое же, как int64_t
, но количество записанных байтов может достигать UINT64_MAX
. Итак, если я хочу сравнить возвращенное int
или ssize_t
с беззнаковым значением для ожидаемых записанных байтов, необходимо явное приведение к unsigned int
или size_t
(если я не ошибаюсь, такой же ширины, как ssize_t
, но без знака)?
РЕДАКТИРОВАТЬ 1:
Я не могу понять следующее:
#include <stdio.h>
#include <stdint.h>
int main (void)
{
int8_t ssi = UINT8_MAX;
uint8_t ssu = ssi;
printf ("ssi = %hhd\n", ssi); // -1
printf ("ssu = %hhu\n", ssu); // 255
if (ssi == ssu) // false
puts ("ssi == ssu");
puts ("");
int16_t si = UINT16_MAX;
uint16_t su = si;
printf ("si = %hd\n", si); // -1
printf ("su = %hu\n", su); // 65535
if (si == su) // false
puts ("si == su");
puts ("");
int32_t i = UINT32_MAX;
uint32_t u = i;
printf ("i = %d\n", i); // -1
printf ("u = %u\n", u); // 4294967295
if (i == u) // true????
puts ("i == u");
puts ("");
int64_t li = UINT64_MAX;
uint64_t lu = li;
printf ("li = %ld\n", li); // -1
printf ("lu = %lu\n", lu); // 18446744073709551615
if (li == lu) // true
puts ("li == lu");
}
В то время как 64-битный пример можно объяснить тем, что нет более широкого целого числа для перехода, 32-битный пример противоречит здравому смыслу. Разве это не должно быть таким же, как 8- и 16-битные случаи?
int8_t X = x;
. Вы пытаетесь вставить беззнаковое восьмибитное значение 250 во что-то, что выдает 127. Противоположное (вставление знака в беззнаковое) имеет четкие правила преобразования (добавляйте max-type-val +1, пока он не попадет в диапазон) , а вы делаете наоборот. - person WhozCraig   schedule 23.11.2017X
(со знаком) действительно показывает -6. - person Aayla Secura   schedule 23.11.2017X == x
? - person Aayla Secura   schedule 23.11.2017(uint16_t)x * y
, и предполагая 32-битныйint
, умножение по-прежнему выполняется сint
типами, а сравнение в(uint16_t)x * y == z
также выполняется сint
типами. - person ad absurdum   schedule 23.11.2017int8_t ssi = UINT8_MAX;
у вас есть поведение, определяемое реализацией. Смотрите ссылку в моем первом комментарии. - person ad absurdum   schedule 23.11.2017uint32_t
(который имеет тот же размер, что иint
) иint32_t
(такой же, какint
на моей платформе), результат отличается, и требуется явное приведение? - person Aayla Secura   schedule 23.11.2017uint32_t
иint32_t
знаковый операнд преобразуется в беззнаковый тип, но сначала применяются целочисленные расширения. Еслиint
имеет тот же размер, что иint32_t
, оба операнда будут преобразованы вint
, если это будет содержать оба значения, иначе вunsigned int
, если это необходимо. Вы можете привести к более крупному типу, если хотите. - person ad absurdum   schedule 23.11.2017ssize_t x = foo(); if ( x < 0 ) { errorhandling } else if ( x == expected_unsigned_value )
... - person M.M   schedule 23.11.2017