C/C++数据类型转换详解

2023-11-01

目录

C语言数据类型转换

1、自动类型转换

(1)算术表达式的自动类型转换

(2)赋值运算中的自动类型转换

2、强制类型转换

C++数据类型转换

1、static_cast<>

2、const_cast<>

3、dynamic_cast<>

4、reinterpret_cast<>


C语言数据类型转换

1、自动类型转换

(1)算术表达式的自动类型转换

在实际运算中,整型(int,short,long,char)和浮点型(float,double)是可以混合运算的,例如下面这段代码就是合法的。

10+'a'+1.5-8765.1234*'b'

只是在算术表达式进行运算的过程中,不同类型的数据要自动转换成同一类型,然后进行运算。

算术表达式中自动类型转换的具体规则:

如果某个操作数的类型在上面这个列表中排名较低,那么首先要转换为另外一个操作数的类型后执行运算。

横向向左的箭头表示必定的转换,如char,short型数据在运算时必定先自动类型转换为int类型(char,short自动类型转换为int也称为整型提升),float型数据在运算时一律先自动类型转换为double类型以提高运算精度。

纵向的箭头表示当运算对象为不同类型时转换的方向,类型转换方向由低到高,如某类型数据与double型数据进行运算时,该类型数据自动类型转换为double类型,然后在两个double型数据间进行运算,结果为double型。如果表达式中,类型转换的最高级别是long,那么其它类型的数据就会自动类型转换为long类型。以此类推…

注:

  • 不要错误地将该图理解为int型先转换成unsigned int型,再转换成long型,再转成double型
  • 在算术表达式中进行自动类型转换的时候,原数据类型并无发生变化,实际上是得到一个自动类型转换后的中间变量,表达式运算的过程是对转换后的中间变量进行运算,运算结果为转换后的数据类型

如图所示b+c在进行运算的时候肯定会发生自动类型转换,但是我们最终算b的大小发现它依然是2字节,这就说明原数据类型并 没有发生变化,在运算过程中发生的自动类型转换实际上得到的是一个自动类型转换后的中间变量以参加运算,我们再输出b+c 的大小发现它是4字节,这就说明运算后的结果类型就是转换后的数据类型。

接下来我们来看下面的例子。

例1:

int i;
float f;
double d,result;
long e;
result = 10 + 'a' + i * f - d / e;

10+'a’在进行运算的时候,会先将’a’自动类型转换为int类型,即97,运算结果为107。i * f在进行运算的时候,会将i与f都转换成double类型,运算结果为double类型。整数107与i*f的积相加,先将整数107转换成double类型,最终运算结果为double类型。d/e在进行运算的时候,会将变量e转换为double类型,所以d/e的结果为double类型。所以最终的结果就是double类型。

例2:

int main()
{
	char c = 1;
	
	printf("%u\n", sizeof(c));
	printf("%u\n", sizeof(+c));
	printf("%u\n", sizeof(-c));
	printf("%u\n", sizeof(!c));
	
	return 0;
}

在该例中,c只要参与表达式运算,就会发生自动类型转换,表达式 +c ,就会使得变量c自动类型转换为int(整型提升),所以 sizeof(+c) 是4个字节,表达式 -c 也会自动类型转换为int(整型提升),所以 sizeof(-c) 是4个字节,但是 sizeof( c) 和sizeof(!c),就是1个字节。

例3:

一道经典面试题,下列代码的输出结果是什么:

#include <stdio.h>
int i;
int main()
{
    i--;
    if (i > sizeof(i))
    {
        printf(">\n");
    }
    else
    {
        printf("<\n");
    }
    return 0; 
}

A.>

B.<

C.不输出

D.程序有问题

解析:

C语言中,0为假,非0即为真。

全局变量,没有给初始值时,编译器会默认将其初始化为0。

i的初始值为0,i–结果-1,i为整形,sizeof(i)求i类型大小是4,按照此分析来看,结果应该选择B,但是sizeof的返回值类型实际为无符号整形,因此编译器会将左侧i自动转换为无符号整形的数据。

-1的原码:10000000000000000000000000000001

-1的反码:111111111111111111111111111111111110

-1的补码:111111111111111111111111111111111111

将-1转化为无符号整型,实际上就是将它的符号位视为有效位,故此时它是一个正数,正数的补码,反码,原码相同,故此时-1的原码就是11111111111111111111111111111111

所以-1对应的无符号整形是一个非常大的数字,超过4或者8,故实际应该选择A

有符号数与无符号数其实是一种看待内存中数据的角度,并不是绝对的,就比如我们把-1看作无符号数,那么他的符号位就会被视作为有效位

整型提升的原理:

无符号整形的整形提升,高位补0

有符号整型的整形提升是按照变量的符号位来提升的,即高位补符号位。

负数的整型提升如下:

char c1 = -1;
//-1是int类型的整数,存储在char类型的变量中会进行高位截断
//变量c1的二进制位(补码)中只有8个比特位:1111111
//因为char为有符号的char,所以整形提升的时候,高位补充符号位,即为1
//提升之后的结果是:11111111111111111111111111111111

正数的整型提升如下:

char c2 = 1;
//变量c2的二进制位(补码)中只有8个比特位:00000001
//因为 char 为有符号的 char,所以整形提升的时候,高位补充符号位,即为0
//提升之后的结果是:00000000000000000000000000000001

(2)赋值运算中的自动类型转换

C语言规定:如果赋值运算符两侧的类型不一致,但都是数值型或字符型时,在赋值时会进行自动类型转换。

赋值运算中自动类型转换的规则:

  • 将浮点型数据赋值给整型变量时,舍弃浮点数的小数部分。如这段代码:int i = 8.24;i的值为8,在内存中以整数形式存储
  • 将整型赋值给浮点型变量时,数值不变,但以浮点数的形式存储到变量中,如float f = 35;,先将35转换成35.00000,再存储到f中。double d = 35;,则将35转换成35.00000000000000,然后以双精度浮点数形式存储到d中。
  • 将一个double型数据赋值给float类型变量时,截取前面7位有效数字,存放到float变量的存储单元(32位)中,但是应注意数值范围不能溢出
  • 将一个float类型数据赋值给double类型变量时,数值不变,有效位数扩展到16位,在内存中以64位存储。
  • 将字符型数据赋值给其它整型变量时,由于字符只有1个字节,而其它整型变量至少为2个字节,因此将字符数据放到整型变量的低八位中,将其他整形变量赋值给char型变量时,只将其低8位原封不动地送到char型变量(高位截断)。

例1:

int i = 289;
char c = 'a';
c = i;
//以%d形式输出,c的值为33,以%c形式输出,得到字符’!'(其ASCII码值为33)

例2:

int main()
{
  unsigned char a = 200;
  unsigned char b = 100;
  unsigned char c = 0;
  c = a + b;
  printf("%d\n", c);
  return 0;
}

c=a+b这句代码在运算的过程中,a和b会发生整型提升,但是在存储的时候,变量c还一个char类型的变量,所以存不下去整型提升后的结果,所以会发生高位截断。

例3:

int main()
{
	char a = 0xb6;
	short b = 0xb600;
	int c = 0xb6000000;
	
	if(a==0xb6)
		printf("a");
	if(b==0xb600)
		printf("b");
	if(c==0xb6000000)
		printf("c");
	
	return 0;
}

0xb6,0xb600是int类型的整数分别存储在char类型和short类型的变量,所以发生了截断,故变量a和变量b的值实际上并不分别等于0xb6,0xb600,所以最终输出c。

2、强制类型转换

自动类型转换常被称为隐式类型转换,强制类型转换常被称为显示类型转换,强制类型转换的一般形式为(类型标识符)(操作数)或(类型标识符)(表达式)。其功能就是把操作数或表达式结果的数据类型暂时地强制转换为圆括号()中的类型。

类型标识符两边的圆括号就是C语言中的强制类型转换符。

在进行强制类型转换时,原来变量的类型未发生变化,我们所得到的是一个所转类型的中间变量。 如:

int main()
{
	float x;
	int i;
	x = 3.6;
	i = (int)x;
	printf("x = %f,i = %d", x, i);
	
	return 0;
}

在进行强制类型转换后得到的是一个int型的中间变量,它的值等于x的整数部分,而x的类型不变,仍为float型,x的值也不变。

输出结果如下:

在了解了强制类型转换后,我们简单地看一下强制类型转换的例子:

int main()
{
	int a = 20000000, b = 30000000, x;
	x = a * b;
	printf("%d*%d = %d", a, b, x);
	
	return 0;
}

输出结果如下:

这样的输出结果显然是错误的,因为正确的结果显然超过了int的范围,发生了溢出错误,截去了高位部分,那如果我们把程序改为下面这样呢?

int main()
{
	long long x;
	int a = 20000000, b = 30000000;
	x = a * b;

	printf("%d*%d = %d", a, b, x);
	
	return 0;
}

运行结果依然为:

因为a和b就是int型,a*b也是int型,这时已经产生了溢出错误,所以只是把高位截断后的值赋值给了long long型的变量,然后将左边高位补零,数值大小不变。

要得到正确的结果,可以用强制类型转换把程序改为

int main()
{
	long long x;
	int a = 20000000, b = 30000000;
	x = (long long)a * b;

	printf("%d*%d = %lld", a, b, x);
	
	return 0;
}

这样就能得到最终的正确结果:

C++数据类型转换

C++兼容C语言的类型转换风格,同时C++提供了自己的类型转换操作符,一共有四种,如下代码所示:

static_cast<new_type>(expression);
const_cast<new_type>(expression);
dynamic_cast<new_type>(expression);
reinterpret_cast<new_type>(expression);

下面就对这四种类型转换操作符进行总结:

1、static_cast<>

static_cast<>是最常用的类型转换操作符,它主要执行非多态的转换,用于代替C语言中通常的转换操作,但同时它还可以用于类层次结构中基类和子类之间指针或引用的转换,在进行上行转换(把子类指针或引用转换成基类类型)是安全的,但是在进行下行转换(把基类指针或引用转换成子类类型)时,由于没有动态类型检查,是不安全的。

根据代码进行分析,首先是将double类型的变量a转化为int类型,但是根据输出结果发现,变量a依然是double类型,所以和C语言数据类型转换一样,只是创建了一个转换类型后的临时变量,然后将临时变量的值赋值给指定变量,被转换的原变量本身类型不变。然后下面也进行了上行转换和下行转换,只是下行转换是没有动态类型检查的,是不安全的。

2、const_cast<>

const_cast<>在进行类型转换时用来删除类型的const或volatile属性,除了const或volatile修饰之外,原来的数据值和数据类型都是不变的,const_cast<>中的数据类型必须是指针或者引用。

首先将const指针p转换为非const指针然后赋值给指针变量p2,转换成功,因为如果没有将const指针p转换为非const指针,那么就是将const指针赋值给非const指针p2,这是权限的放大,编译器会报错,后面通过指针修改p所指变量的值,编译器报错,说明指针p依然是const的,只是创建了一个转换后的非const的临时变量,然后将这个非const临时变量赋值给p2,被转换的原变量本身的const和volatile属性是不变的。

3、dynamic_cast<>

该操作符用于运行时检查类型转换是否安全,但只在多态类型时合法,即该类型至少具有一个虚函数。它与static_cast具有相同的语法,但dynamic_cast主要用于类层次间的上行和下行转换,以及类之间的交叉转换。在类层次间进行上行转换时,它和static_cast效果是一样的,在进行下行转换时,它具有类型检查的功能,比static_cast更安全,dynamic_cast会先检查是否能转换成功,能成功则转换,不能则返回0。

4、reinterpret_cast<>

该操作符将一个类型(如int类型)的数据a转换为另一个类型(如double类型)的数据b时仅仅是将a的比特位复制给b,不作数据转换,也不进行类型检查。而且reinterpret_cast要转换的new_type类型必须是指针类型、引用或算术类型,例如下面的代码:

char c = 'a';
int d = reinterpret_cast<int&>(c);

这个转换只是单纯的将字符c中的比特位复制给了d,并没有进行必要的分析和类型等的检查。这种转换方式一般较少使用。

本篇文章部分内容参考书籍《C语言程序设计》(刘天印 冯运仿 主编)和博客链接 https://book.itheima.net/course/223/1275663370879508481/1275663662815649798

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C/C++数据类型转换详解 的相关文章

  • 如何在 C# 中定义文本框数组?

    您好 当我在 Windows 申请表上创建文本框时 我无法将其命名为 box 0 box 1 等 我这样做的目的是因为我想循环使用它们 其实我发现TextBox array firstTextBox secondTextBox 也有效
  • 关于在 Windows 上使用 WiFi Direct Api?

    我目前正在开发一个应用程序 我需要在其中创建链接 阅读 无线网络连接 在桌面应用程序 在 Windows 10 上 和平板电脑 Android 但无关紧要 之间 工作流程 按钮 gt 如果需要提升权限 gt 创建类似托管网络的 WiFi 网
  • 如何在 Linq 中获得左外连接?

    我的数据库中有两个表 如下所示 顾客 C ID city 1 Dhaka 2 New york 3 London 个人信息 P ID C ID Field value 1 1 First Name Nasir 2 1 Last Name U
  • java中日期转换dd-MMM-yyyy到dd-MM-yyyy

    在Java中将23 Mar 2011转换为23 03 2011的最简单方法是什么 感谢大家 这似乎解决了这个问题 try Calendar cal Calendar getInstance cal setTime new SimpleDat
  • 未定义的行为或误报

    我 基本上 在野外遇到过以下情况 x x 5 显然 它可以在早期版本的 gcc 下编译干净 在 gcc 4 5 1 下生成警告 据我所知 警告是由 Wsequence point 生成的 所以我的问题是 这是否违反了标准中关于在序列点之间操
  • Java 不可变对象 [关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 我正在学习不变性的概念 据我了解 一旦创建对象 不可变对象就无法更改其值 但我不明白不可变对象的以下用途 They are 自动是线程
  • 在一个字节中存储 4 个不同的值

    我有一个任务要做 但我不知道从哪里开始 我不期待也绝对不想要代码中的答案 我想要一些关于该怎么做的指导 因为我感到有点失落 将变量打包和解包到一个字节中 您需要在一个字节中存储 4 个不同的值 这些值为 NAME RANGE BITS en
  • 如何在Java中模拟引用传递?

    我是一个十足的 Java 菜鸟 我知道 Java 将所有参数视为按值传递 并且还有其他几个线程人们对此进行了解释 例如 在 C 中我可以这样做 void makeAThree int n n 3 int main int myInt 4 m
  • 如何编写一个同时需要请求和响应Dtos的ServiceStack插件

    我需要提供本地化数据服务 所有本地化的响应 Dto 都共享相同的属性 IE 我定义了一个接口 ILocalizedDto 来标记那些 Dto 在请求端 有一个ILocalizedRequest对于需要本地化的请求 Using IPlugin
  • std::async 与重载函数

    可能的重复 std bind 重载解析 https stackoverflow com questions 4159487 stdbind overload resolution 考虑以下 C 示例 class A public int f
  • while 之后无法访问的语句[重复]

    这个问题在这里已经有答案了 我只是修改代码 在以下代码中出现错误 int x 1 System out println x x while true x System out println x x 错误在最后一行 我可以知道错误 错误 无
  • ASTParser:解析绑定后查找声明节点

    我创建了一个启用了绑定的 AST 当我稍后解析绑定时 我得到了一个有效的 ITypeBinding 但是 当我想要获取绑定的声明 Node 时 它 总是返回 null 除非 ITypeBinding 在 sourceFile 中声明 这是我
  • 如何使 JScrollPane 与嵌套 JPanel 一起正常工作?

    我正在使用 NetBeans 在 Java 中构建 Swing 应用程序 但我遇到布局问题 我的主框架包含一个JScrollPane其中包含一个JPanel called contentPanel其中又包含一个JPanel called l
  • 如何在 Log4j2 - JSON 布局中自定义或删除默认属性

    In Spring Boot 2我已配置的应用程序Log4j2 with JsonLayout像下面这样
  • (de)从 CSV 序列化为对象(或者最好是类型对象的列表)

    我是一名 C 程序员 试图学习 C 似乎有一些内置的对象序列化 但我在这里有点不知所措 我被要求将测试数据从 CSV 文件加载到对象集合中 CSV 比 xml 更受青睐 因为它更简单且更易于人类阅读 我们正在创建测试数据来运行单元测试 该集
  • Lucene/Hibernate 搜索锁定异常

    我使用 Hibernate Search 在 Web 应用程序上索引和全文搜索项目 没有问题 来自我的 pom xml
  • Process.Start() 方法在什么情况下返回 false?

    From MSDN https msdn microsoft com en us library e8zac0ca v vs 110 aspx 返回值 true 表示有新的进程资源 开始了 如果由 FileName 成员指定的进程资源 St
  • 线程和 fork()。我该如何处理呢? [复制]

    这个问题在这里已经有答案了 可能的重复 多线程程序中的fork https stackoverflow com questions 1235516 fork in multi threaded program 如果我有一个使用 fork 的
  • AndroidAnnotations 和 Dagger

    我正在尝试使用 Dagger 注入 Android 带注释的 Activity java lang IllegalArgumentException No inject registered for members com app serv
  • Java 可变 BigInteger 类

    我正在使用 BigIntegers 进行计算 该计算使用一个调用 multiply 大约 1000 亿次的循环 并且从 BigInteger 创建新对象使其非常慢 我希望有人编写或找到了 MutableBigInteger 类 我在 jav

随机推荐

  • toFixed精度丢失问题

    bug说明 10 3950 3935 00 用toFixed 2 得到的是40904 32 实际应该是40904 33 解决的方法 第一种 在main js中直接复制下面代码即可 Number prototype toFixed funct
  • 【9秒原创】cocos2d-x横版rts手游《口袋仙侠》alpha1.0正式开源

    9秒原创 Firefly cocos2d x的横版rts手机网游 口袋仙侠 alpha V1 0 商用版本 完整源码下载 特别声明 1 口袋仙侠 项目基于MIT协议 9秒社团团队允许任何厂商及个人对其进行修改和商用 并将会在本板块内进行技术
  • Linux NetworkManager网络服务详解

    一 网络配置文件 Linux 为 配 置 网 络 提 供 了 许 多 工 具 其 中 有 图 形 界 面 的 如 NetworkManager 也有伪图形界面 如 system config network 的 虽然使用这些工具来配置网络会
  • iSH使用与优化全网整合教程【持续更新】【精华】

    最后一次更新 2023 4 22 请勿利用文章内的相关技术从事非法测试 由于传播 利用此文所提供的信息而造成的任何直接或者间接的后果及损失 均由使用者本人负责 作者不为此承担任何责任 iSH介绍与换源 已安装并已完成换源的用户可直接跳过 介
  • Deep Java Library(六)DJLServing自定义模型,自定义Translator注意事项

    DJLServing自定义模型中自定义Translator注意事项需要仔细读一下DJLServing源码中的ServingTranslatorFactory类 一开始不了解以为DJLServing选择Translator像玄学 后来看了像迷
  • C语言常见笔试题——strcpy函数的实现

    转载地址 http blog csdn net gpengtao article details 7464061 大家一般认为名不见经传strcpy函数实现不是很难 流行的strcpy函数写法是 cpp view plain copy ch
  • Java电子招投标采购系统源码-适合于招标代理、政府采购、企业采购、等业务的企业

    功能描述 1 门户管理 所有用户可在门户页面查看所有的公告信息及相关的通知信息 主要板块包含 招标公告 非招标公告 系统通知 政策法规 2 立项管理 企业用户可对需要采购的项目进行立项申请 并提交审批 查看所有的立项信息 主要功能包含 招标
  • 用java做一个登录界面

    这篇文章教大家做一个简单的登录界面 方法如下 1 界面由三个部分组成 可视化部分 窗体 按钮 输入框 标签 元素规则部分 尺寸 颜色 字体 布局管理器 内容部分 字符串 图片 2 界面开发包 1 java awt Abstract Wind
  • C++基础之注释

    文章目录 前言 一 注释的语法 二 注意 三 优美的注释 四 总结 前言 注释在程序编写中很重要 一个良好的注释在编写注释时更重要 一 注释的语法 注释有两种风格的语法 c风格或者说 多行 注释 只是一个c风格的注释 或者说是多行注释 c
  • pytest 之skip 跳过某条测试用例执行

    前言 相信大家对于pytest 框架官方介绍的那几种跳过用例执行的方法都熟悉并且能够运用在自己的项目中了 但是设想有下面一种场景 使用pytest 框架编写了一个接口的自动化测试 请求体使用了 pytest mark parametrize
  • idea社区版已经足够强大了

    idea使用企业版还是社区版 社区版不支持的功能 Profiling tools JVM性能分析工具 类似的工具有很多 Spring 微服务开发时没有Servers标签 yaml配置文件不能提前校验 除此以外没啥感觉 JavaEE Micr
  • stm32初学笔记(一)真·入门笔记

    最近在学习stm32 看的是野火的 b站就有视频 此博客记录我在学习中的重点与困惑 笔者不是第一次学习嵌入式 之前学过51 知道嵌入式的门槛 此文章旨在解决真正零基础的人的疑问 所以写的很详细 每一个视频我都看了两遍以上 尽可能的列举了我在
  • 外观检验人员一致性(Kappa)分析

    系列文章目录 文章目录 系列文章目录 前言 一 目的 二 分析方法 三 判定方法 四 评价流程 1 实验设计及实施 五 结果分析 分析一 检验员自身一致性 重复性 分析二 每个检验员与标准之间一致性 分析三 检验员之间 再现性 分析四 所有
  • 航班订票功能的简要实现

    实现航班订票功能与WebService实现身份证验证 一 系统模块分析 a 普通用户 b 管理员 二 UML建模示例 1 航班管理系统UML类图表示 2 航班管理系统UML用例图表示 3 航班管理系统UML时序图表示 三 设计模式分析 1
  • Android 11 「外部存储」权限适配方案——权限申请框架推荐

    文章目录 1 权限种类 1 1权限种类区分 普通权限 危险权限 特殊权限 1 2存储权限 变化 2 外部存储和内部存储对比 2 1外部存储在手机哪个位置 2 2外部存储和内部存储的访问权限区别 3 外部存储适配方案 3 1 Android
  • 子矩阵和求激光炸弹

    题目描述 地图上有 N 个目标 用整数 Xi Yi表示目标在地图上的位置 每个目标都有一个价值 Wi 注意 不同目标可能在同一位置 现在有一种新型的激光炸弹 可以摧毁一个包含 R R 个位置的正方形内的所有目标 激光炸弹的投放是通过卫星定位
  • linux内核SPI总线驱动分析(二)

    origin http www cnblogs com liugf05 archive 2012 12 03 2800459 html SPI驱动编写 简而言之 SPI驱动的编写分为 1 spi device就构建并注册 在板文件中添加sp
  • R语言 第四章 初级绘图(4)axis函数,设置坐标轴,text()修文本属性,axes,线条样式,修改点的符号与线条

    关注公众号凡花花的小窝 收获更多的考研计算机专业编程相关的资料 修改点符号与线条 points 函数可以在画布中添加点 使用格式 points x y pch cex bg 其中 x y确定点的位置 可以设置的参数包括 lwd 点边框的宽度
  • 剑指offer 学习笔记 字符串的排列

    面试题38 字符串的排列 输入一个字符串 打印出该字符串中字符的所有排列 如输入abc 则输出六个不同的全排列 我们可以把求排列的过程分为两步 第一步求可能出现在第一个位置的字符 即把第一个字符和后面所有的字符交换 第二步固定一个字符 求后
  • C/C++数据类型转换详解

    目录 C语言数据类型转换 1 自动类型转换 1 算术表达式的自动类型转换 2 赋值运算中的自动类型转换 2 强制类型转换 C 数据类型转换 1 static cast lt gt 2 const cast lt gt 3 dynamic c