我的原始帖子相当广泛,包含许多具体问题,我应该给每个问题单独的页面。但是,我在这里解决并回答每个问题,以便未来的访问者可以更轻松地了解答案。
答案 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 charto转换时不会损失精度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 charandchar和 back 之间进行转换,而不会丢失精度并且不会冒未定义(或实现定义)行为的风险?
您可以安全地从charto转换而unsigned char不会丢失精度(宽度或信息),但在另一个方向转换 - unsigned charto char- 会导致实现定义的行为。
C99 标准提供了某些保证,使我们能够安全地char从unsigned char.
在第 6.2.5 节第 15 段中:
实现应定义为具有与orchar相同的范围、表示和行为。signed charunsigned char
在这里,我们保证将具有与orchar相同的范围、表示和行为。如果实现选择了该选项,则从to的转换本质上是to的转换——因此不会丢失宽度或信息,也不会出现任何问题。signed charunsigned charunsigned charcharunsigned charunsigned charunsigned 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 charto转换char会导致实现定义的行为。例如,如果char由实现定义为signed char,则unsigned char变量可能保存的值超出了 a 可表示的值范围signed char。在这种情况下,结果要么是实现定义的,要么引发实现定义的信号。
在第 6.3.1.3 节第 3 段中:
否则,新类型是有符号的,值不能在其中表示;结果是实现定义的,或者引发了实现定义的信号。
答案 3
问题:
unsigned这是否意味着将值存储signed在相同类型的变量中是不安全的?
如果无法在新类型中表示类型值,则尝试将unsigned类型值转换为signed类型值可能会导致实现定义的行为。unsignedsigned
unsigned foo = UINT_MAX;
signed bar = foo; /* possible implementation-defined behavior */
在第 6.3.1.3 节第 3 段中:
否则,新类型是有符号的,值不能在其中表示;结果是实现定义的,或者引发了实现定义的信号。
实现定义的结果signed将是在新类型可表示的值范围内返回的任何值。理论上,42对于这些情况,实现可以始终如一地返回相同的值(例如),因此会出现丢失信息——即不能保证从unsignedto转换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 段无需隐式转换即可应用。