当前位置: 首页 > 知识库问答 >
问题:

C++在减法时转换为字节(unit8_t)不会像我期望的那样强制下溢;输出为INT16_T;为什么?

郎志
2023-03-14

注意byte是8位类型(uint8_t),而无符号int是16位类型(uint16_t)。

以下并没有产生我所期望的结果。我希望它下溢,结果总是一个uint8_t,但它变成了一个有符号的int(int16_t)!!!为什么?

while (true)
{
  static byte tStart = 0;
  static unsigned int seconds = 0;
  seconds++;

  //Print output from microcontroller
  typeNum((byte)seconds); typeString(", "); typeNum(tStart); typeString(", "); 
  typeNum((byte)seconds - tStart); typeString("\n");

  if ((byte)seconds - tStart >= (byte)15)
  {
    typeString("TRUE!\n");
    tStart = seconds; //update 
  }
}
196, 195, 1
197, 195, 2
198, 195, 3
199, 195, 4
200, 195, 5
201, 195, 6
202, 195, 7
203, 195, 8
204, 195, 9
205, 195, 10
206, 195, 11
207, 195, 12
208, 195, 13
209, 195, 14
210, 195, 15
TRUE!
211, 210, 1
212, 210, 2
213, 210, 3
214, 210, 4
215, 210, 5
216, 210, 6
217, 210, 7
218, 210, 8
219, 210, 9
220, 210, 10
221, 210, 11
222, 210, 12
223, 210, 13
224, 210, 14
225, 210, 15
TRUE!
226, 225, 1
227, 225, 2
228, 225, 3
229, 225, 4
230, 225, 5
231, 225, 6
232, 225, 7
233, 225, 8
234, 225, 9
235, 225, 10
236, 225, 11
237, 225, 12
238, 225, 13
239, 225, 14
240, 225, 15
TRUE!
241, 240, 1
242, 240, 2
243, 240, 3
244, 240, 4
245, 240, 5
246, 240, 6
247, 240, 7
248, 240, 8
249, 240, 9
250, 240, 10
251, 240, 11
252, 240, 12
253, 240, 13
254, 240, 14
255, 240, 15
TRUE!
0, 255, -255
1, 255, -254
2, 255, -253
3, 255, -252
4, 255, -251
5, 255, -250
6, 255, -249
7, 255, -248
8, 255, -247
9, 255, -246
10, 255, -245
11, 255, -244
12, 255, -243
13, 255, -242
14, 255, -241
15, 255, -240
16, 255, -239
17, 255, -238
18, 255, -237
19, 255, -236
20, 255, -235
21, 255, -234
22, 255, -233
23, 255, -232
24, 255, -231
25, 255, -230
26, 255, -229
27, 255, -228
28, 255, -227
29, 255, -226
30, 255, -225
31, 255, -224
32, 255, -223
33, 255, -222
34, 255, -221
35, 255, -220
//--------------------------------------------------------------------------------------------
//typeNum (overloaded)
//-see AVRLibC int to string functions: http://www.nongnu.org/avr-libc/user-manual/group__avr__stdlib.html
//--------------------------------------------------------------------------------------------
//UNSIGNED:
void typeNum(uint8_t myNum)
{
  char buffer[4]; //3 for the number (up to 2^8 - 1, or 255 max), plus 1 char for the null terminator 
  utoa(myNum, buffer, 10); //base 10 number system 
  typeString(buffer);
}
void typeNum(uint16_t myNum)
{
  char buffer[6]; //5 for the number (up to 2^16 - 1, or 65535 max), plus 1 char for the null terminator 
  utoa(myNum, buffer, 10); //base 10 number system 
  typeString(buffer);
}
void typeNum(uint32_t myNum)
{
  char buffer[11]; //10 chars for the number (up to 2^32 - 1, or 4294967295 max), plus 1 char for the null terminator 
  ultoa(myNum, buffer, 10); //base 10 number system 
  typeString(buffer);
}

//SIGNED:
void typeNum(int8_t myNum)
{
  char buffer[5]; //4 for the number (down to -128), plus 1 char for the null terminator 
  itoa(myNum, buffer, 10); //base 10 number system 
  typeString(buffer);
}
void typeNum(int16_t myNum)
{
  char buffer[7]; //6 for the number (down to -32768), plus 1 char for the null terminator 
  itoa(myNum, buffer, 10); //base 10 number system 
  typeString(buffer);
}
void typeNum(int32_t myNum)
{
  char buffer[12]; //11 chars for the number (down to -2147483648), plus 1 char for the null terminator 
  ltoa(myNum, buffer, 10); //base 10 number system 
  typeString(buffer);
}

共有1个答案

慕容嘉熙
2023-03-14

所以我想通了:

答案很简单,但背后的理解却不是。

(如何修复):
不要使用(byte)seconds-tStart,而是使用(byte)seconds-tStart)。就是这样!问题解决了!您所需要做的就是将数学运算(在本例中是减法)的输出转换为字节,并且它是固定的!否则,它将以有符号int返回,从而产生错误行为。

因此,这段代码(byte)seconds-tstart被编译器隐式强制转换(在本例中被提升)如下:(int)(byte)seconds-(int)tstart...它也返回int。很困惑,嗯?我当然是这么想的!

(星号*越多越有用)

  • *****byte+byte=int...为什么?<--特别有用
  • *****C++运算符中的隐式类型转换规则<--特别有用。这里的答案显示了何时发生隐式强制转换,并声明:“注意。操作的最小大小是int。所以short/char在操作完成之前被提升为int。”
  • ******在谷歌搜索“C++是否会使用比较进行隐式转换?”
  • https://www.tutorialspoint.com/cprogramming/c_type_castast.htm
  • http://www.improgrammer.net/type-casting-c-language/
  • 谷歌搜索“C++隐式转换”
  • http://www.cplusplus.com/doc/tutorial/typecasting/
  • http://en.cppreference.com/w/cpp/language/implicit_conversion
  • http://en.cppreference.com/w/cpp/language/operator_comparation

下面是一个完整的C++程序,您可以编译并运行它来测试表达式,看看返回类型是什么,如果它已经被编译器隐式强制转换为您不想要的类型:

#include <iostream>

using namespace std;

//----------------------------------------------------------------
//printTypeAndVal (overloaded function)
//----------------------------------------------------------------
//UNSIGNED:
void printTypeAndVal(uint8_t myVal)
{
  cout << "uint8_t = " << (int)myVal << endl; //(int) cast is required to prevent myVal from printing as a char
}
void printTypeAndVal(uint16_t myVal)
{
  cout << "uint16_t = " << myVal << endl;
}
void printTypeAndVal(uint32_t myVal)
{
  cout << "uint32_t = " << myVal << endl;
}
void printTypeAndVal(uint64_t myVal)
{
  cout << "uint64_t = " << myVal << endl;
}
//SIGNED:
void printTypeAndVal(int8_t myVal)
{
  cout << "int8_t = " << (int)myVal << endl; //(int) cast is required to prevent myVal from printing as a char
}
void printTypeAndVal(int16_t myVal)
{
  cout << "int16_t = " << myVal << endl;
}
void printTypeAndVal(int32_t myVal)
{
  cout << "int32_t = " << myVal << endl;
}
void printTypeAndVal(int64_t myVal)
{
  cout << "int64_t = " << myVal << endl;
}
//FLOATING TYPES:
void printTypeAndVal(float myVal)
{
  cout << "float = " << myVal << endl;
}
void printTypeAndVal(double myVal)
{
  cout << "double = " << myVal << endl;
}
void printTypeAndVal(long double myVal)
{
  cout << "long double = " << myVal << endl;
}

//----------------------------------------------------------------
//main
//----------------------------------------------------------------
int main()
{
  cout << "Begin\n\n";

  //Test variables
  uint8_t u1 = 0;
  uint8_t u2 = 1;

  //Test cases:

  //for a single byte, explicit cast of the OUTPUT from the mathematical operation is required to get desired *unsigned* output
  cout << "uint8_t - uint8_t:" << endl;
  printTypeAndVal(u1 - u2); //-1 (bad)
  printTypeAndVal((uint8_t)u1 - (uint8_t)u2); //-1 (bad)
  printTypeAndVal((uint8_t)(u1 - u2)); //255 (fixed!)
  printTypeAndVal((uint8_t)((uint8_t)u1 - (uint8_t)u2)); //255 (fixed!)
  cout << endl;

  //for unsigned 2-byte types, explicit casting of the OUTPUT is required too to get desired *unsigned* output
  cout << "uint16_t - uint16_t:" << endl;
  uint16_t u3 = 0;
  uint16_t u4 = 1;
  printTypeAndVal(u3 - u4); //-1 (bad)
  printTypeAndVal((uint16_t)(u3 - u4)); //65535 (fixed!)
  cout << endl;

  //for larger standard unsigned types, explicit casting of the OUTPUT is ***NOT*** required to get desired *unsigned* output! IN THIS CASE, NO IMPLICIT PROMOTION (CAST) TO A LARGER *SIGNED* TYPE OCCURS.
  cout << "unsigned int - unsigned int:" << endl;
  unsigned int u5 = 0;
  unsigned int u6 = 1;
  printTypeAndVal(u5 - u6); //4294967295 (good--no fixing is required)
  printTypeAndVal((unsigned int)(u5 - u6)); //4294967295 (good--no fixing was required)
  cout << endl;

  return 0;
}

您也可以在这里在线运行此程序:http://cpp.sh/6kjgq

下面是输出。请注意,C++编译器将单个无符号字节UINT8_T-UINT8_T和双无符号字节UINT16_T-UINT16_T都隐式强制转换(提升)为4字节有符号INT32_T(INT)变量类型。这是你需要注意的行为。因此,这些减法的结果是负的,这是一个最初让我困惑的不寻常行为,因为我已经预料到它会下溢,变成无符号变量的最大值(因为我们做的是0-1)。为了实现所需的下溢,我必须显式地将减法的输出结果强制转换为所需的无符号类型,而不仅仅是输入。但是,对于无符号int的情况,不需要对结果进行这种显式强制转换。

开始

uint8_t-uint8_t:
int32_t=-1
int32_t=-1
uint8_t=255
uint8_t=255

uint16_t-uint16_t:
int32_t=-1
uint16_t=65535

#include <stdio.h>

int main(int argc, char **argv) 
{
  unsigned char x = 130;
  unsigned char y = 130;
  unsigned char z = x + y;

  printf("%u\n", x + y); // Prints 260.
  printf("%u\n", z);     // Prints 4.
}

此处测试:http://cpp.sh/84eo

 类似资料:
  • 有一个不希望的C样式强制转换,我无法防止编译。不需要的强制转换执行从某个类的对象到其他类的非常量引用的C样式强制转换。这些类是不相关的。同时,我喜欢支持从同一个类的对象到常量引用的C样式转换。我提供了一个公共转换操作符来支持理想的强制转换。在这种情况下,似乎无法防止不希望的强制转换。 对非常量引用的强制转换未能生成(“Sandbox::B::操作符Sandbox::A&()”(在第30行声明)不可

  • 问题内容: 所以这工作: 但这不是: 总而言之,我得到了第一部分(拳击),但是我发现第二部分不起作用是非常不直观的。是否有特定的原因(除了String从Object继承而int不从Object继承)? 编辑: 为了完善我的问题,这也可以: 但是,以下内容却没有: 令人惊讶的是,您遇到了与String相同的问题: 在最后一行产生类强制转换异常。仍然有效: 问题答案: 我刚刚找到了我正在寻找自己的答案

  • 问题内容: 执行此强制转换时出现编译错误: 应该被继承,尽管不能直接继承。 从文档: 农具其中inturn & 为什么这无效? 也感谢您提供有关使用as 的正确方法的意见? 我正在考虑包装方法。 问题答案: 扩展,并且 不 扩展。 如果您想从中获得帮助,我认为实现包装器类是您最简单的选择。幸运的是的唯一抽象方法是。 RandomAccessFile实现了DataInput,该数据输入将依次转为Da

  • 问题内容: 我有以下状态: 然后我更新状态: 由于setState是假设要合并的,所以我希望它是: 但是它吃掉了id,状态为: 这是预期的行为吗?仅更新嵌套状态对象的一个​​属性的解决方案是什么? 问题答案: 我认为不做递归合并。 您可以使用当前状态的值构造一个新状态,然后调用该状态: 我在这里使用过函数function(来自underscore.js库),通过创建状态的浅表副本来防止对该状态的现

  • 问题内容: 我在一个新项目中将Swift样板代码用于Core Data。我的文件有一个定义的单一实体()和一个属性()。 我有一个看起来像这样的文件: 当我运行它时,它起作用: 我什至可以进入iOS模拟器正在使用的SQLite数据库,并确认已添加该行。 但是,当我运行与上面完全相同的代码,但使用而不是时,出现了与…行关联的错误消息,导致崩溃。如果我继续执行,则每次执行线程1时它都会到达并位于线程1