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?
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.
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 :
That's a lot of legalize for what effectively says this:
signed int
and a std::string::size_type
std::string::size_type
is greater than that of signed int
signed int
operand is converted to type std::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 2 n 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 2 32 -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
@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
.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.