【问题标题】:C difference between *(unsigned char *)s1 and (unsigned char)*s1*(unsigned char *)s1 和 (unsigned char)*s1 之间的 C 区别
【发布时间】:2013-11-20 20:50:51
【问题描述】:

我有一个任务是重写一些在 libc 中可用的流行 C 函数。

我正在写strcmp,当我写完并且对它感到满意时,我去检查了 libc 中的那个。

这是我的:

int     ft_strcmp(const char *s1, const char *s2)
{
    while (*s1 && *s1 == *s2)
    {
        s1++;
        s2++;
    }
    return ((unsigned char)*s1 - (unsigned char)*s2);
}

这是 libc (https://www.opensource.apple.com/source/Libc/Libc-262/ppc/gen/strcmp.c) 中的那个:

int
strcmp(const char *s1, const char *s2)
{
    for ( ; *s1 == *s2; s1++, s2++)
    if (*s1 == '\0')
        return 0;
    return ((*(unsigned char *)s1 < *(unsigned char *)s2) ? -1 : +1); // HERE ! Why *(unsigned char *) :/ ?
}

我不明白为什么*(unsigned char *)s1 有效,我以为它不会,但它似乎真的有效!

然后我在另一个 libc (https://sourceware.org/git/?p=glibc.git;a=blob;f=string/strcmp.c;h=a4645638eb685e479b89a5e3912076329cc27773;hb=HEAD) 中找到了这个实现

int
strcmp (p1, p2)
  const char *p1;
  const char *p2;
{
    const unsigned char *s1 = (const unsigned char *) p1;
    const unsigned char *s2 = (const unsigned char *) p2;
    unsigned char c1, c2; 
    do
    {
       c1 = (unsigned char) *s1++;
       c2 = (unsigned char) *s2++;
       if (c1 == '\0')
           return c1 - c2;
     }
     while (c1 == c2); 
     return c1 - c2;
}

这也很奇怪,但出于其他原因,这个使用了我认为正确的(const unsigned char *) p1

【问题讨论】:

  • “我认为不会”——为什么不会呢?
  • 为什么还要加一个 * 呢?我的意思是这 2 颗星的意义何在?
  • 嗯什么?那么为什么你认为这个结构不起作用呢?第一个星号是取消引用指针的* 运算符。第二颗星(在类型转换内)是指针限定符。
  • 为什么*(unsigned char *)s1中有2个*
  • 在这种情况下,没有区别,但如果指针不是char *,而是e。 G。如果代码将别名为int *,那么会有。

标签: c pointers syntax casting


【解决方案1】:

您获取 char* 并将其取消引用到 char,然后将其转换为 unsigned char

您认为不起作用的那个只是先将指针转换为unsigned char*,然后当它取消引用时,它是一个unsigned char

在这种情况下,因为它只是从charunsigned char,所以基本上没有区别。

但是,如果原始指针指向int 或其他内容,您的指针将获得int 并将其转换为unsigned char。另一个将获取int 的第一个字节并将其作为unsigned char 返回

【讨论】:

  • 谢谢你更清楚,对不起@H2CO3 也许这对你来说真的很容易但不清楚!
  • @ItsASecret:这是合乎逻辑的,这是肯定的,但这并不容易,因为人类不习惯逻辑,...... - 根据定义。 ;-)
【解决方案2】:

(unsigned char *)s1 将 s1 从 const char *s1 类型转换为 (unsigned char *)s1*(unsigned char *)s1 取消引用它以获取值。

【讨论】:

  • 所以不是一回事吧?它的行为不应该完全相同
  • 也许 char 已签名,这就是它被强制转换为 unsigned 的原因。
  • @ItsASecret 再一次,最后一次。 属于类型,是指针限定符。
  • 好吧,有一个“错误”..这个*1,这似乎是一个错字。
  • 所以慢慢来,找到你的想法和话语。并且:总是把你写的东西读两遍.. ;-)
【解决方案3】:

(unsigned char)*s1*(unsigned char*)s1 的区别在于如何从s1 指向的位置加载数据:

  • (unsigned char)*s1 读取s1 指向的类型值,然后将该值转换为unsigned char。此变体不能调用未定义的行为。

    如果s1double*,则将读取double(即将从内存中加载8 个字节),并将其值转换为unsigned char

  • *(unsigned char*)s1 首先更改指针应该指向的内容,然后读取s1 指向的位置的第一个字节。在某些情况下,这是较新标准的未定义行为,但您的案例不会调用未定义行为。

    如果s1 再次是double*,则生成的代码将加载存储双精度的第一个字节中的位模式(即,仅加载一个字节)。这将与 double 的逻辑值完全不同。


旁白

关于未定义行为的可能性,规则大致如下:

  • 将指针投射到“足够接近”的东西是可以的。这包括强制转换改变 constness 和 signness。

  • 转换为char* 类型是一种特殊情况,它们从不调用未定义的行为。 (感谢 Jens Gustedt 指出这一点。)

所以我们有以下几种情况:

  • int* 转换为const unsigned int* 很好。

  • int* 转换为char* 很好。

  • double* 转换为 uint64* 以分析双精度的位模式是未定义的行为,并允许编译器插入格式化硬盘的代码。

【讨论】:

  • 虽然部分错误的答案。将任何数据指针转换为unsigned char* 非常好。该标准保证这将始终有效。 unsigned char 具有最低对齐要求,不能有陷阱表示。总是可以将一个字节解释为一个字节。
  • @JensGustedt 但是将double* 转换为unsigned char* 不会违反严格的别名规则吗?我知道重新解释指针转换曾经是有效的 C,但我的印象是,新标准中严格的别名规则使得这种重新解释几乎不可能在不调用 UB 的情况下进行。
  • @cmaster,不,字符类型不受这些别名规则的约束,而且一直都是。
  • @JensGustedt 谢谢你的纠正。你是对的,我现在修改了答案以反映我增加的知识。我希望,你现在更喜欢它:-)
【解决方案4】:

简答:charunsigned char 非常相似,可以用相同的方式解释。

长答案:C 标准足够具体,它保证 charunsigned char 的大小均为 1 字节,并以相同的格式存储它们的“值位”。所以在 127 之前,这个函数的行为是严格定义的。

只有当你到达符号位时它才会变得混乱。 C 标准允许符号位表示一个补码、二进制补码或有符号幅度,具体取决于实现。因此,在使用二进制补码的平台上(这是迄今为止最常见的),-1 将表示为11111111,当解释为unsigned char 时将等于 255。但是使用带符号的幅度,它将被表示为10000001,当解释为unsigned char时等于129。

在后一种情况下,这与通过显式转换为 unsigned char(unsigned char) s1++ 示例)得到的结果不同:

如果新类型是无符号的,则通过重复添加或转换值 比新类型可以表示的最大值多减一 直到值在新类型的范围内。

因此,C 标准保证,如果您将 -1 显式转换为 unsigned char,则将添加值 256,从而使转换结果为 255。因此,如果您在使用有符号幅度的平台上:

    char c = -1;
    unsigned char u1 = (unsigned char)c; // this results in 255
    unsigned char u2 = *(unsigned char *)&c; // this results in 129!

我想这些差异是如此罕见,以至于没有人注意到它们。不使用 2 的补码的 C 实现很少。

【讨论】:

    猜你喜欢
    • 2013-08-27
    • 2019-11-29
    • 2012-03-02
    • 2014-05-03
    • 2016-07-07
    • 2013-06-15
    • 1970-01-01
    • 1970-01-01
    • 2020-10-08
    相关资源
    最近更新 更多