簡體   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