【问题标题】:Does C provide an operator to check the signedness of a type?C 是否提供运算符来检查类型的签名?
【发布时间】:2020-04-06 06:02:05
【问题描述】:

如果我正在使用当前使用特定数字类型别名的库,例如

typedef uint32_t library_type;

void library_function(library_type x);

我如何确保需要从不同类型传入值的代码保持正确,即使库更改了其 typedef?

uint64_t x = get_some_number();

// …need checks here…

library_function((library_type)x);

我可以添加以下检查:

assert(sizeof library_type >= sizeof uint32_t);
assert(x <= UINT32_MAX);

第二次检查将确保我得到的值在 current library_type 的范围内。库作者没有提供LIBRARY_TYPE_MAX 定义,因此第一个检查试图保护第二个,以防library_type 在将来编译代码时出于任何原因而更改。

如果 library_type 更改为例如int8_t,但如果 library_type 改为 int32_t 会怎样?它仍然是正确的“大小”,但范围仍然小于我正在检查的范围!

C 语言是否提供了一个内省类型符号的运算符,就像sizeof 让我知道宽度一样?有没有其他方法可以确保只有在正确的情况下才能到达我对library_type 的演员表?

【问题讨论】:

  • 您混淆了运行时和编译时检查。 C 在运行时并没有真正执行动态类型(除非您构建自己的某种方案来存储应该用于重新解释 C 最初将其视为原始数据的类型),因此您进行任何检查以确定是否源代码已更改必须在编译时进行,而不是像 assert() 这样的运行时检查。
  • 看起来 C++11 提供了std::is_signed;在这里我想知道C中是否有类似的东西(以及这是否是正确的方法;-)
  • 您是否试图防止对库源代码的错误编辑,或在运行时传递给库的错误参数?这两个问题都不是特别可以解决的,但在你澄清你的确切恐惧之前,这个问题真的是无法回答的。
  • 你不能删除显式转换然后用-Wconversion 捕获不正确的隐式转换吗?得到类似的东西:conversion to uint32_t {aka unsigned int} from int32_t {aka int} may change the sign of the result
  • 那么在调用函数之前将其转换为您想要检查的类型。 uint32_t x_cast = (uint32_t) x; library_function(x_cast);

标签: c casting typedef unsigned signed


【解决方案1】:

我如何确保需要从不同类型传入值的代码保持正确,即使库更改了其 typedef?

您试图防御的情况是一种不负责任的人类行为,而不是技术行为。

在 API 中更改参数的 typedef 是“重大更改” - 就像任何其他行为更改一样。这样的变化,如果不仅仅是一个错误修正,也可能是对其他可能更微妙和严重的暗示。

重大更改应记录在发行说明中。

通常,如果自动执行 API 中的重大更改,则可以通过版本标识符或功能查询等方式在构建和/或运行时检查 - 库头中的某处将是 #define LIBRARY_WHATEVER_VERSION 3然后你的代码可以有预处理指令来检查。

当重大更改非常严重时,通常会重命名软件本身,以至于错误的版本甚至无法满足#include 或链接尝试。

【讨论】:

  • 我认为你被这个假设库的设计分心了,它恰好由一个不关心我们意见的大公司控制。我可以编写可移植的 C 代码来检测是否可以将数字转换为更小但不知道的类型吗?
  • 我认为您会因一项重大更改而分心,而忽略其他更改。而且您的提供商不会在不增加版本号的情况下进行更改。在开始使用新版本之前,请花点时间了解更改!如果没有编码的版本,让您的构建系统检查标头的 md5sum,并且您认为同事在设置构建时可能会草率。更好的是,将所有供应商可交付成果捕获到一个 git 子模块中,即使他们只是将标头 + 二进制 zip 文件转储给您,您仍然可以跟踪它们的内容并在 git 中查看有意义的标头差异。
【解决方案2】:

是的,关系运算符与强制转换运算符相结合。例如:

_Bool TYPE_is_signed = ((TYPE)-1 < 0);

或者作为断言:

assert((TYPE)-1 < 0); // Require that TYPE is signed
assert((TYPE)-1 > 0); // Require that TYPE is unsigned

【讨论】:

  • 简洁,如果需要可以变成宏。在实践中,我可能更喜欢@kaylum 的解决方案,该解决方案使用current 基础类型的中间转换(例如转换为uint32_t 而不是library_type)并让编译器捕获任何重大更改。但这回答了我提出的问题。
  • assert(((TYPE1)-1 &lt; 0) == ((TYPE2)-1 &lt; 0));
猜你喜欢
  • 2014-06-17
  • 2011-01-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-04-29
  • 1970-01-01
  • 2022-11-04
  • 2017-12-24
相关资源
最近更新 更多