【问题标题】:Is there a difference between -1 and ~0?-1 和 ~0 之间有区别吗?
【发布时间】:2014-08-30 06:18:25
【问题描述】:

比较无符号值时,如本测试:

if (pos == (size_t)-1)

这种比较在技术上是否与以下比较不同:

if (pos == (size_t)~0)

我不习惯第二种变体。这就是我问这个问题的原因。如果是的话,答案可能相当直截了当。

【问题讨论】:

标签: c++ c


【解决方案1】:

C++ 标准保证size_t 是一个无符号类型,无符号类型遵循通常的模运算规则(其中模数是该类型值表示中位数的两倍,参见 3.9/4) ,因此-1 转换为size_t 必须是该类型可以表示的最大值。

0 是一个int,而~0 具有零翻转的int 表示中的所有位。该结果的值取决于您的平台上int 的表示形式。然后将该值(可能是陷阱表示,感谢@Matt McNabb)转换为size_t(这是按照模算术规则完成的)。

总之,结果值是否相等是实现定义的。 (比如int用二进制补码表示,那么~0的值就是-1,所以两者是一样的。)

【讨论】:

  • @KerrekSB 你说“取决于你的平台上int 的表示”。出于兴趣,如果在演员表之后发生反转怎么办,即~((size_t)0)。这会消除对int 实现的依赖吗? IE。是否有更强的保证(size_t)-1~((size_t)0) 是等效的? (我上次评论中的错字修复)
  • @Niall:我相信是这样,尽管我现在还没有把所有的部分都放在一起。 ~ 运算符被定义为反码,无符号整数的值是其值表示的二进制值(这是我缺少的部分),因此零的补码是“全1” ,这是最大的可表示值。
  • 我认为可能是标准要求的。来自 C++ 草案;第 3.9.1 节第 7 段; “整数类型的表示应使用纯二进制计数系统定义值。[示例:本国际标准允许整数类型的 2 的补码、1 的补码和有符号幅度表示。-结束示例]”+1 我觉得这很有趣问题/答案对。
  • @Niall:当然很难看出它是怎么回事,特别是如果你将 3.9.1/4、3.9.1/7 和脚注 52 结合起来。基本上,一个无符号整数有 N 值位,位的值以通常的方式确定整数的值。实际上,只有带符号的整数才能为实现细节留下任何空间。 (当然,整数不需要唯一表示;它们很可能有填充。)
  • 请注意,在一个补码中,~0 可能是一个陷阱表示(负零)
【解决方案2】:

Assuming (guaranteed by the standard) that size_t refers to an unsigned integer value,这个:

if(pos == (size_t)~0)

用于等同于:

if(pos == (size_t)-1)

假设机器使用2's complement 表示负整数。标准的doesn't enforce it,所以如果你希望你的代码是 100% 可移植的,你不应该假设它。

【讨论】:

  • size_t 始终未签名。
  • C11 does 指定如何将 -1 转换为无符号:6.3.1.3,第 2 段。值为UINT_MAX
  • @ShafikYaghmour ~0 已签名。 ~0 == 0 可能在一个补码系统上,然后转换为 size_t 仍然产生零。如果是~0ULL,那可能就是另外一回事了。
  • 我见过一些赞美机器的 +0 != -0。我不知道这是否是标准的。
【解决方案3】:

因此,在您的示例中技术上没有任何区别。因为很难找到不会优化像 -1~0 这样的文字操作的编译器。通过您的示例,我得到了确切的信息:

        ; ...
        movq    $-1, -16(%rbp)
        movq    $-1, -8(%rbp)
        ; ...

不要害怕那些-1's,assebmly 是无类型的 ;)

更有趣的问题是,如果您的示例是:

#include <stddef.h>
int main() {
        int var0 = 0;
        int var1 = 1;
        size_t a = (size_t) -var1;
        size_t b = (size_t) ~var0;
        return a ^ b;
}

就我而言(Kubuntu、gcc 4.8.2、x86_64、-O0 选项),感兴趣的部分是:

        movl    $0, -24(%rbp)    ; var0 = 0
        movl    $1, -20(%rbp)    ; var1 = 1

        movl    -20(%rbp), %eax
        negl    %eax             ; 2's complement negation

        ; ...

        movl    -24(%rbp), %eax
        notl    %eax             ; 1's complement negation

        ; ...

查看英特尔的手册:

NEG - 二进制补码否定

将操作数(目标操作数)的值替换为其 二进制补码。 (这个操作相当于 从 0 中减去操作数。)

NOT - 一个的补否定

执行按位非运算(每个 1 设置为 0,每个 0 在目标操作数上设置为 1) 并存储 目标操作数位置中的结果。

我的结论是,理论上,代码在某些特殊平台和编译器上可能会有所不同,但除此之外 - 不会。如果不确定,请始终检查您平台上的程序集列表。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-10-27
    • 2016-01-27
    • 2010-12-24
    • 2022-05-04
    • 1970-01-01
    • 2012-02-16
    • 1970-01-01
    相关资源
    最近更新 更多