C语言类型转换-自动类型转换、强制类型转换、指针类型转换

2023-05-16

数据类型转换就是将数据(变量、数值、表达式的结果等)从一种类型转换为另一种类型。

自动类型转换

自动类型转换就是编译器默默地、隐式地、偷偷地进行的数据类型转换,这种转换不需要程序员干预,会自动发生。

  1. 将一种类型的数据赋值给另外一种类型的变量时就会发生自动类型转换,例如:

float f = 100;

100 是 int 类型的数据,需要先转换为 float 类型才能赋值给变量 f。再如:

int n = f;

f 是 float 类型的数据,需要先转换为 int 类型才能赋值给变量 n。

在赋值运算中,赋值号两边的数据类型不同时,需要把右边表达式的类型转换为左边变量的类型,这可能会导致数据失真,或者精度降低;所以说,自动类型转换并不一定是安全的。对于不安全的类型转换,编译器一般会给出警告。

  1. 在不同类型的混合运算中,编译器也会自动地转换数据类型,将参与运算的所有数据先转换为同一种类型,然后再进行计算。转换的规则如下:
  • 转换按数据长度增加的方向进行,以保证数值不失真,或者精度不降低。例如,int 和 long 参与运算时,先把 int 类型的数据转成 long 类型后再进行运算。
  • 所有的浮点运算都是以双精度进行的,即使运算中只有 float 类型,也要先转换为 double 类型,才能进行运算。
  • char 和 short 参与运算时,必须先转换成 int 类型。

下图对这种转换规则进行了更加形象地描述:
img

unsigned 也即 unsigned int,此时可以省略 int,只写 unsigned。

自动类型转换示例:

#include<stdio.h>
int main(){
    float PI = 3.14159;
    int s1, r = 5;
    double s2;
    s1 = r * r * PI;
    s2 = r * r * PI;
    printf("s1=%d, s2=%f\n", s1, s2);
    return 0;
}

运行结果:
s1=78, s2=78.539749

在计算表达式r*r*PI时,r 和 PI 都被转换成 double 类型,表达式的结果也是 double 类型。但由于 s1 为整型,所以赋值运算的结果仍为整型,舍去了小数部分,导致数据失真。

强制类型转换

自动类型转换是编译器根据代码的上下文环境自行判断的结果,有时候并不是那么“智能”,不能满足所有的需求。如果需要,程序员也可以自己在代码中明确地提出要进行类型转换,这称为强制类型转换。

自动类型转换是编译器默默地、隐式地进行的一种类型转换,不需要在代码中体现出来;强制类型转换是程序员明确提出的、需要通过特定格式的代码来指明的一种类型转换。换句话说,自动类型转换不需要程序员干预,强制类型转换必须有程序员干预。

强制类型转换的格式为:

(type_name) expression

type_name为新类型名称,expression为表达式。例如:

(float) a;  //将变量 a 转换为 float 类型
(int)(x+y);  //把表达式 x+y 的结果转换为 int 整型
(float) 100;  //将数值 100(默认为int类型)转换为 float 类型

下面是一个需要强制类型转换的经典例子:

#include <stdio.h>
int main(){
    int sum = 103;  //总数
    int count = 7;  //数目
    double average;  //平均数
    average = (double) sum / count;
    printf("Average is %lf!\n", average);
    return 0;
}

运行结果:
Average is 14.714286!

sum 和 count 都是 int 类型,如果不进行干预,那么sum / count的运算结果也是 int 类型,小数部分将被丢弃;虽然是 average 是 double 类型,可以接收小数部分,但是心有余力不足,小数部分提前就被“阉割”了,它只能接收到整数部分,这就导致除法运算的结果严重失真。

既然 average 是 double 类型,为何不充分利用,尽量提高运算结果的精度呢?为了达到这个目标,我们只要将 sum 或者 count 其中之一转换为 double 类型即可。上面的代码中,我们将 sum 强制转换为 double 类型,这样sum / count的结果也将变成 double 类型,就可以保留小数部分了,average 接收到的值也会更加精确。

在这段代码中,有两点需要注意:

  • 对于除法运算,如果除数和被除数都是整数,那么运算结果也是整数,小数部分将被直接丢弃;如果除数和被除数其中有一个是小数,那么运算结果也是小数。这一点已在《C语言加减乘除运算》中进行了详细说明。
  • ( )的优先级高于/,对于表达式(double) sum / count,会先执行(double) sum,将 sum 转换为 double 类型,然后再进行除法运算,这样运算结果也是 double 类型,能够保留小数部分。注意不要写作(double) (sum / count),这样写运算结果将是 3.000000,仍然不能保留小数部分。

类型转换只是临时性的

无论是自动类型转换还是强制类型转换,都只是为了本次运算而进行的临时性转换,转换的结果也会保存到临时的内存空间,不会改变数据本来的类型或者值。请看下面的例子:

#include <stdio.h>
int main(){
    double total = 400.8;  //总价
    int count = 5;  //数目
    double unit;  //单价
    int total_int = (int)total;
    unit = total / count;
    printf("total=%lf, total_int=%d, unit=%lf\n", total, total_int, unit);
    return 0;
}

运行结果:
total=400.800000, total_int=400, unit=80.160000

注意看第 6 行代码,total 变量被转换成了 int 类型才赋值给 total_int 变量,而这种转换并未影响 total 变量本身的类型和值。如果 total 的值变了,那么 total 的输出结果将变为 400.000000;如果 total 的类型变了,那么 unit 的输出结果将变为 80.000000。

自动类型转换 VS 强制类型转换

在C语言中,有些类型既可以自动转换,也可以强制转换,例如 int 到 double,float 到 int 等;而有些类型只能强制转换,不能自动转换,例如以后将要学到的 void * 到 int *,int 到 char * 等。

可以自动转换的类型一定能够强制转换,但是,需要强制转换的类型不一定能够自动转换。现在我们学到的数据类型,既可以自动转换,又可以强制转换,以后我们还会学到一些只能强制转换而不能自动转换的类型。

可以自动进行的类型转换一般风险较低,不会对程序带来严重的后果,例如,int 到 double 没有什么缺点,float 到 int 顶多是数值失真。只能强制进行的类型转换一般风险较高,或者行为匪夷所思,例如,char * 到 int * 就是很奇怪的一种转换,这会导致取得的值也很奇怪,再如,int 到 char * 就是风险极高的一种转换,一般会导致程序崩溃。

使用强制类型转换时,程序员自己要意识到潜在的风险。

指针类型之间的转换

指向值的一个类型的指针可以转换为指向另一类型的指针。 但是,由于对齐需求和存储中不同类型的大小,结果可能是未定义的。 指向对象的指针可转换为指向其类型要求小于或等于严格存储对齐的对象的指针,然后再次返回而不做更改。

指向 void 的指针可转换为/自指向任何类型的指针,且不受限制或不丢失信息。 如果结果转换回原始类型,则将恢复原始指针。

如果指针转换为另一个类型相同但具有不同的或其它限定符的指针,则新指针与旧指针相同(新限定符强加的限制除外)。

指针值也可以转换为整数值。 根据以下规则,转换路径取决于指针的大小和整型的大小:

  • 如果指针的大小大于或等于整型的大小,则指针的行为类似于转换中的无符号值,除非它无法转换为浮点值。
  • 如果指针小于整型,则指针首先转换为与整型大小相同的指针,然后转换为整型。

相反,整型可以基于以下规则转换为指针类型:

  • 如果整型与指针类型的大小相同,则转换只会促使整数值被视为指针(无符号整数)。
  • 如果整型类型的大小与指针类型的大小不同,则使用表从带符号整型类型转换和从无符号整型类型转换中给定的转换路径,首先将整型转换为指针的大小。 然后将其视为一个指针值。

值为 0 的整型常量表达式或强制转换为类型 void* 的此类表达式可以通过类型强制转换、赋值或与任何类型的指针进行比较来进行转换。 这将产生与同一类型的另一个 null 指针相等的 null 指针,但此 null 指针与指向函数或对象的任何指针不相等。 常数 0 以外的整数可以转换为指针类型,但结果是不可移植的。

指针的本质是变量,指针就是指针变量。
一个指针涉及两个变量:一个是指针变量自己本身,一个是指针变量指向的那个变量。
int *p; :定义指针变量时,p是int *类型,*p(p指向的那个变量)是int类型的。int *说白了就是指针类型,只要是指针类型都是占4个字节,解析方式都是按照地址方式来解释(意思是里面存的23个二进制加起来表示一个内存地址)的。

结论:所有指针类型(int *,double *,char *…)解析方式是相同的,都是地址。

对于指针所指向的那个变量来说,指针类型就很重要。指针指向的变量类型要取决于指针类型。

指针类型主要是为了其指向的变量所标记的。

#include <stdio.h>
int main(void)
{
        int a=5float *p;
        p=&a;
        printf("*p=%f.\n",*p);
}
#include <stdio.h>
int main(void)
{
        int a=5int *p1=&a;
        float *p;
        p=(float *)p1;    //强制类型转换
        printf("*p=%f.\n",*p);
}

指针的强制类型转换是有风险的

#include <stdio.h>
int main(void)
{
        int a=5char *p1=&a;
        printf("*p1=%d.\n",*p1);
        short *p2=&a;
        printf("*p2=%d.\n",*p2);

int和char类型都是整形,是兼容的,强制类型转换时有时候对有时候出错。int有两个字节char只有一个,int能表示的范围比int大,超过范围后int朝char转会出错。char往int就就不会出错(127)。short也有两个字节范围比char大还是比int小(65535)。

#include <stdio.h>
int main(void)
{
        int a[2]={0x11223344,ox55667788,0};
        int *p1=a;
        char *p2=(char *)a;
        printf("*p1=%x.\n",*p1);
        printf("*p2=%x.\n",*p2);
        printf("*p2=%x.\n",*(p2+1));
        printf("*p2=%x.\n",*(p2+2));
}

链接:https://www.jianshu.com/p/458928c7ec35

指针类型转换的应用

我们知道,所有指针都是指向一块内存的一个字节,然后根据指针的类型来对这个字节及其后面的字节进行解析。

因此我们可以利用这个特性来完成一些很方便的操作。

一、将结构体指针转换成数组指针,方便整体赋值操作

#include <string.h>
typedef struct 
{
    int a;
    int b;
    float c;
}ST;

int main(void) {
    ST st;
    memset((unsigned char*)&st, 0, sizeof(ST));
}

这样的好处是我们可以不用对结构体中的元素一个一个初始化,我们可以方便的操作结构体占用的内存。使用这种方法需要注意结构体中的内存对齐问题。

二、串口接收数据转换

问题背景可以参考这篇文章:https://mp.weixin.qq.com/s/cA4e9gvew5LYwZoVtdXZQw

三、函数、函数指针的参数和返回值

将函数参数设置成void *,可以让函数接收的数据类型更加灵活。

#include <stdio.h>
#define MSG_DRIVER_SIZE (10)
#define TRUE 1
#define FALSE 0

typedef struct msg_node{
	void *parm;
	void (*handler)(void *parm); 
}msg_node_t;  /* 消息数据结构 */

typedef struct msg_driver{
	unsigned int in;               //写入的位置
	unsigned int out;              //读出的位置
	msg_node_t *buf[MSG_DRIVER_SIZE];    
}msg_driver_t;

bool publish_msg(msg_driver_t *msg_buf, msg_node_t *msg)
{
	msg_buf->buf[msg_buf->in] = msg;
	msg_buf->in = (++msg_buf->in) % MSG_DRIVER_SIZE;    //防止越界
	
	return TRUE;
}

static msg_node_t *get_messge(msg_driver_t *msg_buf)
{
	msg_node_t *msg = NULL;
	
	msg = msg_buf->buf[msg_buf->out];
	msg_buf->out = (++msg_buf->out) % MSG_DRIVER_SIZE;    //防止越界
	
	return msg;
} 

void message_driver_handle(msg_driver_t *msg_buf)
{
	msg_node_t *msg;
	while( (msg = get_messge(msg_buf)) != NULL )
	{
		if (msg->handler != NULL)
			msg->handler(msg->parm);
	}
}

msg_driver_t msg_driver;

static void msg1_handle(void *parm)
{
	printf("gets msg1\r\n");
}

static void msg2_handle(void *parm)
{
	printf("get msg2\r\n");
}

static void msg3_handle(void *parm)
{
	printf("do msg3\r\n");
}

msg_node_t msg1 = {
	.parm = (void *)"I love u",
	.handler = msg1_handle
};

msg_node_t msg2 = {
	.parm = (void *)"I hate u",
	.handler = msg2_handle
};

msg_node_t msg3 = {
	.parm = NULL,
	.handler = msg3_handle
};

int main(void)
{
	publish_msg(&msg_driver, &msg1);
	publish_msg(&msg_driver, &msg2);
	publish_msg(&msg_driver, &msg3);
	
	while(1)
	{
		message_driver_handle(&msg_driver);
	}
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C语言类型转换-自动类型转换、强制类型转换、指针类型转换 的相关文章

  • 电子工程师是怎样的成长之路?

    转载于无际 xff1a https blog csdn net weixin 43982452 article details 121535177 spm 61 1001 2014 3001 5502 10年前 xff0c 我就是通过智能小
  • 单片机怎么做定时器矩阵,彻底解决各种定时问题?

    转载于 xff1a https blog csdn net weixin 43982452 article details 120555258 spm 61 1001 2014 3001 5502 大家好 xff0c 我是无际 定时功能非常
  • 为什么我学51单片机很顺利,学STM32却一头雾水?

    转载于 xff1a https blog csdn net weixin 43982452 article details 120515134 spm 61 1001 2014 3001 5502 五年懂行 xff0c 十年称王 当初自学转
  • 物联网专业真的把人坑惨了?浅谈物联网的未来发展趋势和未来方向

    转载于 xff1a https blog csdn net weixin 43982452 article details 120200879 spm 61 1001 2014 3001 5502 大家好 xff0c 我是无际 从事10年单
  • STM32单片机跑RTOS会比裸机有优势吗?

    转载于无际 xff1a https blog csdn net weixin 43982452 article details 115139030 spm 61 1001 2014 3001 5502 在工作中总是能碰到通过秀技术来满足虚荣
  • 如何快速学会别人的代码和思维

    转载于 xff1a https blog csdn net weixin 43982452 article details 120700863 spm 61 1001 2014 3001 5502 大家好 xff0c 我是无际 也有很多天没
  • 单片机和嵌入式哪个好?单片机会被嵌入式取代吗?

    转载于 xff1a https blog csdn net weixin 43982452 article details 120062206 spm 61 1001 2014 3001 5502 很多初学者都搞不清楚单片机和嵌入式的区别
  • 怎么看懂别人写的单片机项目代码?

    转载于 xff1a https blog csdn net weixin 43982452 article details 120049443 spm 61 1001 2014 3001 5502 记得刚开始接触代码的时候 xff0c 总觉
  • 嵌入式单片机产品开发设计框架

    转载于 xff1a https blog csdn net weixin 43982452 article details 119616145 spm 61 1001 2014 3001 5502 老板突然要给你一个新的需求 xff0c 要
  • 8大话题,解惑企业数字化

    从IT到DT xff0c 从信息化到数字化 xff0c 这个观念已经毋庸置疑 xff0c 但是 xff0c 这条路却缺少参照 xff0c 暗夜前行 xff0c 全靠摸索 关于数字化 xff0c 肯定不是上马一些OA工具 购买一些营销工具就行
  • ARINC 429总线学习资料?

    Hello xff0c 我是小熊coder xff0c 方向是嵌入式AI xff0c 后端开发 我的主页 xff1a Home xff0c 欢迎互相关注 xff0c 互相学习 最近在网上寻找关于ARINC 429总线的资料时 xff0c 发
  • 周期任务框架在裸机、RTOS上的实现

    周期任务框架在裸机 RTOS上的实现 一 任务的类型 运行的程序 xff0c 有响应指令的触发式程序 xff0c 也有一直运行的守护程序 xff0c 周期程序 贴别是在单片机 嵌入式领域 xff0c 大部分程序都是周期性的执行 xff0c
  • CAN通讯实验

    前面我们讲解了CAN总线的一些基础知识 xff0c 文章链接 xff1a 一口气从零读懂CAN总线以及应用 了解完之后 xff0c 我们也需要来用一用CAN总线 这篇文章就是主要讲解在STM32中怎么使用CAN总线
  • 航空机载总线网络概述

    1 机载总线网络概述 现代战斗机的航空电子系统是航空电子技术经历了半个多世纪的漫长演变和不断进步的结果 航空电子系统结构的每次变化 xff0c 其核心的机载总线网络技术也不断跨上新台阶 xff0c 而且每次变革都能使飞机性能得到大幅提升 现
  • 【C/C++开源库】单片机/嵌入式中的C语言日志库

    日志系统在系统开发和调整过程中的重要性 xff0c 大家应该都清楚 xff0c 特别是项目出问题之后 xff0c 却没有日志可以帮忙定位问题 xff0c 就非常令人痛苦 因为我们不可能一直通过调试器去单步调试程序 xff0c 所以设备的运行
  • 稚晖君软件硬件开发环境总结

    0 引言 这两天在bilibili上发现一个宝藏up主 xff0c 稚晖君 啧啧啧 xff0c 很厉害 虽然年龄不大 xff0c 但是真全栈 xff0c 从产品到机械到电路到软件 xff0c 这就是那种真的聪明 xff0c 一学就会的高智商
  • 一文弄清51、STM32、Linux点灯的区别

    嵌入式初学者入门的第一个 项目 就是LED点灯 xff0c 那么 xff0c 本文带你看看51 STM32 Linux点灯有什么区别 xff1f 51点灯 51点灯 xff0c 是很多单片机初学者的首选 xff0c 难度也是相对比较低的 准
  • Keil 模拟器 STM32F103 上手指南

    一般嵌入式操作系统因为它的特殊性 xff0c 往往和硬件平台密切相关连 xff0c 具体的嵌入式操作系统往往只能在特定的硬件上运行 对于刚接触 RT Thread 操作系统的读者并不容易马上就获得一个和 RT Thread 操作系统相配套的
  • 用PyOD工具库进行「异常检测」

    转载于知乎 xff1a 微调 异常检测 xff08 又称outlier detection anomaly detection xff0c 离群值检测 xff09 是一种重要的数据挖掘方法 xff0c 可以找到与 主要数据分布 不同的异常值
  • 在Keil 模拟器上测试RT-Thread代码

    前面一篇文章已经讲解了如何使用Keil模拟器来模拟硬件 xff0c 然后在此基础上运行RT Thread代码 文章链接 xff1a Keil 模拟器 STM32F103 上手指南 我们可以看到RT Thread官方有许多代码示例供我们学习参

随机推荐

  • 在这个艰难的环境下,我裸辞了

    2022年 xff0c 疫情期间 xff0c 工作了22年的我 xff0c 从软件研发管理的相关工作中 xff0c 辞职创业 xff0c 开启我的独立咨询顾问生涯 很多人不解和迷惑 xff0c 也有朋友关切的询问我的近况 xff0c 就差用
  • 2022年顶级实时操作系统榜单发布

    全球知名的开源软件仓库平台SourceForge最近公布了一份 2022年顶级实时操作系统 xff08 RTOS xff09 榜单 其中包括大家熟悉的FreeRTOS RT Thread ThreadX embOS等实时操作系统 地址 xf
  • IDE也卷了,微软杀入嵌入式IDE

    为什么说 IDE 也卷了 xff1f 因为前不久 xff08 3月初 xff09 xff0c Keil 官方推出了免费 没有代码大小限制的社区版IDE xff08 Community xff09 随后 xff08 在上周 xff09 xff
  • 飞机上一般是什么操作系统?

    航空软件其实并不神秘 xff0c 从计算机架构上来说 xff0c 同普通的计算机系统并无太大区别 xff0c 都是由处理器 总线 I O设备 存储设备 网络设备 通讯设备 操作系统和应用软件所构成的 仅仅是为了满足很高指标的可靠性 健壮性和
  • 重构代码总结

    下面推荐一下进行代码重构的文章 xff1a 重构指北 重构 xff0c 改善既有代码设计 精读 经验总结 重构让你的代码更优美和简洁 常见代码重构技巧 如何实施代码重构 xff1f 代码重构新手教程 xff1a 如何将烂代码变成好代码 xf
  • C语言#和##的使用方法和宏嵌套

    推荐下面几篇好文 xff1a C语言 宏嵌套的展开规则 C语言中define的全部使用方法总结 C语言宏的定义和宏的使用方法 xff08 define xff09 C语言宏定义中 与 的用法
  • 深入PCI与PCIe

    转载于老狼 xff1a https zhuanlan zhihu com p 26172972 https zhuanlan zhihu com p 26244141 PCI总线和设备树是X86硬件体系内很重要的组成部分 xff0c 几乎所
  • 【程序员学理财】了解不同理财产品的利息及合理的选择自己的理财配置

    1 利息计算公式 利息计算公式 2 不同理财产品的利率 首先是银行相关的理财 xff1a 余额宝利率 xff1a 年利率在1 94 支付宝短期定期理财 xff1a 一个月利率 xff1a 3 3 xff1b 三个月利率 xff1a 3 9
  • 嵌入式设备系统有无操作系统的区别

    随着通信 电子物联网的飞速发展 xff0c 每天都有各种各样的芯片被研发出来 xff0c 而要想知道这些芯片怎样工作以及工作后的作用 xff0c 则离不开软硬件工程师的努力 xff0c 任何一个计算机系统都是系统中软硬件协作的结果 xff0
  • 从C语言来理解文件系统

    文章目录 一 文件 文件系统二 C语言文件操作详解C语言中的文件是什么 xff1f 文件流 C语言fopen函数的用法 xff0c C语言打开文件详解fopen 函数的返回值判断文件是否打开成功 fopen 函数的打开方式关闭文件实例演示
  • 如何把CPP源程序改写成C语言?

    转载于 xff1a https mp weixin qq com s Xk5k UAItEndEGrUsQr Yw 曾经参与过公司的bpp项目 xff0c 就是bluetooth print profile 由于使用了hpijs的开源包 x
  • Android源码学习之六——ActivityManager框架解析

    ActivityManager 在操作系统中有重要的作用 xff0c 本文利用操作系统源码 xff0c 逐步理清 ActivityManager 的框架 xff0c 并从静态类结构图和动态序列图两个角度分别进行剖析 xff0c 从而帮助开发
  • 怎样修改Ubuntu&windows10双系统的默认启动顺序

    怎样修改Ubuntu amp windows10双系统的默认启动顺序 参考网址 xff1a https jingyan baidu com article f71d60379e16021ab641d1ab html 很多人在安装Linux系
  • 代码文档生成工具Doxygen教程及实例

    程序员的很多文档 xff0c 特别是有代码的文档 xff0c 绝大部分都是由一款文档生成工具 Doxygen 生成 什么是Doxygen Doxygen 是一个程序的文件产生工具 xff0c 可将程序中的特定批注转换成为说明文件 通常我们在
  • 什么是模块化,怎么写一个好的C语言模块?通过标准库,开源库,驱动库学习

    文章目录 什么是模块化1什么是模块化2为啥要用模块化模块化具体原理 xff1a 3模块化基本代码实现 xff1a c语言中条件编译相关的预编译指令4模块化编程注意事项 外设库 xff0c 硬件库 xff0c 驱动库stm32f10x gpi
  • 完成一个设计大赛,需要按照什么步骤?

    转载于 xff1a https blog csdn net rtthreadiotos article details 124094524 spm 61 1001 2014 3001 5502 亲爱的开发者们 xff1a RT Thread
  • 我又发现一个开源串口神器,太强了

    转载于 xff1a https mp weixin qq com s bLsFkIxQ 3adzskT9xUrEQ 最近发现了一个开源的串口项目 Serial Studio xff0c 这是一个强大的数据可视化软件 xff0c 支持串口通信
  • 互联网架构的软件架构是否会对车用软件等传统嵌入式领域的系统架构产生影响

    转载于 xff1a 互联网架构的软件架构是否会对车用软件等传统嵌入式领域的系统架构产生影响 xff1f xff1f 辣笔小星的回答 知乎 https www zhihu com question 34875160 answer 241043
  • 搭建一个轻量级实验室,还不错

    转载于 xff1a https mp weixin qq com s m4 IwyEheiCne5oSr6 LwQ 几乎绝大部分电子工程师都有一个 梦想 xff0c 那就是拥有自己的实验室 有不少朋友问 xff0c 搭建一个电子实验室是不是
  • C语言类型转换-自动类型转换、强制类型转换、指针类型转换

    数据类型转换就是将数据 xff08 变量 数值 表达式的结果等 xff09 从一种类型转换为另一种类型 自动类型转换 自动类型转换就是编译器默默地 隐式地 偷偷地进行的数据类型转换 xff0c 这种转换不需要程序员干预 xff0c 会自动发