如果char c = 0x80,为什么printf(“%d \ n”,c << 1)输出-256?

问题描述:

#include<stdio.h> 
int main(void) 
{ 
    char c = 0x80; 
    printf("%d\n", c << 1); 
    return 0; 
} 

在这种情况下输出是-256。如果我写c << 0那么输出是-128如果char c = 0x80,为什么printf(“%d n”,c << 1)输出-256?

我不明白这段代码背后的逻辑。

+1

请参阅http://c-faq.com/ansi/maindecl.html – 2010-10-22 23:49:04

+0

thnx fr正确。 – 2010-10-22 23:49:49

已经你的出发点是有问题的:

char c = 0x80; 

如果(如看似在你的情况)char是有符号类型,你是分配整型常量128到只保证保存值一个类型到127。你的编译器可能会选择给你一些实现定义的值(我猜你的情况为-128)或者发出一个范围错误。

然后,您正在对该负值进行左移。这给出了未定义的行为。在总你有几个实现定义选择加未定义行为决定结果:

符号性的 char
  • 如何128转换为signed char
  • char
  • 符号表示的宽度选择
    • int(有三种可能性)
    • 关于如何实施(或不)左转的选择

    查看所有这些情况以查看不同的结果可能是一个很好的练习。

    总结了一些建议:

    • 选择一个合适的常数初始化与普通char
    • 可变
    • 不会做算术题不做左移签署类型
  • +0

    Thanku Jens先生你的答案。 – 2010-10-23 12:04:53

    +0

    解释的第一段是错误的,可能会误导没有经验的人。 “char c = 0x80;”就像“int c = 0xFFFFFFFF”很好,但是不好的部分是假设char可以被签名时0x80将是128(范围-128到127,而不是0到255) – 2015-06-27 03:20:53

    +0

    @ B.Nadolson ,我不认为这是误导。常量“0x80”的类型为“int”,值为“128”。RHS上的这种类型和值首先被确定,然后该类型和值被转换为符合LHS的类型。试图用不符合类型的值初始化可能带符号的变量是语义错误。 – 2015-06-27 07:08:41

    char可能会在您的平台上签名,在这种情况下0x80代表-128(假设是二进制补码)。

    char<<运算符一起用作操作数时,它被提升为int(仍然是-128)。所以当你应用左移时,你会得到-256。从技术上讲,转向负值是实现定义未定义,但您看到的是典型行为。

    +0

    我不认为你的声明是关于'int'的提升是正确的。 Shift运算符不会进行升级,结果类型是左侧的类型。 – 2010-10-23 14:38:09

    +0

    @Jens:公平地说,我只有C99标准在我面前。但它在6.5.7节中说:“整数升级是在每个操作数上执行的。” – 2010-10-23 14:43:01

    +0

    哎呀,对。所以我会在我的答案中加入这个:) – 2010-10-23 14:55:17

    c分配给0x80。假设8位字节,它的二进制表示值是10000000。显然,在您的平台上,char是签名类型。因此,0x80(即10000000)对应于-128。

    <<应用于char值时,它被提升为int并且符号被保留。所以,当向左移动32位整数时,它变为11111111111111111111111100000000(二进制补码),即-256。

    +0

    在这种情况下,对'int'的提升与'printf'无关;这是由于' 2010-10-22 23:36:31

    +0

    @Oli感谢您的纠正。 – 2010-10-22 23:40:50

    +0

    非常感谢你Oli和Sinan。我明白了逻辑.. – 2010-10-22 23:46:21

    我想知道为什么你的编译器不会抱怨0x80不适合char的警告,在你的平台上它只能表示从-0x80到0x7F的值。

    尝试这段代码:

    #include <stdio.h> 
    #include <limits.h> 
    #include <stdlib.h> 
    
    int main() { 
         printf("char can represent values from %d to %d.\n", CHAR_MIN, CHAR_MAX); 
         return EXIT_SUCCESS; 
    } 
    

    你的情况被称为溢出。

    只是一个侧面说明。从底部开始,按位移位(和屏蔽)基于架构的字长(以位表示)。单词的长度因体系结构而异。

    See this Wiki page for word lengths by architecture

    如果一个人知道了目标架构的字长,可以使用比特移位到乘,除(在某些情况下),不是使用操作数更快。

    See this Wiki page for interesting diagrams of bit-shifting

    由于比特移位代码是体系结构相关的,不能假定一个特定的片的比特移位的代码将工作以同样的方式从架构的架构。然而,一旦熟悉不同体系结构的不同字长度的想法,位移就变得不那么神秘和可预测。谢谢,今天我们有8位,16位,32位和64位字长,并且只有8位字符长度。在古代计算的时代,架构可能有12或15或23位字长(等等)。

    +0

    Thanku Mike先生分享这些信息。你们都有很好的知识。 – 2010-10-23 12:07:32