【问题标题】:Addition of two chars produces int两个字符相加产生 int
【发布时间】:2011-06-16 10:13:35
【问题描述】:

我做了一个简单的程序,用 GCC 4.4/4.5 编译如下:

int main ()
{
  char u = 10;
  char x = 'x';
  char i = u + x;

  return 0;
}

g++ -c -Wconversion a.cpp

我有以下内容:

a.cpp: In function ‘int main()’:
a.cpp:5:16: warning: conversion to ‘char’ from ‘int’ may alter its value

对于以下代码,我收到了同样的警告:

  unsigned short u = 10;
  unsigned short x = 0;
  unsigned short i = u + x;

a.cpp: In function ‘int main()’:
a.cpp:5:16: warning: conversion to ‘short unsigned int’ from ‘int’ may alter its value

谁能解释一下为什么添加两个字符(或两个无符号短裤)会产生 int? 是编译器错误还是符合标准?

谢谢。

【问题讨论】:

  • 我想知道这里是否正在进行一些编译器优化,因此您添加的 'u' 只是被文字值 10 替换。但是这似乎相当有问题且不符合标准。

标签: c++ integer integer-overflow integer-promotion


【解决方案1】:

您所看到的是算术表达式期间发生的所谓“通常算术转换”的结果,尤其是那些本质上是二进制的(带两个参数)。

这在 §5/9 中有描述:

许多期望算术或枚举类型的操作数的二元运算符会导致转换并以类似的方式产生结果类型。目的是产生一个通用类型,这也是结果的类型。这种模式称为常用算术转换,定义如下:

——如果任一操作数的类型为long double,则另一个应转换为long double
— 否则,如果任一操作数为double,则另一个应转换为double
— 否则,如果任一操作数为float,则另一个应转换为float
— 否则,应在两个操作数上执行积分提升 (4.5)。54)
— 然后,如果任一操作数为 unsigned long,则另一个应转换为 unsigned long
— 否则,如果一个操作数是long int 而另一个是unsigned int,那么如果long int 可以表示unsigned int 的所有值,则unsigned int 应转换为long int;否则两个操作数都应转换为unsigned long int
— 否则,如果任一操作数为long,则另一个应转换为long
— 否则,如果任一操作数为unsigned,则另一个应转换为unsigned

[注意:否则,唯一剩下的情况是两个操作数都是int]

第 4.5 节中提到的促销活动是:

1 如果int 可以表示源类型;否则,源右值可以转换为unsigned int 类型的右值。

2 wchar_t (3.9.1) 或枚举类型 (7.2) 类型的右值可以转换为以下第一种类型的右值,这些类型可以表示其基础类型的所有值:intunsigned intlongunsigned long

3 如果int 可以表示位域的所有值,则整数位域(9.6)的右值可以转换为int 类型的右值;否则,如果unsigned int 可以表示位域的所有值,则可以将其转换为unsigned int。如果位域更大,则不会对其应用积分提升。如果位字段具有枚举类型,则将其视为该类型的任何其他值以用于提升目的。

4 bool 类型的右值可以转换为 int 类型的右值,false 变为零,true 变为 one

5 这些转换称为积分促销。

从这里开始,诸如“乘法运算符”或“加法运算符”之类的部分都有这样的短语:“执行通常的算术转换...... " 来指定表达式的类型。

换句话说,当您进行积分算术时,类型是由上述类别确定的。在您的情况下,促销包含在 §4.5/1 中,表达式的类型为 int

【讨论】:

  • 好的,谢谢。但是我认为 §4.5/1 说“可以转换”,而不是“必须”……这是否意味着其他 C++ 编译器可能会生成 char,而不是 int?
  • @Roman:它所说的是“这种类型可以变成另一种类型”作为明确的声明(要求),然后它将实际进行转换的行为称为“整体促销”。所以当它说“执行整体提升”时,你知道char 将被提升为int(或unsigned int),因为按照它的要求,它可转换为这种类型( “可以”)。
  • 谢谢,我明白了。但它看起来对编译器开发人员有用,而不是对其他开发人员(例如对我来说)。为了避免这个警告,我需要写这样的东西: char res = char(u + x);好像不太舒服。
  • @Roman:背后有其理由。为什么要成为char
  • 好吧,它是 char 还是 unsigned short 都没有关系。关键是我有一些使用小整数类型数组的遗留代码。因此,例如,在我开始使用 GCC 4.4 而不是旧的编译器版本后,添加两个数组会导致警告。
【解决方案2】:

当您对char 类型进行任何算术运算时,它返回的结果是int 类型。

看这个:

char c = 'A';
cout << sizeof(c) << endl;
cout << sizeof(+c) << endl;
cout << sizeof(-c) << endl;
cout << sizeof(c-c) << endl;
cout << sizeof(c+c) << endl;

输出:

1
4
4
4
4

ideone 演示:http://www.ideone.com/jNTMm

【讨论】:

  • 是的,当您考虑它时,它是有道理的。将两个chars 加在一起很容易溢出结果,但如果结果类型至少是原始类型宽度的两倍,则不会!
  • @Tomalak Geret'kal:是的,这就是它背后的基本原理。谢谢你提到这一点。 :-)
  • @Tomalak Geret'kal:不能保证sizeof(int)&gt;sizeof(char)。基本原理是(据我所知)加法是由 CPU 完成的,而许多 RISC CPU 只做全角加法。您将在寄存器中以 16 或 32 位值结束。将其限制为char 宽度需要额外的操作。
  • @Tomalak:实际上我怀疑这是因为潜在的溢出。毕竟int i; sizeof(i+i); 也是4short s; sizeof(s+s) 也一样。我认为尽可能与ints 操作(即当 int 足够大时)比覆盖溢出更多。
  • @MatthieuM:实际上sizeof 的结果由平台/实现控制。
【解决方案3】:

当您将这两个字符相互添加时,它们首先被提升为 int。

加法的结果是一个右值,它被隐式提升为 如有必要,键入 int,如果 int 可以包含结果值。 在 sizeof(int) > sizeof(char) 的任何平台上都是如此。 但请注意 char 可能被视为已签名的字符 你的编译器。

这些链接可以提供更多帮助 - wikisecurecoding

【讨论】:

  • 描述此行为的文档是什么? C++ 标准还是...?能给个链接吗?
猜你喜欢
  • 2011-01-01
  • 2010-09-27
  • 2012-01-31
  • 1970-01-01
  • 2022-06-28
  • 2015-12-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多