问题
I'm confused why the following code produces this output:
#include <iostream>
#include <string>
using namespace std;
int main()
{
int i = -1;
string s = "abc";
int j = s.size();
int x = 1 % 3;
int y = i % j;
int z = i % s.size();
cout << s.size() << endl; // 3
cout << x << endl; // 1
cout << y << endl; // -1
cout << z << endl; // 0
}
Why is z = 0? Does it have to do with casting?
回答1:
So, stripping down your code to a minimal example, you're asking why this prints 0
:
#include <iostream>
#include <string>
int main()
{
int a = -1;
std::string::size_type b = 3;
int c = a % b;
std::cout << c << '\n';
}
The primary operation in question here is this:
a % b
Per the standard,
5.6 Multiplicative operators [expr.mul]
- The operands of * and / shall have arithmetic or unscoped enumeration type; the operands of % shall have integral or unscoped enumeration type. The usual arithmetic conversions are performed on the operands and determine the type of the result.
So.. what about those "usual arithmetic conversions"? This is to mate the types of the two operands to a common type prior to performing the actual operation. The following are considered in order :
- If both operands are integers, integer promotion is first performed on both operands. If after integer promotion the operands still have different types, conversion continues as follows:
- If one operand has an unsigned type T whose conversion rank is at least as high as that of the other operand’s type, then the other operand is converted to type T.
- Otherwise, one operand has a signed type T whose conversion rank is higher than that of the other operand’s type. The other operand is converted to type T only if type T is capable of representing all values of its previous type.
- Otherwise, both operands are converted to the unsigned type that corresponds to the signed type T.
That's a lot of legalize for what effectively says this:
- You have two operands, a
signed int
and astd::string::size_type
- The rank of
std::string::size_type
is greater than that ofsigned int
- Therefore, the
signed int
operand is converted to typestd::string:size_type
prior to the operation being requested.
So all that is left is the conversion, to wit, there is one more piece of legalize:
4.7 Integral conversions [conv.integral]
- If the destination type is unsigned, the resulting value is the least unsigned integer congruent to the source integer (modulo 2n where n is the number of bits used to represent the unsigned type). [Note: In a two’s complement representation, this conversion is conceptual and there is no change in the bit pattern (if there is no truncation). —end note]
That means, on a 32-bit std::string::size_type
platform, you're going to get 232-1 as the converted value from int
(-1).
Which means...
4294967295 % 3
Which is... zero. If std::string::size_type
is 64-bits, then everything above stays the same, save for the final calculation, which would be:
18446744073709551615 % 3
回答2:
What really happens here:
int z = i % s.size();
is i
is converted to size_t
because the other side s.size()
is size_t
. And unlike int
, size_t
is unsigned; That is to say the value is interpreted as a positive number.
Check the output of this line:
std::cout << (size_t)-1 << std::endl;
to see what -1 has become.
回答3:
@GhaziMajdoub's answer is correct, but - why don't you let the compiler tell you what's happening?
Let's use the Flags to enable thorough and verbose g++ warnings ...
$ g++ -pedantic -Wall -Wextra -Wcast-align -Wcast-qual -Wctor-dtor-privacy \
-Wdisabled-optimization -Wformat=2 -Winit-self -Wlogical-op -Wmissing-declarations \
-Wmissing-include-dirs -Wnoexcept -Wold-style-cast -Woverloaded-virtual \
-Wredundant-decls -Wshadow -Wsign-conversion -Wsign-promo -Wstrict-null-sentinel \
-Wstrict-overflow=5 -Wswitch-default -Wundef -Werror -Wno-unused -o a a.cpp
a.cpp: In function ‘int main()’:
a.cpp:12:13: error: conversion to ‘std::__cxx11::basic_string<char>::size_type’ {aka
‘long unsigned int’} from ‘int’ may change the sign of the result
[-Werror=sign-conversion]
12 | int z = i % s.size();
| ^
cc1plus: all warnings being treated as errors
a.cpp: In function ‘int main()’:
a.cpp:12:13: warning: conversion to ‘std::__cxx11::basic_string<char>::size_type’
{aka ‘long unsigned int’} from ‘int’ may change the sign of the result [-Wsign-
conversion]
12 | int z = i % s.size();
| ^
and there you have it: i
is converted to long unsigned int
, so it's no longer -1
.
来源:https://stackoverflow.com/questions/61299317/why-does-the-c-modulo-operator-return-0-for-1-str-size