c/c++语言开发共享C中的符号扩展

我正在这里了解标志扩展: http : //www.shrubbery.net/solaris9ab/SUNWdev/SOL64TRANS/p8.html

struct foo { unsigned int base:19, rehash:13; }; main(int argc, char *argv[]) { struct foo a; unsigned long addr; a.base = 0x40000; addr = a.base << 13; /* Sign extension here! */ printf("addr 0x%lxn", addr); addr = (unsigned int)(a.base << 13); /* No sign extension here! */ printf("addr 0x%lxn", addr); } 

他们声称:

—————— 64位:

 % cc -o test64 -xarch=v9 test.c % ./test64 addr 0xffffffff80000000 addr 0x80000000 % 

—————— 32位:

 % cc -o test32 test.c % ./test32 addr 0x80000000 addr 0x80000000 % 

我有3个问题:

编辑:4。为什么不在32位系统中出现问题?

     a.base << 13 

    按位运算符对其两个操作数执行整数提升。

    所以这相当于:

      (int) a.base << 13 

    这是int类型的负值。

    然后:

     addr = (int) a.base << 13; 

    将此带符号的负值( (int) a.base << 13 )转换为addr的类型,该值通过整数转换为unsigned long整数。

    整数转换(C99,6.3.1.3p2)规则与执行相同:

     addr = (long) ((int) a.base << 13); 

    转换long在此处执行符号扩展,因为((int) a.base << 13)是负的有符号数。

    在另一种情况下,使用一个演员你有相当于:

     addr = (unsigned long) (unsigned int) ((int) a.base << 13); 

    因此,在第二种情况下不执行符号扩展,因为(unsigned int) ((int) a.base << 13)是无符号(当然是正数)值。

    编辑 :正如KerrekSB在他的回答中提到的,a a.base << 13 base a.base << 13实际上在int是不可表示的(我假设是32位int )所以这个表达式调用未定义的行为,并且实现他有权以任何其他方式行事,例如崩溃。

    有关信息,这绝对不是可移植的,但如果您使用gccgcc不会将a.base << 13视为未定义的行为。 来自gcc文档:

    “GCC不使用C99中给出的宽容度仅将已签名的'<<'的某些方面视为未定义,但这可能会发生变化。”

    在https://gcc.gnu.org/onlinedocs/gcc/Integers-implementation.html中

    <<运算符的左操作数经历了标准的促销,所以在你的情况下它被提升为int - 到目前为止一直很好。 接下来,将值0x4000int乘以2 13 ,这会导致溢出,从而导致未定义的行为。 但是,我们可以看到发生了什么:表达式的值现在只是INT_MIN ,最小的可表示的int 。 最后,当您将其转换为无符号的64位整数时,通常的模运算规则要求结果值为0xffffffff80000000 。 类似地,转换为无符号的32位整数会得到值0x80000000

    要对无符号值执行操作,您需要使用强制转换来控制转换:

     (unsigned int)(a.base) << 13 

    这更像是关于位域的问题。 请注意,如果将结构更改为

     struct foo { unsigned int base, rehash; }; 

    你会得到非常不同的结果。

    正如@JensGustedt在无符号位字段类型中指出的那样:int或unsigned int规范说:

    如果int可以表示原始类型的所有值(由宽度限制,对于位字段),该值将转换为int;

    即使您已指定base是无符号的,编译器在读取它时也会将其转换为signed int 。 这就是为什么当你将它转换为unsigned int时你没有得到符号扩展。

    符号扩展与负数如何以二进制表示有关。 最常见的方案是2s补码。 在这个方案中,-1以32位表示为0xFFFFFFFF,-2是0xFFFFFFFE等。那么当我们想要将32位数转换为64位数时应该怎么做? 如果我们将0xFFFFFFFF转换为0x00000000FFFFFFFF,则数字将具有相同的无符号值(约40亿),但签名值不同(-1对40亿)。 另一方面,如果我们将0xFFFFFFFF转换为0xFFFFFFFFFFFFFFFF,则数字将具有相同的有符号值(-1)但不同的无符号值。 前者称为零扩展(适用于无符号数),后者称为符号扩展(适用于有符号数)。 它被称为“符号扩展”,因为“符号位”(最重要或最左边的位)被扩展或复制,以使数字更宽。

    我花了一段时间和很多阅读/测试。
    也许我的,初学者的方式来了解正在发生的事情会得到你(因为我得到它)

    这就是在你甚至都不知道的所有thos转换之后打印的内容: 0xffffffff80000000
    为什么第二行打印0x80000000是因为转换为long int 之前转换为(unsigned int)。 当将unsigned int转换为long int ,没有位符号,因此值只用尾随0填充以匹配大小,这就是全部。

    与32位的不同之处在于,在从32-bit signed int转换为32-bit signed int 32-bit unsigned long 32-bit signed int ,它们的大小匹配并且添加了尾随位符号,因此: 1(0)x31将保持1(0)x31
    甚至在从int转换为long int之后(它们具有相同的大小,该值被解释为不同但位完整。)

    从您的链接报价:

    任何使用此假设的代码都必须更改为适用于ILP32和LP64。 虽然int和long在ILP32数据模型中都是32位,但在LP64数据模型中,long是64位。

      以上就是c/c++开发分享C中的符号扩展相关内容,想了解更多C/C++开发(异常处理)及C/C++游戏开发关注计算机技术网(www.ctvol.com)!)。

      本文来自网络收集,不代表计算机技术网立场,如涉及侵权请联系管理员删除。

      ctvol管理联系方式QQ:251552304

      本文章地址:https://www.ctvol.com/c-cdevelopment/559771.html

      (0)
      上一篇 2021年1月27日
      下一篇 2021年1月27日

      精彩推荐