【问题标题】:gcc size_t and sizeof arithmetic conversion to intgcc size_t 和 sizeof 算术转换为 int
【发布时间】:2019-12-15 15:04:03
【问题描述】:

我决定在启用 -Wsign-conversion 的情况下测试编译一个项目,看看会出现什么警告,并遇到了一些看起来不正确的东西,其中 gcc 的行为与 clang 不同。谁能告诉我哪个是正确的?

我有一个接受size_t 参数的函数:

void func(size_t) {}

其他结构

struct Test {};

和调用代码

int i = some_initialiser();
func(sizeof(Test) + static_cast<size_t>(i));

所以根据我的理解,sizeof 返回size_t,而size_t 类型的两个变量之间的算术应该返回size_t,所以除了我的static_cast 之外,这里不应该有任何转换,但是gcc 给了我警告

 warning: conversion to ‘long unsigned int’ from ‘int’ may change the sign of the result [-Wsign-conversion]

Clang 不会在此处发出警告,但如果我按预期删除函数调用中的 static_cast,则会发出警告。

【问题讨论】:

  • 使用 gcc 9 的更详细的错误消息指向 static_cast 作为警告的来源。如果仅将 static_cast 作为参数传递,警告就会消失。 gcc 似乎对 int 转换为 size_t 感到不满,但仅适用于加法操作。显然,对size_t 的显式强制转换不足以为+ 运算符关闭它,但如果将结果作为参数传递,它会保持沉默。
  • 有趣的是,如果我将sizeof 移动到局部变量中,它会在它被声明为auto 时发出警告,但如果它被声明为size_t 则不会发出警告。 sizeof 的返回类型可能有些奇怪?
  • 当然警告与从isize_t 的转换有关,而不是添加。也许 clang 将显式转换视为程序员元通信以抑制警告
  • @MM 在问这个问题时,我假设警告按照文档所说的那样做,并且只对隐式转换发出警告,所以出于某种原因考虑 gcc 在某处进行隐式转换称呼。正如现在接受的答案中所述,问题在于 gcc 的警告实现与其文档不匹配,而不是它正在执行意外的隐式转换。

标签: c++ implicit-conversion gcc-warning


【解决方案1】:

警告是正确的。

如果i 具有负值,则强制转换会出现问题。您的函数应该返回一个无符号值(例如 unsigned int)。

来自 GCC 文档 - https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html:

对于 C++,还警告用户定义转换的重载分辨率混淆;以及从不使用类型转换运算符的转换:到void、相同类型、基类或对它们的引用的转换。 C++ 中默认禁用有符号和无符号整数之间转换的警告,除非显式启用 -Wsign-conversion

【讨论】:

  • 它不应该警告显式转换,只警告隐式转换。如果不对具有sizeof 的结果的强制转换结果执行算术运算,则不会发出警告。
  • @JohnIlacqua:谁说它不应该对显式转换发出警告?编译器可以警告他们喜欢的任何事情。 (顺便说一句,没有“隐式强制转换”之类的东西;强制转换是一种显式转换。)
  • @JohnIlacqua:但这种行为似乎与 gcc 的文档相矛盾。 OP 在调用 gcc 时明确使用了-Wsign-conversion,并且文档说“显式强制转换使警告静音”。
  • 我已更新我的答案并添加了 GCC 文档中的相关文本。希望现在可以澄清它。
  • @Tomer:您链接的文档为 -Wsign-conversion 本身指定(添加了重点):“警告可能会更改整数值符号的 隐式 转换,例如分配一个有符号整数表达式到一个无符号整数变量。显式转换使警告静音。" 这里的转换是显式的,加法是size_t + size_t,结果作为size_t传递,所以没有隐式正在铸造。
【解决方案2】:

这是 gcc 中的一个已知错误,已在 9.3.0 及更高版本中修复。

警告是有效的(编译器可以警告他们喜欢的任何东西),但是 gcc 的行为与其自己的文档相矛盾。有针对此问题的现有错误报告(见下文)。

这里有一个更简单的测试用例来说明这个问题:

#include <cstddef>
int main() {
    int i = 42;
    size_t s0 = sizeof (int) + (size_t)i;
    size_t s1 = sizeof (int) + static_cast<size_t>(i);
}

当我使用 gcc 9.1.0 在我的系统上编译它时,我得到:

$ g++ -Wsign-conversion -c c.cpp
c.cpp: In function ‘int main()’:
c.cpp:4:32: warning: conversion to ‘long unsigned int’ from ‘int’ may change the sign of the result [-Wsign-conversion]
    4 |     size_t s0 = sizeof (int) + (size_t)i;
      |                                ^~~~~~~~~
c.cpp:5:32: warning: conversion to ‘long unsigned int’ from ‘int’ may change the sign of the result [-Wsign-conversion]
    5 |     size_t s1 = sizeof (int) + static_cast<size_t>(i);
      |                                ^~~~~~~~~~~~~~~~~~~~~~
$ 

请注意,C 样式转换和static_cast 都会出现警告。

确实,转换可能会改变结果的符号(将负的int 转换为size_t 会产生正的结果),但gcc 的-Wsign-conversion 文档说:

'-Wsign-conversion'
     Warn for implicit conversions that may change the sign of an
     integer value, like assigning a signed integer expression to an
     unsigned integer variable.  An explicit cast silences the warning.
     In C, this option is enabled also by '-Wconversion'.

在这种情况下,显式转换不会消除警告。

此错误已被报告:
Bug 87519 - -Wsign-conversion -Wconversion explicit cast fails to silence warning

修复是在 gcc git repo 中提交 61e52125c935279af11b10d27060a96bff7477a4,于 2019 年 8 月 8 日提交。

【讨论】:

  • 谢谢。我曾假设警告已正确实现,并且发生了某种意外的隐式转换,但这是有道理的 - 出于某种原因,在某些情况下,显式转换无法使警告静音。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-05-13
  • 1970-01-01
相关资源
最近更新 更多