C/C++共有的类型转换与c++特有的四种强制类型转换

news2025/5/20 3:44:43

前言

C 语言和 C++ 共有的类型转换:

  • 自动类型转换(隐式类型转换): 编译器在某些情况下会自动进行的类型转换。
  • 强制类型转换(显示类型转换): 使用 (type)expressiontype(expression) 语法进行的类型转换。

C++ 中新增的类型转换关键字:

  • static_cast
  • const_cast
  • reinterpret_cast
  • dynamic_cast

这四个关键字用于支持 C++ 特有风格的强制类型转换,它们相比 C 风格的强制类型转换提供了更精细的控制和更好的类型安全性。

C/C++共有的类型转换

自动类型转换(隐式类型转换

隐式类型转换是指编译器在没有明确指示的情况下自动进行的类型转换。在算术和逻辑运算中,常见的隐式转换规则如下:

算术运算中的类型转换:

在 C/C++ 中,表达式的两个操作数可能是不同类型的,比如一个是 int,一个是 float。为了进行运算,它们必须是相同的类型。这时候,编译器就需要根据一定规则来“提升”它们,使它们变为同一种类型,然后再进行运算。

常用算术转换主要发生在以下运算符的操作数上:

  • 二元算术运算符:+、-、*、/、%(模运算只用于整数)
  • 位运算:&、|、^、<<、>>
  • 比较运算符:<、>、<=、>=、==、!=

常用算术转换流程图:

1. 是否含有 long double?→ 转为 long double
2. 否 → 是否含有 double?→ 转为 double
3. 否 → 是否含有 float?→ 转为 float
4. 否 → 整型提升:
   a. char/short → int
   b. 比较剩余整型:unsigned long long > long long > unsigned long > long > unsigned int > int

由于之前我没有了解过long double类型,所以特意来补充介绍一下这个类型。 

逻辑运算和关系运算中的类型转换:

在逻辑运算(&&、||、!)和关系运算(<、>、<=、>=、==、!=)中,操作数通常会被转换为 bool 类型进行求值。任何非零值都会被转换为 true,而零值会被转换为 false。运算的结果也是 bool 类型。

赋值运算中的类型转换:

在赋值运算(=)中,右侧表达式的值会被转换为左侧变量的类型。这可能会导致数据丢失或精度损失。

int i = 3.14; // double 类型的 3.14 会被截断为 int 类型的 3
double d = 10; // int 类型的 10 会被提升为 double 类型的 10.0

 

隐式类型转换的注意

数据丢失和精度损失:

浮点型转换为整型: 当将 floatdouble 类型的值赋给 int 或其他整型变量时,小数部分会被直接截断,导致数据丢失。

精度降低的浮点类型转换:double 类型的值赋给 float 类型变量时,可能会损失精度,因为 float 的精度范围比 double 小。

有符号类型转换为无符号类型: 当将一个负的有符号整数赋给一个无符号整数变量时,其值会根据无符号类型的范围进行重新解释,通常会变成一个非常大的正数。

意想不到的符号性问题:

有符号和无符号整数的混合运算: 当有符号和无符号整数进行算术运算时,通常会将有符号整数隐式转换为无符号整数。如果带符号数是负数,这会导致非常令人困惑的结果。

函数参数的隐式转换:

当向函数传递参数时,如果实参的类型与形参的类型不完全匹配,编译器可能会尝试进行隐式转换。这可能导致类型不匹配的错误,或者调用了意想不到的函数重载版本。

隐式类型转换使用建议

  • 尽量保持类型一致: 在进行运算和赋值时,尽量使用相同或兼容的类型,减少隐式转换的发生。
     
  • 尽量不要有符号类型和无符号类型混用: 因为这样会涉及到 负数的意外转换,比较运算的陷阱,算术运算的意外结果等,这类bug非常难排查!!!
     
  • 使用显式类型转换: 当需要进行类型转换时,使用 static_castdynamic_castconst_castreinterpret_cast 等 C++ 风格的强制类型转换运算符,明确表达你的意图,并让编译器进行必要的类型检查(在适用情况下)。

强制类型转换(显示类型转换)

(type)expression

是对紧跟在后面的变量或子表达式进行的类型转换,默认情况下,它只对紧随其后的那个操作数起作用。

int a = 5;
int b = 2;
float result;

// 示例 1:只转换 'a'
result = (float) a / b;
// 这里,(float) 只作用于变量 'a'。'a' 先被转换为 float (5.0),然后与 'b' (int 型,会被隐式转换为 float) 相除,结果是 float (2.5)。

// 示例 2:只转换 'b'
result = a / (float) b;
// 这里,(float) 只作用于变量 'b'。'b' 先被转换为 float (2.0),然后 'a' (int 型,会被隐式转换为 float) 与其相除,结果是 float (2.5)。

// 示例 3:转换整个表达式的结果
result = (float) (a / b);
// 这里,括号内的 (a / b) 会先进行整数除法运算,结果是 int (2)。然后,(float) 将这个整数结果 2 转换为 float (2.0)。

// 示例 4:不加括号,只转换 'a'
result = (float) a + b;
// 这里,(float) 只作用于 'a','a' 被转换为 float (5.0),然后与 'b' (int 型,会被隐式转换为 float) 相加,结果是 float (7.0)。

// 示例 5:转换 'b'
result = a + (float) b;
// 这里,(float) 只作用于 'b','b' 被转换为 float (2.0),然后 'a' (int 型,会被隐式转换为 float) 与其相加,结果是 float (7.0)。

type(expression)

这种语法看起来像一个函数调用,其中 type 是目标数据类型,expression 是要转换的值。

double double_num = 3.14159;
int integer_part;

integer_part = int (double_num); // 将 double_num 强制转换为 int 类型

 

算法竞赛中注意

int a;
int b;
long long c = a + b;

你可能乍一看觉得上面这个代码很对,甚至会沾沾自喜,觉得自己考虑到了a+b的结果会溢出int范围,所以将c设置成了long long类型,但是我只能说你只做对了一半~

事实上:上面这个写法在发生溢出时候,最终 c 依然得不到正确的数学结果!!!

 

为什么在a+b溢出时,不能得到正确结果?

运算的类型决定结果的类型: 在 C/C++ 中,算术运算的结果类型通常由参与运算的操作数的类型决定。在 a + b 这个表达式中,ab 都是 int 类型,因此,它们的加法运算会以 int 类型的规则进行。 

赋值给 long long 的时机: 即使你将 a + b 的结果赋值给 long long 类型的变量 c,溢出也可能在 a + b 运算完成之后才发生。当 a + b 发生溢出并产生一个错误的 int 值后,这个错误的 int 值才会被提升(隐式转换)为 long long 类型并赋给 c。此时,c 存储的仍然是那个错误的、溢出后的值,而不是 ab 的真实数学和。

如何避免溢出并得到正确结果?

其中一个操作数/两个操作数全部强制转换为 long long,注意一定不是对于整个表达式a+b进行强制类型转换

long long c = (long long)a + b; // 或者 long long c = a + (long long)b;
long long c = (long long)a + (long long)b;

 注意千万不能像下面这样对a+b这个表达式进行转化

long long c = (long long)(a + b);

这种写法只是将可能已经溢出的 int 结果转换为 long long 类型,并不能防止 a + b 运算过程中 int 类型的溢出。要得到正确的 long long 结果,你需要确保加法运算本身是以 long long 的精度进行的。

c++特有的四种强制类型转换

static_cast<目标类型>(表达式);

const_cast<目标类型>(表达式);

reinterpret_cast<目标类型>(表达式);

dynamic_cast<目标类型>(表达式);

static_cast

static_cast: 用于执行可以在编译时确定的类型转换,例如基本数据类型之间的转换(但不会进行安全性检查,例如从 int* 到 char*),以及具有继承关系的类型之间的转换(向上转型是安全的,向下转型需要程序员保证安全性)。

double d = 3.14;
int i = static_cast<int>(d);

class Base {};
class Derived : public Base {};
Derived* derivedPtr = new Derived();
Base* basePtr = static_cast<Base*>(derivedPtr); // 向上转型

dynamic_cast

dynamic_cast: 主要用于在继承层次结构中执行安全的向下转型。它会在运行时检查转换是否有效,如果转换不合法(例如,尝试将一个基类指针转换为一个派生类指针,而该基类指针实际上指向的是基类对象),则返回空指针(对于指针类型)或抛出 std::bad_cast 异常(对于引用类型)。dynamic_cast 只能用于具有虚函数的基类。

class Base { virtual void func() {} };
class Derived : public Base {};
Base* basePtr = new Derived();
Derived* derivedPtr = dynamic_cast<Derived*>(basePtr); // 向下转型,运行时检查
if (derivedPtr) {
    // 转换成功
} else {
    // 转换失败
}

 

const_cast

const_cast: 用于添加或移除变量的 const 或 volatile 限定符。通常情况下,应该避免使用 const_cast!!!因为它可能会破坏程序的常量性。

const int ci = 10;
int* nonConstPtr = const_cast<int*>(&ci);
*nonConstPtr = 20; // 修改了原本声明为 const 的变量,可能导致未定义行为

reinterpret_cast

reinterpret_cast: 执行低级的位模式重新解释。这是最危险的类型转换运算符,应该谨慎使用。它通常用于不同类型指针之间的转换,或者将指针转换为整数类型,反之亦然。reinterpret_cast 不进行任何类型检查,仅仅是重新解释内存中的位。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2332662.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【蓝桥杯】贪心算法

1. 区间调度 1.1. 题目 给定个区间,每个区间由开始时间start和结束时间end表示。请选择最多的互不重叠的区间,返回可以选择的区间的最大数量。 输入格式: 第一行包含一个整数n,表示区间的数量 接下来n行,每行包含两个整数,分别表示区间的开始时间和结束时间 输出格式:…

OSPF接口的网络类型和不规则区域

网络类型(数据链路层所使用的协议所构建的二层网络类型) 1、MA --- 多点接入网络 BMA --- 支持广播的多点接入网络 NBMA --- 不支持广播的多点接入网络 2、P2P --- 点到点网络 以太网 --- 以太网最主要的特点是需要基于MAC地址进行物理寻址&#xff0c;主要是因为以太网接口所连…

idea 创建 maven-scala项目

文章目录 idea 创建 maven-scala项目1、创建普通maven项目并且配置pom.xml文件2、修改项目结构1&#xff09;创建scala目录并标记成【源目录】2&#xff09;导入scala环境3&#xff09;测试环境 idea 创建 maven-scala项目 1、创建普通maven项目并且配置pom.xml文件 maven依赖…

ansible+docker+docker-compose快速部署4节点高可用minio集群

目录 github项目地址 示例服务器列表 安装前 修改变量文件group_vars/all.yml 修改ansible主机清单 修改setup.sh安装脚本 用法演示 安装后验证 github项目地址 https://github.com/sulibao/ansible_minio_cluster.git 示例服务器列表 安装前 修改变量文件group_var…

使用libcurl编写爬虫程序指南

用户想知道用Curl库编写的爬虫程序是什么样的。首先&#xff0c;我需要明确Curl本身是一个命令行工具和库&#xff0c;用于传输数据&#xff0c;支持多种协议。而用户提到的“Curl库”可能指的是libcurl&#xff0c;这是一个客户端URL传输库&#xff0c;可以用在C、C等编程语言…

K8S学习之基础七十五:istio实现灰度发布

istio实现灰度发布 上传镜像到harbor 创建两个版本的pod vi deployment-v1.yaml apiVersion: apps/v1 kind: Deployment metadata:name: appv1labels:app: v1 spec:replicas: 1selector:matchLabels:app: v1apply: canarytemplate:metadata:labels:app: v1apply: canaryspec…

【设备连接涂鸦阿里云】

设备连接涂鸦阿里云 ■ Tuya IoT on Alibaba Cloud■ 控制台操作步骤■ 1. 创建产品■ 2. 添加设备■ 3. 添加设备■ 4. 获取设备MQTT连接参数 ■ MQTTX使用教程■ 1&#xff0c;先在 Tuya IoT on Alibaba Cloud 新建产品和设备■ 2&#xff0c;MQTTX 设置■ 3&#xff0c;MQTT…

c语言学习16——内存函数

内存函数 一、memcpy使用和模拟实现1.1参数1.2 使用1.3 模拟实现 二、memmove使用和模拟实现2.1 参数2.2 使用2.3 模拟实现 三、memset使用3.1 参数3.2 使用 四、memcmp使用4.1 参数4.2 使用 一、memcpy使用和模拟实现 1.1参数 因为内存中不知道存的是什么类型的地址&#xff…

渗透测试实战:使用Hydra破解MySQL弱口令(附合法授权流程+防御方案)

渗透测试实战&#xff1a;使用Hydra破解MySQL弱口令&#xff08;附合法授权流程防御方案&#xff09; 郑重声明&#xff1a;本文仅供安全学习研究&#xff0c;任何未经授权的网络攻击行为均属违法。实操需获得目标系统书面授权&#xff0c;请遵守《网络安全法》相关规定。 一、…

一文了解亿级数据检索:RedisSearch

文章目录 1.什么是Redis Search2.为什么要使用Redis Search3.RedisSearch 的核心特性4.RedisSearch 的原理4.1 倒排索引4.2 索引创建与数据存储4.3 数据模型4.4 搜索查询处理4.5 高性能与可扩展性&#xff1a; 5.有了ES为什么还需要RedisSearch5.RedisSearch的安装6.RedisSearc…

uniApp开发微信小程序-连接蓝牙连接打印机上岸!

历经波折三次成功上岸&#xff01; 三次经历简单絮叨一下&#xff1a;使用uniAppvue开发的微信小程序&#xff0c;使用蓝牙连接打印机&#xff0c;蓝牙所有的接口都是插件中封装的&#xff0c;用的插件市场中的这个&#xff1a; dothan-lpapi-ble &#xff1b;所以&#xff0c…

【特权FPGA】之按键消抖

完整代码如下所示&#xff1a; timescale 1ns / 1ps// Company: // Engineer: 特权 // // Create Date: // Design Name: // Module Name: // Project Name: // Target Device: // Tool versions: // Description: // // Dependencies: // // Revision: // …

P1331 洛谷 海战

题目描述 思路 这个题需要读懂题意&#xff0c;即“什么样的形式表示两只船相撞&#xff1f;” ----> 上下相邻或左右相邻 如果图是不和法的&#xff0c;一定存在如下结构&#xff1a; # # . # 或 # # # . 或 # . # # 或 . # # #即四个格子里有三个#&#xff0c;一个"…

网络安全·第二天·ARP协议安全分析

今天我们来考虑考虑计算机网络中的一类很重要的协议-------ARP协议&#xff0c;介绍他用途的同时&#xff0c;分析分析ARP协议存在的一些漏洞及其相关的协议问题。 一、物理地址与IP地址 1、举例 在计算机网络中&#xff0c;有两类地址十分关键&#xff0c;一类称为物理地址&a…

华为手机或平板与电脑实现文件共享

1.手机或平板与电脑在同一个网络 2.打开手机或平板端&#xff0c;设置---更多连接----快分享或华为分享打开此功能-----开启共享至电脑 3.打开电脑&#xff0c;网络中就可看到手机端分享的用户名称 4. 登陆就可访问手机 5.常见问题 5.1 电脑未发现本机 5.2 修改了访问密码后再…

幻兽帕鲁(Palworld)在线工具集:让游戏体验更轻松!

幻兽帕鲁(Palworld)在线工具集&#xff1a;让游戏体验更轻松&#xff01; &#x1f3ae; 工具介绍 为了帮助广大幻兽帕鲁玩家更好地享受游戏&#xff0c;我开发了这个全面的在线工具集。无需下载安装&#xff0c;打开网页即可使用&#xff0c;完全免费&#xff01; &#x1…

学习51单片机Day02---实验:点亮一个LED灯

目录 1.先看原理图 2.思考一下&#xff08;sbit的使用&#xff09;&#xff1a; 3.给0是要让这个LED亮&#xff08;LED端口设置为低电平&#xff09; 4.完成的代码 1.先看原理图 比如我们要让LED3亮起来&#xff0c;对应的是P2^2。 2.思考一下&#xff08;sbit的使用&…

如何使用通义灵码学习JavaScript和DOM

如果你看到了本手册的页面数量&#xff0c;你就会发现JavaScript的API真的非常丰富&#xff0c;在MDN上专门有一大分类用于介绍JavaScript的API&#xff0c;但软件工程行业有一个著名法则叫2-8法则&#xff0c;意思是只有20%的内容会经常使用到&#xff0c;而80%的内容只在一些…

基于labview的多功能数据采集系统

基于labview的多功能数据采集系统&#xff08;可定制功能&#xff09; 包含基于NI温度采集卡。电流采集卡。电压采集卡的数据采集功能 数据存储 报表存储 数据处理与分析 生产者消费者架构 有需要可联系

SpringMVC基础一(SpringMVC运行原理)

先了解MVC&#xff0c;在JavaWeb基础五中。 回忆servlet&#xff0c;在javaweb基础二中。 创建一个web项目&#xff1a; 1、新建maven项目&#xff0c;导入依赖。&#xff08;junit、springmvc、spring-webmvc、servlet-api、jsp-api、jstl&#xff09; <groupId>org…