繁体   English   中英

为什么 C++ 模运算符为 -1 % str.size() 返回 0?

[英]Why does the C++ modulo operator return 0 for -1 % str.size()?

我很困惑为什么下面的代码会产生这个 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
}

为什么z = 0? 跟选角有关系吗?

这里真正发生了什么:

int z = i % s.size();

is i被转换为size_t因为另一边s.size()size_t int不同, size_t是无符号的; 也就是说,该值被解释为正数。

检查这一行的output:

std::cout << (size_t)-1 << std::endl;

看看 -1 变成了什么。

因此,将您的代码简化为一个最小的示例,您会问为什么会打印0

#include <iostream>
#include <string>

int main()
{
    int a = -1;
    std::string::size_type b = 3; 
    int c = a % b;
    std::cout << c << '\n';
}

这里有问题的主要操作是这样的:

a % b

按照标准,

5.6 乘法运算符 [expr.mul]

  1. * 和 / 的操作数应具有算术或非范围枚举类型; % 的操作数应具有整数或无范围枚举类型。 通常的算术转换在操作数上执行并确定结果的类型

那么..那些“通常的算术转换”呢? 这是为了在执行实际操作之前将两个操作数的类型匹配为一个通用类型。 以下是按顺序考虑的

  • 如果两个操作数都是整数,则首先对两个操作数执行 integer 提升。 如果 integer 提升后操作数仍然有不同的类型,则转换继续如下:
    • 如果一个操作数的无符号类型 T 的转换等级至少与另一个操作数的类型一样高,则另一个操作数将转换为类型 T。
    • 否则,一个操作数的有符号类型 T 的转换等级高于另一操作数类型的转换等级。 只有当类型 T 能够表示其先前类型的所有值时,另一个操作数才会转换为类型 T。
    • 否则,两个操作数都转换为对应于有符号类型 T 的无符号类型。

对于有效地说明这一点来说,这是很多合法化的:

  • 您有两个操作数,一个带signed int和一个std::string::size_type
  • std::string::size_type的秩大于signed int
  • 因此,有signed int操作数在请求操作之前被转换为std::string:size_type类型。

所以剩下的就是转换,也就是说,还有一个合法化:

4.7 积分转换[conv.integral]

  1. 如果目标类型是无符号的,则结果值是与源 integer 一致的最小无符号 integer(模 2 n ,其中 n 是用于表示无符号类型的位数)。 [注意:在二进制补码表示中,这种转换是概念性的,位模式没有变化(如果没有截断)。 ——尾注]

这意味着,在 32 位std::string::size_type平台上,您将获得 2 32 -1 作为来自int (-1) 的转换值。

意思是...

4294967295 % 3

这是...... 如果std::string::size_type是 64 位,那么上面的一切都保持不变,除了最后的计算,这将是:

18446744073709551615 % 3

@GhaziMajdoub 的回答是正确的,但是 - 你为什么不让编译器告诉你发生了什么?

让我们使用标志来启用彻底和详细的 g++ 警告......

$ 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();
      |             ^

你有它: i被转换为long unsigned int ,所以它不再是-1

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM