C/C++ 中的类型转换到底是啥?

Posted

技术标签:

【中文标题】C/C++ 中的类型转换到底是啥?【英文标题】:What exactly is a type cast in C/C++?C/C++ 中的类型转换到底是什么? 【发布时间】:2011-11-25 09:47:57 【问题描述】:

C/C++ 中的类型转换到底是什么?编译器如何检查是否需要(并且有效)显式类型转换?它是否比较一个值所需的空间?例如,如果我有:

int a;
double b = 15.0;
a = (int) b;

如果我没记错的话,双精度值需要比整数(4 字节)更多的空间(是 8 字节吗?!)。并且两者的内部表示完全不同(二/尾数的补码)。那么内部会发生什么呢?这里的例子很简单,但是在 C/C++ 中有很多类型转换。

编译器如何知道(或程序员)我是否可以转换,例如FOO 到 BAR?

【问题讨论】:

任何类型转换的完整解释都需要包括标准转换、用户定义转换、reinterpret_castconst_cast、向上转换、向下转换、交叉转换、RTTI 和值类型。 (我想我忘记了一个话题。) When should static_cast, dynamic_cast and reinterpret_cast be used?的可能重复 @Ben,我想我在谦虚的回答中提到了绝大多数,但没有(相当简洁的)答案可能被认为是完整的。 @MichaelGoldshteyn:当我写这篇文章时,您的 answer 编辑还没有出现,只有两个非常简短的答案专注于 double->int 转换。跨度> @Ben,是的,我花了一段时间来增强我的答案,也许比要求的更详细。但是,我和你有同样的认识,并决定细节是必要的,甚至试图对一般问题提出一个稍微完整的答案。 【参考方案1】:

编译器知道如何隐式执行某些类型转换 - double 到 int 就是其中之一。它只是去掉小数部分。内部表示会作为过程的一部分进行转换,因此分配可以正常工作。

请注意,有些值太大而无法正确转换。我不记得这种情况的规则是什么。它可以由编译器自行决定。

【讨论】:

我认为您的意思是说“int to double”,而不是相反。将双精度分配给 int 时,编译器会警告可能丢失数据。 @Praetorian,我不认为编译器警告是由标准指定的——这也是由编译器决定的。您的描述对于我使用的编译器是正确的。 @Tomolak 拒绝回答?这种问题对他来说就是母乳:-) @MaxLybbert C 和 C++ 标准需要实现来诊断...需要诊断的错误。文档应描述构成诊断的内容。该实现还可以诊断出他们想要的几乎任何东西,例如:“现在是凌晨 2 点,你现在不应该再进行编程了。”或“你正在用 C++ 编程,一个非常复杂的编程语言。你可能应该考虑学习一种更简单的语言。" 即使你想“去掉小数部分”,浮点类型也可以存储比整数类型大得多的整数。由于尾数的大小,double 的典型实现可以表示比 32 位整数类型大得多的整数。如果你想要小数部分,并且你确定它适合整数类型,你最后应该在源代码中尽可能明确地进行这种转换。【参考方案2】:

类型转换基本上是从一种类型到另一种类型的转换。它可以是隐式的(即由编译器自动完成,可能会在过程中丢失信息)或显式的(即由开发人员在代码中指定)。类型占据的空间是次要的。更重要的是转换的适用性(有时是方便性)。

隐式转换可能会丢失信息,可能会丢失/获得符号,并且可能会发生上溢/下溢。编译器不会保护您免受这些事件的影响,除非可能通过在编译时生成的警告。当派生类型隐式转换为基类型(按值)时,也会发生切片。

对于可能非常危险的转换(例如,从基类型到派生类型),C++ 标准需要显式转换。不仅如此,它还提供了更多限制性的显式转换,例如static_castdynamic_castreinterpret_castconst_cast,每个都进一步将显式转换限制为仅可能转换的子集,从而减少潜在的用于投射错误。

隐式和显式的有效转换最终由 C/C++ 标准定义,尽管在 C++ 中,开发人员可以通过使用构造函数和重载 ( cast) 运算符。

标准允许转换的完整规则以及不允许转换的完整规则可能非常复杂。我试图忠实地在这个答案中对其中一些规则进行简要的总结。如果您真的对允许和不允许的内容感兴趣,我强烈建议您访问标准并阅读有关类型转换的相应部分。

【讨论】:

"它提供了更多限制性的显式转换,例如 static_cast、dynamic_cast、reinterpret_cast 和 const_cast" 正确,但转换为浮点类型始终是 static_cast .您不需要在这里详细说明这些差异化的演员表。【参考方案3】:

为您的代码编写一个小 C 程序,然后按照 How to get GCC to generate assembly code 中的说明查看编译器如何进行类型转换。

【讨论】:

但它只会告诉您在给定架构上使用给定选项集调用给定编译器的功能。在许多领域,标准允许 C++ 行为在编译器之间有所不同(实现定义的、未指定的、未定义的行为)。此外,并非所有编译器都能完美地实现每个标准要求。【参考方案4】:

只想提一些经常被忽视的东西:

强制转换总是会创建一个临时的目标类型(尽管如果目标类型是引用,您不会注意到)。

这可能很重要。例如:

#include <iostream>

void change_one_print_other( int& a, const int& b )

    a = 0;
    std::cout << b << "\n";


int main(void)

    int x = 5, y = 5;

    change_one_print_other(x, x);
    change_one_print_other(y, static_cast<int>(y));

那个演员看起来没用。但是looks can be deceiving。

【讨论】:

"强制转换总是创建一个临时的目标类型(尽管如果目标类型是引用,你不会注意到)" 如果目标类型是引用,比演员表不会产生临时的;它会产生一个左值。 void change_one_print_other( int&amp; a, const int&amp; b ); change_one_print_other(y, static_cast&lt;int&gt;(y));如果你想避免这个错误(传递一个临时的引用),你需要一个指针:void change_one_print_other( int&amp; a, const int* pb );这里的每个人:我是这个系统的新手,对不起,如果我不使用这个网站的功能正确。 Ben Voigt 写道:“由于引用是透明的,因此您无法判断是否创建了临时引用。临时引用绑定到同一个目标,是的,它是一个左值(除非它是当然是右值引用)。” (已删除帖子的评论) 我的意思是说一个临时的永远不是一个引用,一个引用也永远不是一个临时的。除非它改变,否则临时值是右值,引用是左值。这可能是吹毛求疵,但在处理 C++ 时,IMO 使用最精确的词汇很重要。 @curiousguy: (*p) 是一个左值,即使p 是一个临时值。您可以以相同的方式考虑引用,但由于命名引用总是产生它所引用的值(引用是完全透明的),因此无法判断是否创建了临时引用。就像你不能问编译器引用有多大一样,你只能问引用的目标有多大。但这并不意味着引用不占用存储空间——它经常占用。 显然,reinterpret_cast 不会创建这个,但是,是的,static_cast 会。

以上是关于C/C++ 中的类型转换到底是啥?的主要内容,如果未能解决你的问题,请参考以下文章

处理 requestAttributionDetailsWithBlock 和 NSDictionary。在不知道它到底是啥的情况下将任何类型转换为 NSString

C/C++中的类型转换

C/C++中的类型转换

C/C++中的类型转换

c/c++类型转换相关总结

某些 Java 泛型类型转换中的类型安全警告是啥意思?