“strlen(s1) – strlen(s2)”永远不会小于零

我目前正在编写一个C程序,需要经常比较string长度,所以我写了下面的帮助函数:

int strlonger(char *s1, char *s2) { return strlen(s1) - strlen(s2) > 0; } 

我注意到,即使当s1长度比s2短,函数也会返回true。 有人能解释这种奇怪的行为吗?

在处理同时包含有符号和无符号数量的expression式时,遇到的是C中出现的一些特殊行为。

当一个操作数被签名而另一个是无符号的操作被执行时,C将隐式地将签名的参数转换为无符号的,并且假定数字是非负的,执行操作。 这个约定常常导致关系运算符(如<>直觉行为。

关于你的帮助函数,请注意,由于strlen返回typessize_t (一个无符号数量),所以差异和比较都是使用无符号算术计算的。 当s1s2短时,差异strlen(s1) - strlen(s2)应该是负数,而是变成一个大的无符号数,大于0 。 从而,

 return strlen(s1) - strlen(s2) > 0; 

即使s1s2短,也返回1 。 要修复你的function,请使用下面的代码:

 return strlen(s1) > strlen(s2); 

欢迎来到C的精彩世界! 🙂


其他示例

由于这个问题最近受到了很多的关注,我想提供一些(简单的)例子,只是为了确保我得到这个想法。 我将假定我们正在使用一个使用二进制补码表示的32位机器。

在C中使用无符号/有符号variables时,理解的重要概念是, 如果在单个expression式中混合使用无符号和有符号数量则将有符号值隐式转换为无符号数

示例#1:

考虑下面的expression式:

 -1 < 0U 

由于第二个操作数是无符号的,因此第一个操作数隐式转换为无符号,因此expression式等同于比较,

 4294967295U < 0U 

这当然是错误的。 这可能不是你期待的行为。

示例#2:

考虑下面的代码试图总结数组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无效元素。

代码可以通过声明lengthint来固定,也可以通过将for循环的testing更改for i < length

结论:您应该何时使用未签名?

我不想在这里陈述任何有争议的事情,但是这是我在C编写程序时经常遵守的一些规则。

  • 不要只因为一个数字是非负的。 犯错很容易,这些错误有时候是非常微妙的(如例2所示)。

  • 执行模块化运算时请使用。

  • 使用位来表示集合时使用。 这通常是方便的,因为它允许您执行逻辑右移而不需要扩展符号。

当然,在某些情况下,你决定违反这些“规则”。 但是大多数情况下,遵循这些build议将使您的代码更易于使用,而且不易出错。

strlen返回一个size_t ,它是一个unsignedtypes的typedef

所以,

 (unsigned) 4 - (unsigned) 7 == (unsigned) - 3 

所有的unsigned值都大于或等于0 。 尝试将由strlen返回的variables转换为long int

Alex Lockwood的答案是最好的解决scheme(紧凑,清晰的语义等)。

有时,明确地转换为size_tptrdiff_t的签名forms是有意义的,例如

 return ptrdiff_t(strlen(s1)) - ptrdiff_t(strlen(s2)) > 0; 

如果你这样做的话,你需要确定size_t值适合于ptrdiff_t (它有一个更less的尾数位)。