出于教育目的(是42是),我正在重写strncmp,一个同学走近我,问我为什么要以这种方式转换返回值。我的建议是先打字,然后再取消引用。我的逻辑是我想将char字符串视为未签名的char字符串,然后将其取消引用。
int strncmp(const char *s1, const char *s2, size_t n)
{
if (n == 0)
return (0);
while (*s1 == *s2 && *s1 && n > 1)
{
n--;
s1++;
s2++;
}
return (*(unsigned char *)s1 - *(unsigned char *)s2);
}
他首先要取消引用,然后再进行类型转换,以确保绝对返回两个未签名字符之间的差异。像这样:
return ((unsigned char)*s1 - (unsigned char)*s2);
讨论之后(我同意他的说法,我很奇怪),我们查找了一些可用于生产的实现的源代码,令我们惊讶的是,Apple似乎按照与我相同的顺序进行了类型转换/取消引用:
https://opensource.apple.com/source/Libc/Libc-167/gen.subproj/i386.subproj/strncmp.c.auto.html
因此,问题是:在这种情况下有什么区别?为什么选择一个呢?
(我已经找到了以下内容;但是它指定了不同大小的数据类型的强制转换/取消引用,但是对于chars/unsigned chars来说,应该没关系吗?
In C, if I cast & dereference a pointer, does it matter which one I do first?)
最佳答案
在的二进制补码系统上(几乎全部都是),它不会有所作为。
第一个示例*(unsigned char *)x
会简单地将存储在该位置的数据的二进制值解释为unsigned char
,因此,如果存储在该位置的十进制值为-1
,则存储的十六进制值(假设CHAR_BIT
= 8)是0xFF
,那么它会被简单地解释为255
,因为它适合十六进制表示形式。
第二个示例(假设char
在此编译器上签名)-(unsigned char)*x
-将首先获取存储在该位置的值,然后将其强制转换为unsigned。因此,我们得到了-1
,并将其转换为unsigned char
,该标准指出,要将负有符号数转换为无符号值,您需要向该负值添加比该类型可存储的最大值多一个的值,直到您拥有一个值在其范围内。所以你得到-1 + 256 = 255
但是,如果您以某种方式使用的补充系统,则情况会有所不同。
同样,使用*(unsigned char *)x
,我们将-1
的十六进制表示形式重新解释为unsigned char
,但是这次的十六进制表示形式是0xFE
,它将被解释为254
而不是255
。
回到(unsigned char)*x
,仍然需要执行-1 + 256
来获得255
的最终结果。
综上所述,我不确定char
的第8位是否可以由C标准的字符编码使用。我知道在ASCII编码的字符串中不使用它,这也是您很可能会使用的字符串,因此在比较实际的字符串时,您可能不会遇到任何负值。
从有符号到无符号的转换可以在C11标准的6.3.1.3节中找到:
关于c - 返回char字符串的指针之间的差时,强制转换和取消引用的顺序有多重要?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/58924371/