【发布时间】:2017-11-23 00:12:37
【问题描述】:
我有两个相关的问题:
在比较 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");
}
第一部分并不让我感到惊讶:正如Which variables should I typecast when doing math operations in C/C++? 中所解释的,编译器在算术运算期间隐式地将更短的整数提升为更长的整数(我想这适用于比较运算符)。这是保证的标准行为吗?
但是该问题的答案以及Signed/unsigned comparisons 的答案都表明有符号整数应该提升为无符号整数。我期待上面的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 直到它落在范围内) ,但你正在做相反的事情。 -
为什么未定义?我的印象是 250 将被转换为 int8_t ,即 -6。打印
X的值(已签名)确实显示 -6。 -
那么“标准怎么说?”的答案是什么?没什么”?并且必须在
X == x中的比较两侧使用显式转换? -
Edit 2 应该是一个新问题。但是你的代码看起来像
ssize_t x = foo(); if ( x < 0 ) { errorhandling } else if ( x == expected_unsigned_value )...
标签: c integer-arithmetic