signed 和 unsigned char 之间的比较
comparison between signed and unsigned char
我几乎认为这是一个愚蠢的问题...但我真的找不到答案。所以我在这里问这个。
出于学习隐式类型转换的目的,我运行以下C语言代码
#include <stdio.h>
int main()
{
unsigned char i;
char cnt = -1;
int a[255];
for (int k = 0; k < 255; k++)
{
a[k] = k;
}
for (i = cnt - 2; i < cnt; i--)
{
a[i] += a[i + 1];
printf("%d\n", a[i]);
}
return 0;
}
当我运行这个程序时,没有任何反应。
第一次迭代时发现for-loop的循环条件为false,所以程序直接退出了for-loop
但是,我不明白为什么。
据我所知,C 在分配或比较不同类型的变量时会进行隐式转换。所以我想在i = cnt - 2
上,减号操作使值-3,然后隐式转换为i赋值253。
那么,条件 i < cnt
不应该为真,因为(由于有符号和无符号字符的比较,通过 cnt 的另一个隐式转换)253 小于 255?
如果不是,为什么这是错误的?有没有我遗漏的东西或者有什么我不知道的例外情况?
对于初学者,我们假设类型 char
的行为与类型相同
signed char
.
在这种情况下
i < cnt
由于整数提升,两个操作数都隐式转换为类型 int
。
来自 C 标准(6.5.8 关系运算符)
3 If both of the operands have arithmetic type, the usual arithmetic
conversions are performed.
and (6.3.1.8 常用算术转换)
1 Many operators that expect operands of arithmetic type cause
conversions and yield result types in a similar way. The purpose is to
determine a common real type for the operands and result. For the
specified operands, each operand is converted, without change of type
domain, to a type whose corresponding real type is the common real
type. Unless explicitly stated otherwise, the common real type is also
the corresponding real type of the result, whose type domain is the
type domain of the operands if they are the same, and complex
otherwise. This pattern is called the usual arithmetic conversions:
...
Otherwise, the integer promotions are performed on both operands. Then
the following rules are applied to the promoted operands:
If both operands have the same type, then no further conversion is
needed
and (6.3.1.1 Boolean, characters, and integers)
- ...If an int can represent all values of the original type (as restricted
by the width, for a bit-field), the value is converted to an int;
otherwise, it is converted to an unsigned int. These are called the
integer promotions.
因此 i
的正值在整数提升后表示为 0000 0000 1111 1101
将大于负值 1111 1111 1111 1111
.
因此 for 循环的条件立即计算为逻辑假,因为类型 int
的正值 253
大于类型 -1
的负值=15=].
这是一个演示程序。
#include <stdio.h>
int main(void)
{
char cnt = -1;
unsigned char i = cnt - 2;
printf( "cnt = %x\n", ( unsigned int )cnt );
printf( "i = %x\n", ( unsigned int )i );
printf ( "i < cnt is %s\n", i < cnt ? "true" : "false" );
return 0;
}
程序输出为
cnt = ffffffff
i = fd
i < cnt is false
你的问题一点都不傻。您已接近解决方案:i
被分配了值 -3
但隐式转换为 i
、unsigned char
的类型会将值更改为 253
.
为了更准确的解释,您的测试代码中存在多个问题:
char
可能有符号或无符号,具体取决于平台和编译器配置,因此 char cnt = -1;
可能会将值 -1
或 255
存储到cnt
,如果 char
是无符号的且超过 8 位,则甚至是其他值。
for (i = cnt - 2; i < cnt; i--)
的行为还取决于char
默认是有符号还是无符号:
在所有情况下,测试 i < cnt
被评估为两个操作数都转换为 int
(或 unsigned int
在 sizeof(int)==1
的罕见情况下).如果 int
可以表示类型 char
和 unsigned char
的所有值,则此转换不会更改值。
如果 char
是无符号的并且有 8 位,cnt
的值为 255
所以 i
被初始化为值 253
并且循环运行 254 次 i
从 253
下降到 0
,然后 i--
再次将值 255
存储到 i
,测试 i < cnt
的计算结果为 false。循环打印 507
,然后 759
,... 32385
.
如果 char
已签名且有 8 位,就像您的系统可能的情况一样,cnt
的值为 -1
和 i
初始化为值 -3
转换为 unsigned char
,即 253
。初始测试 i < cnt
的计算结果为 253 < -1
,这是错误的,导致立即跳过循环体。
您可以通过为编译器提供适当的标志(例如:gcc -funsigned-char
)强制 char
默认无符号,并测试行为如何变化。使用 Godbolt's compiler explorer,您可以看到 gcc
在有符号(默认)情况下仅生成 2 条指令到 return 0,在无符号情况下生成预期输出。
我几乎认为这是一个愚蠢的问题...但我真的找不到答案。所以我在这里问这个。
出于学习隐式类型转换的目的,我运行以下C语言代码
#include <stdio.h>
int main()
{
unsigned char i;
char cnt = -1;
int a[255];
for (int k = 0; k < 255; k++)
{
a[k] = k;
}
for (i = cnt - 2; i < cnt; i--)
{
a[i] += a[i + 1];
printf("%d\n", a[i]);
}
return 0;
}
当我运行这个程序时,没有任何反应。
第一次迭代时发现for-loop的循环条件为false,所以程序直接退出了for-loop
但是,我不明白为什么。
据我所知,C 在分配或比较不同类型的变量时会进行隐式转换。所以我想在i = cnt - 2
上,减号操作使值-3,然后隐式转换为i赋值253。
那么,条件 i < cnt
不应该为真,因为(由于有符号和无符号字符的比较,通过 cnt 的另一个隐式转换)253 小于 255?
如果不是,为什么这是错误的?有没有我遗漏的东西或者有什么我不知道的例外情况?
对于初学者,我们假设类型 char
的行为与类型相同
signed char
.
在这种情况下
i < cnt
由于整数提升,两个操作数都隐式转换为类型 int
。
来自 C 标准(6.5.8 关系运算符)
3 If both of the operands have arithmetic type, the usual arithmetic conversions are performed.
and (6.3.1.8 常用算术转换)
1 Many operators that expect operands of arithmetic type cause conversions and yield result types in a similar way. The purpose is to determine a common real type for the operands and result. For the specified operands, each operand is converted, without change of type domain, to a type whose corresponding real type is the common real type. Unless explicitly stated otherwise, the common real type is also the corresponding real type of the result, whose type domain is the type domain of the operands if they are the same, and complex otherwise. This pattern is called the usual arithmetic conversions:
... Otherwise, the integer promotions are performed on both operands. Then the following rules are applied to the promoted operands:
If both operands have the same type, then no further conversion is needed
and (6.3.1.1 Boolean, characters, and integers)
- ...If an int can represent all values of the original type (as restricted by the width, for a bit-field), the value is converted to an int; otherwise, it is converted to an unsigned int. These are called the integer promotions.
因此 i
的正值在整数提升后表示为 0000 0000 1111 1101
将大于负值 1111 1111 1111 1111
.
因此 for 循环的条件立即计算为逻辑假,因为类型 int
的正值 253
大于类型 -1
的负值=15=].
这是一个演示程序。
#include <stdio.h>
int main(void)
{
char cnt = -1;
unsigned char i = cnt - 2;
printf( "cnt = %x\n", ( unsigned int )cnt );
printf( "i = %x\n", ( unsigned int )i );
printf ( "i < cnt is %s\n", i < cnt ? "true" : "false" );
return 0;
}
程序输出为
cnt = ffffffff
i = fd
i < cnt is false
你的问题一点都不傻。您已接近解决方案:i
被分配了值 -3
但隐式转换为 i
、unsigned char
的类型会将值更改为 253
.
为了更准确的解释,您的测试代码中存在多个问题:
char
可能有符号或无符号,具体取决于平台和编译器配置,因此char cnt = -1;
可能会将值-1
或255
存储到cnt
,如果char
是无符号的且超过 8 位,则甚至是其他值。for (i = cnt - 2; i < cnt; i--)
的行为还取决于char
默认是有符号还是无符号:在所有情况下,测试
i < cnt
被评估为两个操作数都转换为int
(或unsigned int
在sizeof(int)==1
的罕见情况下).如果int
可以表示类型char
和unsigned char
的所有值,则此转换不会更改值。如果
char
是无符号的并且有 8 位,cnt
的值为255
所以i
被初始化为值253
并且循环运行 254 次i
从253
下降到0
,然后i--
再次将值255
存储到i
,测试i < cnt
的计算结果为 false。循环打印507
,然后759
,...32385
.如果
char
已签名且有 8 位,就像您的系统可能的情况一样,cnt
的值为-1
和i
初始化为值-3
转换为unsigned char
,即253
。初始测试i < cnt
的计算结果为253 < -1
,这是错误的,导致立即跳过循环体。
您可以通过为编译器提供适当的标志(例如:gcc -funsigned-char
)强制 char
默认无符号,并测试行为如何变化。使用 Godbolt's compiler explorer,您可以看到 gcc
在有符号(默认)情况下仅生成 2 条指令到 return 0,在无符号情况下生成预期输出。