繁体   English   中英

编译器在将有符号变量转换为更大的变量类型时会使用什么算法,C 语言?

[英]What is the algorithm that a compiler would use while casting signed variables to larger variable types, C language?

答案可能取决于编译器,但是;

以下行的预期 output 是什么?

signed char a = -5;
printf("%x \n", (signed short) a); 
printf("%x \n", (unsigned short) a);

编译器会在将signed char转换为更大的变量时用零 (0) 或一 (1) 填充最高有效位吗? 如何以及何时?


PS 还有其他问题。 我试图在在线编译器上运行下面的代码进行测试。 结果并不像我预期的那样。 所以我添加了详细的转换,但它没有用。 为什么printf("%x \n", (signed char)b);的output是 4 个字节长而不是 1 个字节?

int main()
{
    unsigned char a = (unsigned char)5;
    signed char b = (signed char)-5;
    
    unsigned short c;
    signed short d;
    
    c = (unsigned short)b;
    d = (signed short)b;
    
    printf("%x ||| %x ||| %x ||| %x\n", (unsigned char)a, (signed char)b, c, d);
    printf("%d ||| %d ||| %d ||| %d\n", a, b, c, d);
    printf("%d ||| %d ||| %d ||| %d\n", a, b, (signed char)c, (signed char)d);

    return 0;
}


Output:

5 ||| fffffffb ||| fffb ||| fffffffb
5 ||| -5 ||| 65531 ||| -5   
5 ||| -5 ||| -5 ||| -5

在 C 中,arguments 到等级低于int的可变参数函数(如printf )被转换为int (不是unsigned int除非参数是无符号的并且宽度与int相同)。

signed shortsigned char转换为signed int不会更改值。 如果您从 -5 开始,您将以 -5 结束。

但是,如果您将负符号值转换为无符号类型(例如,使用显式强制转换),则转换将以比无符号类型的最大值大一为模的方式完成。 例如, unsigned short的最大值为 65535(在许多实现中),因此将 -5 转换为unsigned short结果为 -5 模 65536,即 65531。(C 的%运算符不产生数学模归约。)当那个然后 value 被隐式转换为int ,它仍然是 65531,所以这就是用%x ( fffb ) 打印的内容。

请注意,将格式%x应用于signed int在技术上是不正确的。 %x要求相应的参数是一个unsigned int 目前,C 不保证将有符号值解释为无符号值的结果是什么,但这很快就会改变。 (这不是转换。在运行时,类型不再存在,值只是位模式。)

C11 标准的第 6.3.1.3 节中列出了在有符号和无符号类型之间转换的确切规则:

1当 integer 类型的值转换为_Bool以外的另一种 integer 类型时,如果该值可以用新类型表示,则它不变。

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

3否则,新类型已签名,无法在其中表示值; 结果是实现定义的,或者引发了实现定义的信号。

至于上面这段代码的含义:

signed char a = -5;
printf("%x \n", (signed short) a); 
printf("%x \n", (unsigned short) a);

这里发生了一些事情。

对于第一个printf ,您首先将signed char转换为signed short 根据上面的第 1 条,由于值 -5 可以存储在两者中,因此值不会被强制转换更改。 然后,因为这个值被传递给可变参数 function,所以它被提升为int类型,并且再次通过第 1 条,该值保持不变。

然后使用%x格式说明符打印生成的int值,该说明符需要一个unsigned int 对于不匹配的格式说明符,这在技术上是未定义的行为,尽管大多数实现将允许隐式签名/未签名重新解释。 因此,假设二进制补码表示,将打印int值 -5 的表示,并假设 32 位int这将是fffffffb

对于第二个printf ,从signed charunsigned short的转换将根据上面的第 2 条发生,因为值 -5 不能存储在unsigned short中。 假设 16 位短,这给你值 65536 - 5 = 65531。假设两个补码表示,这相当于将表示从fb符号扩展到fffb 这个unsigned short值然后在传递给printf时被提升为int ,并且根据第 1 条,该值保持不变。 然后%x格式说明符将其打印为fffb

当被转换的值可以在目标类型中表示时,integer 类型之间的转换是值保留的。 signed short可以表示signed char可表示的所有值,所以这...

signed char a = -5;
printf("%hd\n", (signed short) a);

...预计 output 包含“-5”的行。

但是,您的代码具有未定义的行为。 转换说明符%x要求相应的参数具有类型unsigned int ,而您传递的是带signed short (根据默认参数促销转换为int )。

如果您的实现对有符号整数使用二进制补码表示(我可以肯定地断言它确实如此),则表示会将原始带signed char符号扩展为带signed short的宽度,然后将符号扩展为(signed) int的宽度。 因此,UB 在您身上的一种合理可能的表现形式……

 printf("%x \n", (signed short) a);

...将是打印

fffffffb

另一种情况有点不同。 Integer 目标类型为无符号且不能表示源值的转换已明确定义。 通过以目标类型中可表示值的数量为模减少源值,将源值转换为目标类型。 因此,如果您的unsigned short有 16 个值位,那么将 -5 转换为unsigned short的结果是 -5 modulo 65536,即 65531。

因此,

printf("%hu\n", (unsigned short) a);

预计会打印包含“65531”的行。

同样, %x转换说明符与相应参数的类型不匹配( (unsigned short) a ,通过默认参数提升转换为int ),因此您的printf具有未定义的行为。 但是,在二进制补码系统上将 16 位unsigned short转换为 32 位int将涉及零扩展源的表示形式,因此 UB 在您的...

 printf("%x \n", (unsigned short) a);

...将是打印

fffb

.

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM