我目前正在编写一个C程序,该程序需要经常比较字符串长度,因此我编写了以下帮助程序函数:
int strlonger(char *s1, char *s2) {
return strlen(s1) - strlen(s2) > 0;
}
我注意到即使s1
长度比短,该函数也会返回true s2
。有人可以解释这种奇怪的行为吗?
我目前正在编写一个C程序,该程序需要经常比较字符串长度,因此我编写了以下帮助程序函数:
int strlonger(char *s1, char *s2) {
return strlen(s1) - strlen(s2) > 0;
}
我注意到即使s1
长度比短,该函数也会返回true s2
。有人可以解释这种奇怪的行为吗?
Answers:
您遇到的是在处理包含有符号和无符号数量的表达式时,C语言中出现的一些特殊行为。
当执行一个操作数为带符号的操作数而另一个操作符为无符号的操作时,C将隐式将带符号的参数转换为无符号,并假设数字为非负数来执行操作。这种约定通常导致诸如<
和的关系运算符具有非直觉的行为>
。
关于您的辅助函数,请注意,由于strlen
返回类型size_t
(无符号数量),差和比较均使用无符号算术计算。当s1
小于时s2
,差strlen(s1) - strlen(s2)
应为负,但变为一个较大的无符号数,大于0
。从而,
return strlen(s1) - strlen(s2) > 0;
1
即使s1
比短,也会返回s2
。要修复您的功能,请改用以下代码:
return strlen(s1) > strlen(s2);
欢迎来到C的美好世界!:)
由于这个问题最近受到了广泛的关注,因此,我想提供一些(简单的)示例,以确保使我明白这一点。我将假定我们正在使用带有二进制补码表示法的32位计算机。
在C中使用无符号/有符号变量时要理解的重要概念是,如果在单个表达式中混合有无符号和有符号数量,则将符号值隐式转换为unsigned。
考虑以下表达式:
-1 < 0U
由于第二个操作数是无符号的,因此第一个操作数被隐式转换为无符号,因此表达式等同于比较,
4294967295U < 0U
哪个当然是错误的。这可能不是您期望的行为。
考虑下面的代码,尝试对数组的元素求和a
,其中元素的数量由parameter给出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,比较总是成立。因此,代码将尝试访问array的无效元素a
。
可以通过声明length
为anint
或通过将for
循环测试更改为来固定代码i < length
。
我不想在这里陈述任何有争议的东西,但是这里是我用C编写程序时经常遵循的一些规则。
请勿仅因为数字为非负数而使用。容易犯错误,并且这些错误有时非常难以捉摸(如示例2所示)。
DO执行模运算时使用。
当使用位表示集时,请务必使用。这通常很方便,因为它允许您执行逻辑右移而无需符号扩展。
当然,在某些情况下您可能决定违反这些“规则”。但是大多数时候,遵循这些建议将使您的代码更易于使用,并且不易出错。
size_t
值之间执行减法运算,这两个值被保证为无符号的,并且无符号的算术包装以适当的二的幂为模。可以进行有符号/无符号转换的唯一位置是该result > 0
部分,其中result
是size_t
两个大小相减得到的值。
unsigned int
除非有某些具体原因不这样做。这样做的好处是所有操作都定义明确(甚至是“环绕”),尽管公认的是,在处理某些不平等时可能需要格外小心。
strlen
返回size_t
其为typedef
用于unsigned
类型。
所以,
(unsigned) 4 - (unsigned) 7 == (unsigned) - 3
所有unsigned
值都大于或等于0
。尝试将返回的变量转换strlen
为long int
。
ptrdiff_t
是指针的减法,而不是size_t
值的减法……
size_t
值减法”的POSIX类型。C简单地将其定义为size_t
因为它是整数类型并且类型匹配。您可能会说那是off_t
,但实际上是文件偏移量。因此,您要做的最好的事情是,由于size_t
需要保留平台可以处理的任何索引,因此它也可以表示任何指针值,因为它可以用于从中索引字节0
。因此,ptrdiff_t
需要的位数与相同size_t
,因此仅是的signed
版本size_t
。
return strlen(s1) > strlen(s2);
。