Integral Promotions整数提升和符号扩展(char,unsigned char,signed char)

时间:2021-06-30 23:52:46

以下来自msdn:

Objects of an integral type can be converted to another wider integral type (that is, a type that can represent a larger set of values). This widening type of conversion is called "integral promotion." With integral promotion, you can use the following in an expression wherever another integral type can be used:

  • Objects, literals, and constants of type char and short int

  • Enumeration types

  • int bit fields

  • Enumerators

C++ promotions are "value-preserving." That is, the value after the promotion is guaranteed to be the same as the value before the promotion. In value-preserving promotions, objects of shorter integral types (such as bit fields or objects of type char) are promoted to type int if int can represent the full range of the original type. If intcannot represent the full range of values, then the object is promoted to type unsigned int. Although this strategy is the same as that used by ANSI C, value-preserving conversions do not preserve the "signedness" of the object.

Value-preserving promotions and promotions that preserve signedness normally produce the same results. However, they can produce different results if the promoted object is one of the following:

  • An operand of /, %, /=, %=<<=>, or >=

    These operators rely on sign for determining the result. Therefore, value-preserving and sign-preserving promotions produce different results when applied to these operands.

  • The left operand of >> or >>=

    These operators treat signed and unsigned quantities differently when performing a shift operation. For signed quantities, shifting a quantity right causes the sign bit to be propagated into the vacated bit positions. For unsigned quantities, the vacated bit positions are zero-filled.

  • An argument to an overloaded function or operand of an overloaded operator that depends on the signedness of the type of that operand for argument matching. (See Overloaded Operators for more about defining overloaded operators.)

  • 整型的对象可以转换为另一个宽整型 (即可可以表示较大设置值的类型。) 转换的此扩大类型称为 “整数提升”。整数提升,在表达式中使用下面,实际上可以使用另一个整数类型:

    • 对象、类型 char 和 short int文本和常数

    • 枚举类型

    • int 位域

    • 枚举数

    C++ 提升 “值保留”。即提升后的值一定相同的值在升级之前。 在值保留的提升,较短整型对象 (如位域或对象类型 char) 提升键入 int ,如果 int 可以表示所有基元类型。 如果 int 不能表示全面值,则对象提升键入 unsigned int。 尽管此方案将与 ANSI C 中使用时,值将转换不想保留 “signedness”对象。

    值保留通常保留 signedness 将产生相同的结果的提升和提升。 但是,因此,如果提升的对象是下列值之一,它们可能产生不同的结果:

    • /、 %、 /=、 %=、 AMP_LT、 AMP_LT=、 AMP_GT或 AMP_GT=操作数

      这些运算符决定的结果符号。 因此,值保留和符号保留提升产生不同的结果,在对这些操作数。

    • AMP_GTAMP_GT 或 AMP_GTAMP_GT=左操作数

      ,在执行移位操作时,这些运算符将不同的有符号和无符号数。 为签名的数量,转换的权限导致符号位传播到操作空出的数位位置。 对于未签名的数目,则操作空出的数位位置是零填充。

    • 对于依赖于该操作数的类型 signedness 参数匹配的重载运算符的重载函数或操作数的参数。 (有关更多 重载运算符 参见有关定义重载运算符。)

http://msdn.microsoft.com/en-us/library/fc9te331.aspx

K&R C中关于整型提升(integral promotion)的定义为:

"A character, a short integer, or an integer bit-field, all either signed or not, or an object of enumeration type, may be used in an expression wherever an integer maybe used. If an int can represent all the values of the original type, then the value is converted to int; otherwise the value is converted to unsigned int. This process is called integral promotion."

上面的定义归纳下来就是以下两个原则:

1). 只要一个表达式中用到了整型值,那么类型为char、short int活整型位域(这几者带符号或无符号均可)的变量,以及枚举类型的对象,都可以被放在这个整型变量的位置。
    2). 如果1)中的变量的原始类型值域可以被int表示,那么原值被转换为int;否则的话,转为unsigned int。
    
    以上两者作为一个整体,被成为整型提升(Integral promotion)

整型提升的概念容易与普通算术类型转换产生混淆。这两者的区别之一在于后者是在操作数之间类型不一致的情况下发生,最终将操作数转换为同一类型。而在算术运算这种情景下,即使操作数具有相同的类型,仍有可能发生整型提升。

例如:

char a , b ,c;
c=a + b;

在上述过程中,尽管两个运算符"+"和"="的操作数全为char型,但在中间计算过程中存在着整数提升:对于表达式a+b ,a、b都是char型,因此被提升至int型后,执行“+”运算,计算结果(int型)再赋值给c(char型),又执行了隐式的类型转换。

是不是有一种很不相信的感觉?Me too,然而标准就是标准。可以使用下面的代码证明上面所描述的过程。

Integral Promotions整数提升和符号扩展(char,unsigned char,signed char)printf ( " the size of the result of a+b :%d " ,sizeof( a+b) );



输出4. 因为a+b转成了int型。

最后提一句,ANSI C规定,编译器如果能保证保证运算结果一致,也可以省略类型提升的操作——这通常出现在表达式中存在常量操作数的时候。

-------------------------------

类型提升 

每天都会看CU的博客,尤其是CU首页上面的博客。个人感觉有很多同学并不关注基础知识,

在遇到问题时,经常会舍本求末。遇到问题,总是找不到根本原因,得出了一些结论。但这些结论并不是真正的原因,整个儿过程,也把真正的原因给掩盖了。
 
今天主要说一下C语言的类型提升的事情。
 
下面是引用的一个例子——这个代码是从一个朋友的博文中复制过来的,但是当时这位朋友没有去说明类型提升的问题,而是阐述汇编的过程。
/***************************************************************/

下面的程序输出什么:

signed char a=0xff;
int b=a;
printf("%08X",b);

 

输出:FFFFFFFF。

我用printf("%d%d",a,b); 输出-1,-1;

改为:

unsigned char a=0xff;
int b=a;
printf("%08X",b);

输出:

000000FF。

我用printf("%d%d",a,b);都输出255.

结果为什么不同。

这就是有关类型提升了。

还有一个例子。

char a=0xff;
signed int b=a;
unsigned int c=b;
printf("%d\n",a);
printf("%d\n",b);
printf("%u\n",c);

b是有符号,c是无符号。

输出:

-1
-1
4294967295 (FFFFFFFF)
请按任意键继续. .

b和c都是int,里面存储的都是FFFFFFFF,只不过解释不一样。b把它解释为负数,c把它解释为整数。

如果你用%d打印C,结果为-1,和b相等。

int main()
{
int i;
unsigned char *p;
char *p1;
int a[] = {0xffffffff, 0xffffffff, 0xffffffff};
 
p = a;
p1 = a;
原作者这里错误,a是int【】类型,p是char *类型,必须强制转换:

p=(unsigned char *)a;
p1=(char *)a;

for(i = 0 ; i < 8 ; i++) {
printf(" 0x%02x  0x%02x \n", p[i], p1[i]);
}
 
}
$ gcc main.c 
main.c: In function ‘main’:
main.c:10: warning: assignment from incompatible pointer type
main.c:11: warning: assignment from incompatible pointer type
$ ./a.out 
 0xff  0xffffffff 
 0xff  0xffffffff 
 0xff  0xffffffff 
 0xff  0xffffffff 
 0xff  0xffffffff 
 0xff  0xffffffff 
 0xff  0xffffffff 
 0xff  0xffffffff 
。。。。。。 。。。。。。
/***************************************************************/
 
根本原因其实很简单。
%x是打印无符号整数的16进制,而例子中传递的类型是字符型,那么这里就有一个字符提升的问题,将类型提升为无符号整形。
*p是unsigned char,其值为0xff,那么对应的无符号整形的值仍然是0xff。
而*p1确实char,其值为0xff,其对应的无符号整形的值为0xffffffff。为什么这次是0xffffffff呢?
因为*p1为-1,而无符号整数的-1则是0xffffffff。
 
为什么是这样呢?
因为在在编码为补码的情形下,类型提升有两种情况:
1. 符号扩展:对于有符号数,扩展存储位数的方法。在新的高位字节使用当前最高有效位即符号位的值进行填充。(个人理解即前面用符号位填充)
2. 零扩展:对于无符号数,扩展存储位数的方法。在新的高位直接填0.
 
对于这个例子来说。*p是无符号数,所以填充的是0,即为0x000000ff。而*p1是有符号数,所以填充的是1,即为0xffffffff。
因此,从char型到unsigned int,是对有符号数的提升,因此用的是符号扩展,oxff被扩展为oxffffffff;而从unsigned char型到unsigned int型,是对无符号数的扩展,使用零扩展,oxff被扩展为ox000000ff,而填充的这些零是不会被打印出来的。
如果说这样教科书式的概念不容易理解。还有这样一种理解方式,也许不一定准确,但更容易理解。
对于这里的类型提升,整个步骤可以这样理解:
1. %x要求参数为无符号整数,需要参数为4个字节;
2. *p, *p1为(unsigned) char型,只占1个字节;
3. 因为参数的类型不符,需要扩展;
4. 定位需要扩展到4个字节;
5. 那么就需要填充增加的3个字节;
6. 这3个字节需要什么值?这里就需要上面所需要的概念了。针对有符号数和无符号数,进行不同值的填充。
这就是为什么在编程的过程中,要避免有符号数和无符号数的混用。我个人认为,在我们解决问题的时候,不要一味儿的想着怎么用高级的技术解决。其实最重要的是基础。一般情况下,大部分的问题都可以由C语言基础解决。
 
--------------------------------------------------------------------------------------------------------------------------

符号扩展和截断的几点认识:

  1. 1.      符号扩展。。

   short int a = -4;

   short int b = 8;

   print_binary(a);

   print_binary(b);

   unsigned short int c = a//类型转换

   unsigned int d = a;

   print_binary(c);

   print_binary(d);

   return 0;

-4补码1111111111111100.

结果是:

11111111 11111100

00000000 00001000

11111111 11111100

11111111 11111111 11111111 11111100

请按任意键继续. . .

 

从结果可以看到:

1.类型转换时不改变位的标志的。

  1. ……

2.截断处理:

int a = -4;

   short int b = a;

   int c = 4;

   short int d = c;

   print_binary(a);

   print_binary(b);

   print_binary(c);

   print_binary(d)

结果是:

11111111 11111111 11111111 11111100

11111111 11111100

00000000 00000000 00000000 00000100

00000000 00000100

请按任意键继续. . .

发现了,截断高位,不管符号,都要截断高位的部分。

3.乘法和除法:

当存储数据的位数不足以存储乘积的时候,会造成截断误差,通过截断,得到我们的结果不是想要的结果:看下例:

char a = 120;

   print_binary(a);

   char c = 120;

   print_binary(c);

   char d = a * c;

   cout << (short)d << endl;

   print_binary(d);

   int e = a * c;

   cout << e << endl;

   print_binary(e);

   return 0;

结果是:

01111000

01111000

64

01000000

14400

00000000 00000000 00111000 01000000

请按任意键继续. . .

看到了没有,对于一个char类型的数据,如果将两者的乘积还是保存为char,那么就有可能发生截断,产生误差。此例即为说明,而如果将结果用int来保存,可以看到完整的位表示,然后具体的截断也知道了。此时就可以得到正常的结果,因为有足够的位,没有造成损失。。

转自:http://www.cppblog.com/deercoder/articles/98080.html

------------------------------------------------------

C语言中的无符号扩展和带符号扩展

C语言中,当不同类型的数据进行运算的时候,就会发生强制或隐式类型转换,通常是低精度的数据类型扩展到高精度的。有些时候,低精度的位数比较少,扩展到高精度的时候,就要在前面补充一些位。那么这些位是补0还是补1呢?这就涉及到无符号扩展和带符号扩展。

扩展的原则是:1.有符号的数据类型,在向高精度扩展时,总是带符号扩展

    2.无符号的数据类型,在向高精度扩展时,总是无符号扩展

怎么理解呢?首先来看一道题目,按此题目讲解完你就明白了!

char   ca=128;

unsigned  char  ucb=128;

unsigned  short   usc=0;

1)usc=ca + ucb;

printf("%x",usc);

2)usc=ca +(unsigned  short)ucb;

printf("%x",usc);

3)usc=(unsignedchar)ca + ucb;

printf("%x",usc);

4)usc=ca+(char)ucb;

printf("%x",usc);

问,在1、2、3、4这4种情况下分别输出什么?

分析:

1)对于char类型,有符号,128已经溢出了,其二进制是 1000 0000,第一位会被当成符号位,也是就是说此时它是负数了,它扩展成unsigned short时,带符号位扩展,符号位为1,所以在前方补1,结果是 1111 1111 1000 000。 ucb类型为nsigned char,无符号,二进制是1000 0000,扩展成unsigned shor,无符号扩展,所以补0,结果是0000 0000 1000 0000,。相加结果为 1 0000 0000 0000 0000 由于unsigned short是二字节,舍弃最前面的1,所以得到0x0,

2)情况和1一样,只是将ucb显示强制转换为unsigned short,所以得到0x0,

3)ca先强制转换为unsignedchar,仍然是1000 0000,注意此时转换后已经是一个无符号数,所以再往unsigned  short 扩展时,为无符号扩展,结果为0000 0000 1000 0000,ucb扩展后

也是0000 0000 1000 0000,相加结果为 0000 0001 0000 0000,所以结果为0x100

4)ca转为unsigned short,带符号扩展,为1111 1111 1000 000,ucb先强制转换为char,然后再转为unsigned short,此时也要带符号扩展,所以也是1111 1111 1000 000,两数相加,得到 1 1111 1111 0000 0000 ,所以结果为0xff00

Integral Promotions整数提升和符号扩展(char,unsigned char,signed char)的更多相关文章

  1. Java的符号扩展与零扩展

    byte b = -127; System.out.println(b); // -127 int b1 = b & 0xff; System.out.println(b1); // 129 ...

  2. char、signed char 和 unsigned char 的区别

    ANSI C 提供了3种字符类型,分别是char.signed char.unsigned char.而不是像short.int一样只有两种(int默认就是signed int). 三者都占1个字节( ...

  3. char、signed char、unsigned char的区别总结。

    转载地址:http://hi.baidu.com/thewillreigns/blog/item/67e665c4296e69c038db492d.html char 和 unsigned char是 ...

  4. char &comma; unsigned char 和 signed char 区别

    ANSI C 提供了3种字符类型,分别是char.signed char.unsigned char.char相当于signed char或者unsigned char,但是这取决于编译器!这三种字符 ...

  5. char&comma; signed char&comma; and unsigned char in C&plus;&plus;

    关于这三者的区别stackoverrflow里有一个答案是这样说的: 3.9.1 Fundamental types [basic.fundamental] 1 Objects declared as ...

  6. C 中 char、signed char 和 unsigned char 的区别

    C 中 char.signed char 和 unsigned char 的区别 来源:http://bbs.chinaunix.net/thread-889260-1-1.html 参考:https ...

  7. C语言 1字节signed char的范围为什么是-128~127&quest;

    参考 1. 关于 -128 ,+128,-0,+0,-1 的反码补码 | 博客园 2. 八位二进制数为什么表示范围(-128~~+127)理解 | 博客园 无符号单字节范围 无符号单字节unsigne ...

  8. char 、signed char、unsigned char

    看如下代码: char c = -1; signed char sc = -1; unsigned char uc = -1; printf("c=%d, sc=%d, uc=%d, cx= ...

  9. char和signed char不同编译器下的使用反思

    遇到一个问题,在使用一个算法的时候出现了仿真正常,但是在使用时出现函数的返回数据只有正值的异常. 在定位算法问题的时候,由算法函数最后的返回值由后向前推,最后发现问题在与char类型的值在不同编译器下 ...

随机推荐

  1. Linux下百度云盘报 获取bdstoken失败

    在用linux下百度云盘工具(bcloud),登录时,报获取bdstoken失败. 在网上搜了一下,解决办法如下. 找到auth.py文件 locate auth.py |grep bcloud 结果 ...

  2. 在Ubuntu上安装Hadoop(单机模式)步骤

    1. 安装jdk:sudo apt-get install openjdk-6-jdk 2. 配置ssh:安装ssh:apt-get install openssh-server 为运行hadoop的 ...

  3. Codeforces Round &num;367 &lpar;Div&period; 2&rpar; A&period; Beru-taxi (水题)

    Beru-taxi 题目链接: http://codeforces.com/contest/706/problem/A Description Vasiliy lives at point (a, b ...

  4. git基本命令--status&comma; add&comma; diff&comma; commit&comma; log

    git status: git status命令的输出十分详细,但其用语有些繁琐. 如果你使用 git status -s 命令或 git status --short 命令,你将得到一种更为紧凑的格 ...

  5. Python是如何实现生成器的原理

    python中函数调用的实质原理: python解释器(即python.exe)其实是用C语言编写的, 在执行python代码时,实际上是在用一个叫做Pyeval_EvalFramEx(C语言的函数) ...

  6. 【js课设】电子画板01

    这学期web开发课的课设选了电子画板课题.(人家本来想做富文本编辑器的嘛然鹅老师在第二版里把这题删掉了。゚ヽ(゚´Д`)ノ゚。) 主要考虑的有[界面美观][画笔类型][画布分层]这三个点了. [界面美 ...

  7. Spring Cloud Zipkin

    Zipkin the idea is from the googlge paper:Dapper https://yq.aliyun.com/articles/60165 https://www.e4 ...

  8. ARMV8 datasheet学习笔记4:AArch64系统级体系结构之编程模型(4)- 其它

    1. 前言 2.可配置的指令使能/禁用控制和trap控制 指令使能/禁用 当指令被禁用,则这条指令就会变成未定义 指令Trap控制 控制某条或某些指令在运行时进入陷阱,进入陷阱的指令会产生trap异常 ...

  9. Java程序设计17——多线程-Part-B

    5 改变线程优先级 每个线程执行都具有一定的优先级,优先级高的线程获得较多的执行机会,而优先级低的线程则获得较少的执行机会. 每个线程默认的优先级都与创建它的父线程具有相同的优先级,在默认情况下,ma ...

  10. ARC075 E&period;Meaningful Mean(树状数组)

    题目大意:给定n和k,问an中有多少子区间的平均值大于等于k 很巧妙的一个式子,就是如果一个区间[l, r]满足条件 那么则有 sum[r] - sum[l-1] >= (r-l+1)*k 整理 ...