ftp 企业网站,网站开发公司如何拓展业务,wordpress一键搭建,家居企业网站建设平台简介
C 类型转换是开发者必须掌握的重要技能之一, 无论是处理隐式转换还是显式转换, 理解其背后的机制与用法至关重要. 本篇博客旨在从基础到高级全面解析 C 的类型转换, 包括实际开发中的应用场景和性能分析. 自动转换
隐式类型转换
编译器可以在无需明确指示的情况下, 将一…简介
C 类型转换是开发者必须掌握的重要技能之一, 无论是处理隐式转换还是显式转换, 理解其背后的机制与用法至关重要. 本篇博客旨在从基础到高级全面解析 C 的类型转换, 包括实际开发中的应用场景和性能分析. 自动转换
隐式类型转换
编译器可以在无需明确指示的情况下, 将一种类型的值自动转换为另一种兼容类型. 例如:
struct Struct {Struct(float f) : m_(f) {}float m_ 0;
};
float f -3.1415926;
double d f;
int i f;
size_t s f;
char c f;
Struct st f;赋值语句值float f -3.14;-3.14159double d f;-3.14159int i f;-3size_t s f;18446744073709551613char c f;乱码Struct st f;{.m_ -3.14159}
算术类型转换
void drawLine(uint8_t start, uint8_t end);
uint8_t x 10;
uint8_t width 50;
// 此处等价于: drawLine(x, static_castunsigned char(static_castint(x) static_castint(width)));
drawLine(x, xwidth);符号转换
void print(const std::vectorint vec) {// 此处等价于: static_castunsigned long(i) vec.size()for (int i 0; i vec.size(); i) {std::cout i ,;}
}用户转换运算符
class Struct {public:Struct(float f) : m_(f) {}// 重载了转为int类型的操作符operator int() const { return m_; }private:float m_ 0;
};int main() {Struct si(1);int i si;
}显示类型转换
C 风格类型转换
(type)var;使用 var 创建 type 的临时变量type 可以是任何带有限定符的有效类型通过更改变量中位的含义来覆盖类型系统在某些情况下无法编译(稍后详细介绍)支持在 constexpr 上下文中使用(稍后详细介绍)可能导致未定义的行为参与运算符优先级(级别 3)
struct A {};
struct B {};int main() {float f 7.406f;int i (int)f; // int i static_castint(f);A* pa (A*)f; // A* pa reinterpret_castA*(f);B* pb (B*)pa; // B* pb reinterpret_castB*(pa);double d *(double*)(pb); // double d *reinterpret_castdouble*((pb));return 0;
}C 风格和函数式符号转换的问题
单一符号, 多重含义容易出错无法 grep使 C 和 C 语法复杂化
C 强制转换的目标
不同的符号或不同的任务易于识别和搜索执行 C 强制转换可以执行的所有操作消除意外错误使强制转换不那么诱人
C有如下几种类型转换的关键词:
static_castconst_castdynamic_castreinterpret_cast
static_cast
T1 var;
T2 var2 static_castT(var)从 var 类型创建临时变量尝试通过隐式和用户定义的转换或构造找到从 T1 到 T2 的路径. 无法删除 const 限定.
使用场景: 阐明隐式转换 int i 1;
double d static_castdouble(i);指示有意截断 int a 1234;
uint8_t u8 static_castuint8_t(a);在基类和派生类之间进行强制转换 struct Base {};
struct Derived : public Base {};Derived derived;
Base rb derived;
Derived rd static_castDerived(rb);在 void* 和 T* 之间进行强制转换 struct MyStruct {};
void callback(void* handle) {auto p static_castMyStruct*(handle);//...
}多重转换
#include cstdiostruct A {explicit A(int) { puts(A); }
};
struct E {operator int() {puts(B::operator int);return 0;}
};int main() {E e;A a static_castA(e);return 0;
}A 有一个接受单个 int 的构造函数E 有一个用户定义的到 int 的转换所以从e到a的路径为: e - int - a
static_cast 与继承
#include iostreamstruct B1 {virtual ~B1() default;int i;
};struct B2 {virtual ~B2() default;int j;
};struct Derived : public B1, public B2 {int k;
};void Compare(void* p1, void* p2) {if (p1 p2) {std::cout Same.\n;} else {std::cout Different.\n;}
}int main() {Derived d;// pd 指向派生类Derived* pd d;// pb1 是指向基类B1的指针B1* pb1 static_castB1*(d);Compare(pd, pb1); // Same.// pb2 是指向基类B1的指针B2* pb2 static_castB2*(d);Compare(pd, pb2); // Different.void* derived_plus_offset (char*)pd sizeof(B1);Compare(derived_plus_offset, pb2); // Same.return 0;
}为什么会出现这样的情况? 因为Derived的布局为:
--------- --- pd and pb1
| B1 |
--------- --- pb2
| B2 |
---------
| Derived |
---------...static_cast 并非绝对正确
static_cast 无法防止向下转型为不相关的类型
#include iostream
#include type_traitsstruct Base {virtual void f() { std::cout base\n; }virtual ~Base() default;
};
struct Derived : public Base {void f() override { std::cout Derived\n; }
};struct Other : public Base {void f() override { std::cout Other\n; }
};int main() {Derived d;Base b d; // OKd.f(); // Derivedb.f(); // DerivedOther a static_castOther(b); // 危险, 转换到了其他类型a.f(); // Derivedstatic_assert(std::is_samedecltype(a), Other::value, not the same);return 0;
}const_cast
从变量中删除或添加 const 或 volatile 限定符, 不能更改类型不会更改原始变量的 CV 限定符
#include iostreamvoid use_pointer(int* p) { std::cout *p *p std::endl; }
void modify_pointer(int* p) {*p 42;std::cout \tmodify_pointer *p - 42\n \tmodify_pointer *p *p std::endl;
}int main() {const int i 7;use_pointer(const_castint*(i));modify_pointer(const_castint*(i));std::cout i i std::endl; // i 7int j 4;const int* cj j;modify_pointer(const_castint*(cj));std::cout i i std::endl; // i 7return 0;
}输出
*p 7modify_pointer *p - 42modify_pointer *p 42
i 7modify_pointer *p - 42modify_pointer *p 42
i 7可以看到虽然在函数modify_pointer里面指针指向的值发生了变化, 但是在外面的值却不受影响.
const_cast example: member overload
#include stddef.hclass my_array {public:char operator[](size_t offset) {// 此处调用const版本的实现, 避免重写一遍逻辑.return const_castchar(const_castconst my_array(*this)[offset]);}const char operator[](size_t offset) const { return buffer[offset]; }private:char buffer[10];
};
int main() {const my_array a{};const auto c a[4];my_array mod_a;mod_a[4] 7;return 0;
}用于防止成员函数的代码重复.
运行时类型信息 (RTTI)
为实现定义的结构中的每个多态类型存储额外信息允许在运行时查询类型信息可以禁用以节省空间(gcc/clang: –fno-rtti, msvc: /GR-)
dynamic_cast
查看 To 是否与 From 位于同一公共继承树中只能是引用或指针不能删除 CVFrom 必须是多态的需要 RTTI如果类型不相关, 则对指针返回 nullptr, 对引用抛出 std::bad_cast
#include cstdio
#include vectorstruct A {virtual ~A() default;
};
struct B : public A {};
struct C : public A {};
int main() {C c;B b;std::vectorA* a_list {c, b};for (size_t i 0; i a_list.size(); i) {A* pa a_list[i];if (dynamic_castB*(pa)) {printf(a_list[%lu] was a B\r\n, i);}if (dynamic_castC*(pa)) {printf(a_list[%lu] was a C\r\n, i);}}return 0;
}dynamic_cast 用例: UI 框架
struct Widget {};
struct Label : public Widget {};
struct Button : public Widget { void DoClick(); };dynamic_cast can be expensive
from gcc’s rtti.c
reinterpret_cast
#include cstdintstruct A {};
struct B {int i;int j;
};int main() {int i 0;int* pi i;uintptr_t uipt reinterpret_castuintptr_t(pi);float f reinterpret_castfloat(i);A a;B* pb reinterpret_castB*(a);char buff[10];B* b_buff reinterpret_castB*(buff);return 0;
}可以将任何指针或引用类型更改为任何其他指针或引用类型也称为类型双关不能在 constexpr 上下文中使用不能删除 CV 限定不确保 To 和 From 的大小相同适用于内存映射功能
reinterpret_cast 访问私有继承的基类
struct B {void m() { puts(private to D); }
};
struct D : private B {};
int main() {D d;B b reinterpret_castB(d);b.m();return 0;
}Type Aliasing
当两种类型的内存布局兼容时, 将一种类型的内存当作另一种类型的内存来使用的行为.
compatible types
struct Point {int x;int y;
};
struct Location {int x;int y;
};
Point p{1, 2};
auto* loc reinterpret_castLocation*(p);incompatible types
float f 1.0f;
int* i reinterpret_castint*(f);C 风格类型转换在 C 中是如何实际执行的
对与一个类型转换
T conv (T)val;C会依次尝试:
T conv const_castT(val);T conv static_castT(val);T conv const_castT(static_castconst T(val));T conv reinterpret_castT(val);T conv const_castT(reinterpret_castconst T(val));
如果找到匹配则会选择并执行编译, 否则会报错.
总结
C 提供了更安全, 更明确的类型转换工具, 开发者应根据场景选择合适的转换方式. 通过熟练掌握这些工具, 您可以编写更健壮, 更易维护的代码. 希望本博客能帮助您更深入地理解 C 类型转换的精髓!
参考资源
Back to Basics: Casting - Brian Ruth - CppCon 2021