“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 (一个无符号数量),所以差异和比较都是使用无符号算术计算的。 当s1比s2短时,差异strlen(s1) - strlen(s2)应该是负数,而是变成一个大的无符号数,大于0 。 从而, 
 return strlen(s1) - strlen(s2) > 0; 
 即使s1比s2短,也返回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无效元素。 
 代码可以通过声明length为int来固定,也可以通过将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_t : ptrdiff_t的签名forms是有意义的,例如 
 return ptrdiff_t(strlen(s1)) - ptrdiff_t(strlen(s2)) > 0; 
 如果你这样做的话,你需要确定size_t值适合于ptrdiff_t (它有一个更less的尾数位)。