我目前正在使用 Accelerated C ++ ,并在练习2-3中遇到了一个问题。
程序的快速概述 - 程序基本上采用名称,然后在星号框架内显示问候语 - 即Hello!被*的框架包围着。
练习 - 在示例程序中,作者使用const int
来确定问候语和星号之间的填充(空格)。然后,作为练习的一部分,他们要求读者询问用户输入他们想要填充的大小。
所有这一切似乎都很容易,我继续向用户询问两个整数(int
)并存储它们并更改程序以使用这些整数,删除作者使用的整数,编译时虽然我得到了以下警告;
Exercise2-3.cpp:46:警告:有符号和无符号整数表达式之间的比较
经过一些研究后,似乎是因为代码试图将上述整数之一(int
)与string::size_type
进行比较,这很好。但我想知道 - 这是否意味着我应该将其中一个整数更改为unsigned int
?明确说明我的整数是签名还是未签名是否很重要?
cout << "Please enter the size of the frame between top and bottom you would like ";
int padtopbottom;
cin >> padtopbottom;
cout << "Please enter size of the frame from each side you would like: ";
unsigned int padsides;
cin >> padsides;
string::size_type c = 0; // definition of c in the program
if (r == padtopbottom + 1 && c == padsides + 1) { // where the error occurs
以上是代码的相关位,c
属于string::size_type
类型,因为我们不知道问候可能有多长时间 - 但是为什么我现在会在作者的代码中遇到这个问题使用const int
时没有遇到问题?此外 - 对于可能已经完成 Accelerated C ++ 的任何人 - 这将在本书后面解释吗?
我在Linux Mint上使用g ++通过Geany,如果这有助于或有所作为(因为我读到它可以在确定string::size_type
时)。
答案 0 :(得分:81)
如果将变量与大小进行比较,通常最好将变量声明为unsigned
或size_t
,以避免此问题。尽可能使用您要比较的确切类型(例如,与std::string::size_type
的长度进行比较时使用std::string
。
编译器会发出有关比较有符号和无符号类型的警告,因为有符号和无符号整数的范围不同,当它们相互比较时,结果可能会令人惊讶。如果必须进行这样的比较,则应该在检查之后将其中一个值显式转换为与另一个值兼容的类型,以确保转换有效。例如:
unsigned u = GetSomeUnsignedValue();
int i = GetSomeSignedValue();
if (i >= 0)
{
// i is nonnegative, so it is safe to cast to unsigned value
if ((unsigned)i >= u)
iIsGreaterThanOrEqualToU();
else
iIsLessThanU();
}
else
{
iIsNegative();
}
答案 1 :(得分:7)
我昨天在加速C ++中遇到问题2-3时遇到了完全相同的问题。关键是将要比较的所有变量(使用布尔运算符)更改为兼容类型。在这种情况下,这意味着string::size_type
(或unsigned int
,但由于此示例使用前者,我将坚持使用它,即使这两者在技术上是兼容的)。
请注意,正如您正确指出的那样,在原始代码中,他们为c计数器(本书第2.5节第30页)完成了这一操作。
使这个例子更复杂的原因是不同的填充变量(padsides和padtopbottom)以及所有计数器必须也更改为string::size_type
。
以您的示例为例,您发布的代码最终会如下所示:
cout << "Please enter the size of the frame between top and bottom";
string::size_type padtopbottom;
cin >> padtopbottom;
cout << "Please enter size of the frame from each side you would like: ";
string::size_type padsides;
cin >> padsides;
string::size_type c = 0; // definition of c in the program
if (r == padtopbottom + 1 && c == padsides + 1) { // where the error no longer occurs
请注意,在前一个条件中,如果未在string::size_type
循环中将变量r初始化为for
,则会收到错误。因此,您需要使用以下内容初始化for循环:
for (string::size_type r=0; r!=rows; ++r) //If r and rows are string::size_type, no error!
所以,基本上,一旦你在混合中引入string::size_type
变量,任何时候你想对该项执行布尔操作,所有操作数必须具有兼容的类型才能在没有警告的情况下进行编译。
答案 2 :(得分:4)
在极端范围内,unsigned int可以变得大于int 因此,编译器会生成警告。如果您确定这不是问题,请随意将类型转换为相同的类型,以便警告消失(使用C ++强制转换,以便它们很容易被发现)。
或者,使变量的类型相同,以阻止编译器抱怨 我的意思是,是否可能有负填充?如果是,那么将其保存为int。否则你应该使用unsigned int并让流捕获用户键入负数的情况。
答案 3 :(得分:4)
有符号和无符号整数之间的重要区别 是最后一点的解释。最后一点 在签名类型中代表数字的符号,意思是: e.g:
0001是1签名和未签名 1001是-1签名和9无签名
(为了清楚解释,我避免了整个补充问题! 这并不完全是如何在内存中表示整数!)
你可以想象,如果你比较就会有所不同 用-1或+9。在许多情况下,程序员太懒了 将int计数为unsigned(膨胀为for循环头f.i.) 这通常不是问题,因为使用整数你必须计算到2 ^ 31 直到你的标志咬你。这就是为什么它只是一个警告。 因为我们懒得写'unsigned'而不是'int'。
答案 4 :(得分:0)
或使用this header library并写:
// |notEqaul|less|lessEqual|greater|greaterEqual
if(sweet::equal(valueA,valueB))
并且不关心签名/未签名或不同大小
答案 5 :(得分:0)
主要问题是底层硬件CPU仅具有比较两个有符号值或比较两个无符号值的指令。如果您向无符号比较指令传递一个带符号的负值,它将把它视为一个大的正数。因此,-1(所有位都打开(二进制补码))的位模式将变为相同位数的最大无符号值。
8位:-1有符号与255无符号相同 16位:-1有符号与65535无符号相同 等
因此,如果您具有以下代码:
int fd;
fd = open( .... );
int cnt;
SomeType buf;
cnt = read( fd, &buf, sizeof(buf) );
if( cnt < sizeof(buf) ) {
perror("read error");
}
您会发现,如果由于文件描述符无效(或其他一些错误)而导致read(2)调用失败,则该cnt将被设置为-1。与无符号值sizeof(buf)进行比较时,if()语句将为false,因为0xffffffff不小于sizeof()某些(合理的,未被认为是最大大小)数据结构。
因此,如果要删除已签名/未签名的警告,则必须编写以上内容:
if( cnt < 0 || (size_t)cnt < sizeof(buf) ) {
perror("read error");
}
这只是大声说出了问题。
1. Introduction of size_t and other datatypes was crafted to mostly work,
not engineered, with language changes, to be explicitly robust and
fool proof.
2. Overall, C/C++ data types should just be signed, as Java correctly
implemented.
如果值太大,以至于找不到有效的带符号值类型,则说明您选择的语言使用的处理器太小或值的幅度太大。如果像金钱一样,每个数字都很重要,那么在大多数语言中都有可以使用的系统可以为您提供无限的精度。 C / C ++不能很好地做到这一点,并且您必须非常明确地了解类型中的所有内容,如此处许多其他答案所述。