我的原始帖子相当广泛,包含许多具体问题,我应该给每个问题单独的页面。但是,我在这里解决并回答每个问题,以便未来的访问者可以更轻松地了解答案。
答案 1
问题:
在这种特殊情况下,myarr[2]
和之间的比较是安全的,因为两个变量都保存无符号值。foobar
然而,总的来说,这是不正确的。
例如,假设一个实现定义char
为具有与 相同的行为signed char
,并且int
能够表示由unsigned char
和表示的所有值signed char
。
char foo = -25;
unsigned char bar = foo;
if(foo == bar){
printf("This line of text will not be printed.\n");
}
虽然bar
设置为等于foo
,并且 C99 标准保证在从signed char
to转换时不会损失精度unsigned char
(请参阅答案 2),但foo == bar
条件表达式的计算结果为false。
这是由于C99 标准第 6.3.1 节第 2 段所要求的整数提升的性质:
如果 anint
可以表示原始类型的所有值,则将该值转换为int
; 否则,将其转换为unsigned int
.
由于在此实现int
中可以表示 和 的所有值,因此signed char
和unsigned char
的值在被评估之前foo
都bar
被转换为类型。int
因此,结果条件表达式的-25 == 231
计算结果为false。
答案 2
问题:
- 尽管如此,我能否安全地在
unsigned char
andchar
和 back 之间进行转换,而不会丢失精度并且不会冒未定义(或实现定义)行为的风险?
您可以安全地从char
to转换而unsigned char
不会丢失精度(宽度或信息),但在另一个方向转换 - unsigned char
to char
- 会导致实现定义的行为。
C99 标准提供了某些保证,使我们能够安全地char
从unsigned char
.
在第 6.2.5 节第 15 段中:
实现应定义为具有与orchar
相同的范围、表示和行为。signed char
unsigned char
在这里,我们保证将具有与orchar
相同的范围、表示和行为。如果实现选择了该选项,则从to的转换本质上是to的转换——因此不会丢失宽度或信息,也不会出现任何问题。signed char
unsigned char
unsigned char
char
unsigned char
unsigned char
unsigned char
选项的转换signed char
不是那么直观,但隐含地保证保持精度。
在第 6.2.5 节第 6 段中:
对于每个有符号整数类型,都有一个对应的(但不同的)无符号整数类型(用关键字指定unsigned
),它使用相同的存储量(包括符号信息)并具有相同的对齐要求。
在6.2.6.1第 3 段中:
存储在无符号位域和类型对象中的值unsigned char
应使用纯二进制表示法表示。
在第 6.2.6.2 节第 2 段中:
对于有符号整数类型,对象表示的位应分为三组:值位、填充位和符号位。不需要任何填充位;应该只有一个符号位。作为值位的每个位应与相应无符号类型的对象表示中的相同位具有相同的值(如果有符号类型中有
M个值位,无符号类型中有N个,则M <=
N)。
- 首先,
signed char
保证占用与 a 相同的存储量,unsigned char
所有有符号整数相对于它们的无符号对应物也是如此。
- 其次,
unsigned char
保证具有纯二进制表示(即没有填充位和没有符号位)。
signed char
需要恰好有一个符号位,并且不超过与 相同数量的值位unsigned char
。
鉴于这三个事实,我们可以通过鸽巢原理证明signed char
类型最多比值位的数量少一个unsigned char
作为类型。类似地,signed char
可以安全地转换为unsigned char
不仅不损失精度,而且也不损失宽度或信息:
unsigned char
具有N
位的存储大小。
signed char
必须具有相同的N位存储大小。
unsigned char
没有填充或符号位,因此具有N
值位
signed char
最多可以有N
非填充位,并且必须恰好分配一位作为符号位。
signed char
最多可以有一个N-1
值位和一个符号位
因此,所有位都与相应的值位signed char
一对一地匹配;unsigned char
换句话说,对于任何给定的signed char
值,都有一个唯一的unsigned char
表示。
/* binary representation prefix: 0b */
(signed char)(-25) = 0b11100111
(unsigned char)(231) = 0b11100111
不幸的是,从unsigned char
to转换char
会导致实现定义的行为。例如,如果char
由实现定义为signed char
,则unsigned char
变量可能保存的值超出了 a 可表示的值范围signed char
。在这种情况下,结果要么是实现定义的,要么引发实现定义的信号。
在第 6.3.1.3 节第 3 段中:
否则,新类型是有符号的,值不能在其中表示;结果是实现定义的,或者引发了实现定义的信号。
答案 3
问题:
unsigned
这是否意味着将值存储signed
在相同类型的变量中是不安全的?
如果无法在新类型中表示类型值,则尝试将unsigned
类型值转换为signed
类型值可能会导致实现定义的行为。unsigned
signed
unsigned foo = UINT_MAX;
signed bar = foo; /* possible implementation-defined behavior */
在第 6.3.1.3 节第 3 段中:
否则,新类型是有符号的,值不能在其中表示;结果是实现定义的,或者引发了实现定义的信号。
实现定义的结果signed
将是在新类型可表示的值范围内返回的任何值。理论上,42
对于这些情况,实现可以始终如一地返回相同的值(例如),因此会出现丢失信息——即不能保证从unsigned
to转换signed
回 tounsigned
将导致相同的原始unsigned
值。
实现定义的信号是符合C99 标准第 7.14 节规定的规则的信号;允许实现定义 C99 标准未明确列举的附加一致信号。
在这种特殊情况下,实现理论上可以SIGTERM
发出请求终止程序的信号。因此,尝试将unsigned
类型值转换为signed
类型可能会导致程序终止。
答案 4
问题:
- 是否
foo == bar
评估为假值,即使-1
等效于255
使用显式 ( unsigned char
) 强制转换?
考虑以下代码:
signed char foo = -1;
unsigned char bar = 255;
if((unsigned char)foo == bar){
printf("same\n");
}
尽管signed char
和unsigned char
值至少int
在条件表达式的求值之前被提升,但显式unsigned char
转换会将signed char
值转换为unsigned char
在整数提升发生之前。此外,转换为unsigned
值在 C99 标准中是明确定义的,不会导致实现定义的行为。
在第 6.3.1.3 节第 2 段中:
否则,如果新类型是无符号的,则通过在新类型中可以表示的最大值重复加或减一来转换值
这个条件表达式本质上变成255 = 255
了计算结果为true。直到值在新类型的范围内。
答案 5
问题:
一般来说,显式转换char
为可表示的值范围之外的值signed char
会导致实现定义的行为(请参阅答案 3)。C99 标准的第 6.3.1.3 节第 3 段无需隐式转换即可应用。