我有一个包含多个字符的char
数组。我想将其中一个字符与unsigned char
变量进行比较。例如:
char myarr = { 20, 14, 5, 6, 42 };
const unsigned char foobar = 133;
myarr[2] = foobar;
if(myarr[2] == foobar){
printf("You win a shmoo!\n");
}
此比较类型是否安全??
我从 C99 标准中了解到char
,signed char
和unsigned char
是三种不同的类型( 6.2.5节第14段)。
unsigned char
和char
之间进行转换,而不会丢失精度并且不会冒未定义(或实现定义)行为的风险吗?在第6.2.5节第15段:
实施应将
char
定义为具有相同的范围, 表示和行为为signed char
或unsigned char
。
在第6.3.1.3节第3段:
否则,新类型已签名且值无法在其中表示;结果是实现定义的,或者引发实现定义的信号。
我担心如果char
被定义为signed char
,则myarr[2] = foobar
可能会导致实现定义的值无法转换正确回到原来的unsigned char
值;例如,无论所涉及的42
值如何,实现都可能始终生成值unsigned
。
unsigned
值存储在相同类型的signed
变量中是不安全的?什么是实现定义的信号;这是否意味着在这种情况下实现可以简单地结束程序?
在第6.3.1.1节第1段:
-
long long int
的排名应大于long int
的排名,该排名应大于的排名>int
,应大于short int
的等级,该等级应大于signed char
的等级。- 任何无符号整数类型的等级应等于相应的等级 有符号整数类型,如果有的话。
在第6.2.5节第8段:
对于具有相同签名和不同整数转换等级的任何两个整数类型 (见6.3.1.1),具有较小整数转换等级的类型的值范围是a 另一种类型的值的子范围。
在第6.3.1节第2段:
如果
int
可以代表原始类型的所有值,则该值会转换为int
;否则,它会转换为unsigned int
。
在第6.3.1.8节第1段:
否则,两个操作数都将转换为无符号整数类型 对应于带有符号整数类型的操作数的类型。
char
的范围保证与signed char
或unsigned char
的范围相同,分别是int
和unsigned int
的子范围由于它们的整数转换率较小。
由于整数促销规则要求在评估之前将char
,signed char
和unsigned char
提升至至少int
,这是否意味着{{1}在比较中可以保持其“签名”吗?
例如:
char
signed char foo = -1;
unsigned char bar = 255;
if(foo == bar){
printf("same\n");
}
评估为假值,即使foo == bar
在使用显式-1
演员表时等同于255
,也是如此?更新:
在第J.3.5节第1段中,关于哪些案例会导致实现定义的值和行为:
- 将整数转换为有符号整数类型的结果或引发的信号 当值无法在该类型的对象中表示时(6.3.1.3)。
例如,以下代码可能导致实现定义的行为,因为(unsigned char)
可以定义为有符号整数类型:
char
答案 0 :(得分:1)
“这是否意味着char
在整个比较过程中可以保持'签名'?”是;作为-1
的{{1}}将被提升为signed char
,其将保留其signed int
值。对于-1
,它在升级时也会保留其unsigned char
值,所以是的,比较将为false。如果您希望它评估为true,则需要显式转换。
答案 1 :(得分:1)
它与如何存储char的内存有关,在unsigned char中,所有8位用于表示char的值,而signed char仅使用7位用于数字和8'用来表示标志。
举一个例子,让我们采用一个更简单的3位值(我将这个新值类型称为tinychar):
bits unsigned signed
000 0 0
001 1 1
010 2 2
011 3 3
100 4 -4
101 5 -3
110 6 -2
111 7 -1
通过查看此图表,您可以根据比特的排列方式查看有符号和无符号tinychar之间的值差异。直到您开始进入负范围,两种类型的值都相同。但是,一旦达到最左边的位变为1的点,该值突然变为带符号的负值。这种方法的工作方式是,如果达到最大正值(3),然后再加一个,最后得到最大负值(-4),如果从0中减去1,则会下溢并导致签名的tinychar变为 - 1,而无符号的tinychar将变为7.您还可以看到无符号7和带符号-1 tinychar之间的等价(==),因为两者的位相同(111)。
现在,如果将其扩展为总共8位,您应该会看到类似的结果。
答案 2 :(得分:1)
我的原始帖子相当广泛,包含许多具体问题,我应该为每个问题提供自己的页面。但是,我在这里回答并回答每个问题,以便未来的访问者可以更轻松地解决问题。
<强>问题强>:
- 这种比较类型是否安全?
在这种特殊情况下,myarr[2]
和foobar
之间的比较是安全的,因为两个变量都包含无符号值。但总的来说,情况并非如此。
例如,假设某个实现将char
定义为与signed char
具有相同的行为,int
能够表示unsigned char
和{{1}可表示的所有值}}
signed char
虽然char foo = -25;
unsigned char bar = foo;
if(foo == bar){
printf("This line of text will not be printed.\n");
}
设置为bar
,但C99标准保证从foo
转换为signed char
时不会失去精确度(请参阅回答2 ),unsigned char
条件表达式将评估 false 。
这是由于第6.3.1节 C99标准第2段所要求的整数提升的性质:
如果
foo == bar
可以代表原始类型的所有值,则该值会转换为int
;否则,它会转换为int
。
由于在此实施中unsigned int
可以代表int
和signed char
的所有值,因此unsigned char
和foo
的值都会转换为{在评估之前{1}}因此,生成的条件表达式为bar
,其计算结果为 false 。
<强>问题强>:
- 尽管如此,我可以安全地在
int
和-25 == 231
之间进行转换,而不会丢失精度并且不会冒未定义(或实现定义)行为的风险吗?
您可以安全地从unsigned char
转换为char
,而不会失去精确度(也不会丢失宽度和信息),但转换到另一个方向 - char
到unsigned char
- 可以导致实现定义的行为。
C99标准提供了一些保证,使我们能够安全地从unsigned char
转换为char
。
在第6.2.5节第15段:
实施应将
char
定义为具有相同的范围, 表示和行为为unsigned char
或char
。
在此,我们保证signed char
具有与unsigned char
相同的范围,表示和行为 }或char
。如果实施选择signed char
选项,则从unsigned char
到unsigned char
的转换基本上是char
到unsigned char
的转换 - 因此没有宽度和信息是失去了,没有问题。
unsigned char
选项的转换并不直观,但隐含地保证保持精确度。
在第6.2.5节第6段:
对于每个有符号整数类型,都有一个相应的(但不同的)无符号 使用相同数量的整数类型(使用关键字
unsigned char
指定) 存储(包括标志信息)并具有相同的对齐要求。
在 6.2.6.1 第3段:
存储在无符号位字段和
signed char
类型的对象中的值应为 用纯二进制表示法表示。
在第6.2.6.2节第2段:
对于有符号整数类型,对象表示的位应分为三个 groups:值位,填充位和符号位。不需要任何填充位;应该只有一个符号位。作为值位的每个位应具有相同的值 相应的无符号类型的对象表示中的相同位(如果有的话) 签名类型中的 M 值位和无符号类型中的 N ,然后 M
unsigned
N )。
unsigned char
保证与<=
占用的存储量相同,与未签名对应的所有有符号整数相同。signed char
保证具有纯二进制表示(即没有填充位且没有符号位)。unsigned char
要求只有一个符号位,且不得超过与unsigned char
相同的值位数。鉴于这三个事实,我们可以通过pigeonhole principle证明signed char
类型最多少一个,而不是unsigned char
的值位数类型。同样,signed char
可以安全地转换为unsigned char
,不仅不会损失精度,也不会丢失 width 或信息:< / p>
signed char
的存储空间大小为 unsigned char
位。
unsigned char
必须具有 N 位的相同存储大小。N
没有填充或符号位,因此 signed char
值位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
定义为char
,则char
变量可能包含超出signed char
可表示的值范围的值。在这种情况下,结果是实现定义的或实现定义的信号被引发。
在第6.3.1.3节第3段:
否则,新类型已签名且值无法在其中表示;结果是实现定义的,或者引发实现定义的信号。
<强>问题强>:
- 这是否意味着将
unsigned char
值存储在相同类型的signed char
变量中是不安全的?
如果unsigned
类型值无法在新signed
中表示,则尝试将unsigned
类型值转换为signed
类型值会导致实现定义的行为类型。
unsigned
在第6.3.1.3节第3段:
否则,新类型已签名且值无法在其中表示;结果是实现定义的,或者引发实现定义的信号。
实现定义的结果将是在新signed
类型可表示的值范围内返回的任何值。理论上,对于这些情况,实现可以一致地返回相同的值(例如unsigned foo = UINT_MAX;
signed bar = foo; /* possible implementation-defined behavior */
),从而发生丢失信息 - 即无法保证从signed
转换为42
再返回{ {1}}会产生相同的原始unsigned
值。
实现定义的信号符合C99标准 7.14节中规定的规则;允许实现定义C99标准未明确列举的其他符合信号。
在这种特殊情况下,实现理论上可以提高请求终止程序的signed
信号。因此,尝试将unsigned
类型值转换为unsigned
类型可能会导致程序终止。
<强>问题强>:
SIGTERM
评估为假值,即使unsigned
在使用显式(signed
)强制转换时等同于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
值在C99标准中已明确定义,并且不会导致实现定义的行为。
在第6.3.1.3节第2段:
否则,如果新类型是无符号的,则通过重复添加或转换该值 减去一个可以在新类型中表示的最大值
这条件表达式基本上变为signed char
,其计算结果为 true 。
直到该值在新类型的范围内。
<强>问题强>:
- 这是否意味着即使是明确的转换也不安全?
通常情况下,unsigned char
对unsigned
所代表的值范围之外的值的显式强制转换可能会导致实现定义的行为(参见< strong>回答3 )。对于适用于C99标准的第6.3.1.3节第3段,无需隐含转换。
答案 3 :(得分:0)
我已经测试了您的代码,并没有将(signed char)-1
和(unsigned char)255
进行比较。
您应该首先将signed char转换为unsigned char,因为它在操作中不使用MSB符号位。
我在使用signed char类型进行缓冲操作方面经验不佳。那样你的问题就会发生。然后确保在编译期间打开了所有警告并尝试修复它们。