我目前正在编写一个需要经常比较字符串长度的C程序,因此我编写了以下辅助函数:
int strlonger(char *s1, char *s2) {
return strlen(s1) - strlen(s2) > 0;
}
我注意到即使s1
的长度比s2
短,该函数仍然返回true。有人可以解释一下这个奇怪的行为吗?
我目前正在编写一个需要经常比较字符串长度的C程序,因此我编写了以下辅助函数:
int strlonger(char *s1, char *s2) {
return strlen(s1) - strlen(s2) > 0;
}
我注意到即使s1
的长度比s2
短,该函数仍然返回true。有人可以解释一下这个奇怪的行为吗?
<
和>
)出现非直观的行为。strlen
返回size_t
类型(一个无符号数),因此差值和比较都使用无符号算术进行计算。当s1
短于s2
时,差值strlen(s1) - strlen(s2)
应该是负数,但实际上却变成了一个很大的无符号数,它大于0
。因此,return strlen(s1) - strlen(s2) > 0;
即使s1
比s2
短,也会返回1
。为修复您的函数,请改用以下代码:
return strlen(s1) > strlen(s2);
欢迎来到神奇的 C 世界! :)
由于这个问题最近受到了很多关注,我想提供一些(简单的)示例,以确保我传达的思想正确。我将假设我们使用的是32位机器,采用二进制补码表示。
在 C 中处理无符号/有符号变量时需要理解的重要概念是:如果单个表达式中混合了无符号和有符号量,则有符号值会被隐式转换为无符号值。
考虑以下表达式:
-1 < 0U
由于第二个操作数是无符号的,因此第一个操作数被隐式转换为无符号数,因此表达式等同于以下比较:
4294967295U < 0U
当然,这是错误的。这可能不是您所期望的行为。
考虑以下代码,尝试对数组 a
中的元素求和,数组长度由参数length
给出:
int sum_array_elements(int a[], unsigned length) {
int i;
int result = 0;
for (i = 0; i <= length-1; i++)
result += a[i];
return result;
}
length
传递为无符号似乎很自然;毕竟,谁会想要使用负长度呢?停止条件i <= length-1
也似乎非常直观。然而,当使用参数length
等于0
运行时,这两个组合会产生意外结果。length
是无符号的,计算0-1
将使用无符号算术进行,这等效于模加法。然后得到的结果是UMax。 <=
比较也使用无符号比较执行,由于任何数字都小于或等于UMax,因此比较始终成立。因此,代码将尝试访问数组a
的无效元素。length
声明为int
或将for
循环的测试更改为i < length
来修复。不要仅因为数字是非负数而使用。容易犯错,这些错误有时候非常微妙(如示例#2所示)。
在进行模算术时应该使用。
在使用位表示集合时应该使用。这通常很方便,因为它允许您执行逻辑右移而不会出现符号扩展。
size_t
值之间进行的,这些值是保证为无符号数的,并且无符号算术会对适当的二的幂进行模运算。唯一可能存在有符号/无符号转换的地方是在 result > 0
部分,在此部分中,result
是从两个大小值相减得出的 size_t
值。 - Jonathan Lefflerstrlen
返回一个 size_t
类型的值,它是一个 unsigned
类型的别名。
因此,
(unsigned) 4 - (unsigned) 7 == (unsigned) - 3
所有的unsigned
值都大于或等于0
。尝试将strlen
返回的变量转换为long int
。
ptrdiff_t
是用于指针相减,而不是 size_t
值相减... - Mr Listersize_t
values" 没有 POSIX 类型;C 将其定义为 size_t
,因为它是整数类型且类型匹配。你可以认为这是 off_t
,但实际上它是用于文件偏移量的。因此,你只能得出这样的结论,即由于 size_t
需要保存平台所能处理的任何索引,因此它也可以表示任何指针值,因为它可以用于从 0
索引字节。因此,ptrdiff_t
需要与 size_t
具有相同的位数,使其成为 signed
版本的 size_t
。 - Mike DeSimoneAlex Lockwood的答案是最佳解决方案(紧凑,清晰的语义等)。
有时明确转换为带符号的size_t
形式: ptrdiff_t
是有意义的,例如。
return ptrdiff_t(strlen(s1)) - ptrdiff_t(strlen(s2)) > 0;
size_t
值适合于 ptrdiff_t
中(它少了一个尾数位)。