C语言:表达式运算的类型转换

news2025/3/18 9:58:48

相关阅读

C语言icon-default.png?t=N7T8https://blog.csdn.net/weixin_45791458/category_12423166.html?spm=1001.2014.3001.5482


1、前言

        C语言作为一种强类型语言,要求在进行操作时变量类型必须匹配。然而,为了提高灵活性和便利性,C语言提供了一套规则,用于在必要时自动转换不同类型的变量。这些规则称为类型转换(type conversion)。本文将详细介绍C语言中的类型转换及其重要性。

        首先来看一个简单的表达式的例子,例1中的主函数内有一个赋值表达式,其中标识符a是左操作数(左值),一个乘法表达式作为子表达式是右操作数(右值);对于这个乘法表达式,其左操作数(b+c)加法表达式是一个子表达式,标识符d是其右操作数;加法表达式中,标识符b、c分别是左、右操作数。

// 例1

int a;
char b;
float c;
double d;

int main()
{
    a = (b + c) * d;
    return 0;
}

        从例1中可以看出,这些操作数的类型是不同的,因此它们在进行运算前会进行类型转换,需要特别注意的是:类型转换(除整型提升)是从表达式底层向上,且在表达式操作数这个范围进行的。也就是说,对于例1,操作数b和c的运算并不会受到操作数a和d影响(但是加法表达式的结果会受到操作数d的影响);操作数d不会受到操作数a、b、c的影响,而是受到加法表达式结果的影响。对于赋值表达式,类型转换可能导致降级(demotion),子表达式(b + c) * d的结果可能会因为操作数a发生类型转换,但操作数a不会受到赋值号右边表达式的影响。

2、整型提升 (Integer Promotion)

        所有char类型和short类型(包括 signed 和 unsigned)在表达式中(如上面所说,赋值表达式的左操作数除外)都会被提升为int类型。如果short类型和int类型的大小相同(例如都是16位),则unsigned short类型会被转换为unsigned int类型。这是因为在这种情况下,unsigned short类型比int类型所能表示的最大值更大。整型提升是在全局的范围二不是表达式范围内进行的,因此在进行任何表达式计算和其他的类型转换前,整型提升就已经进行了。

        下面是一个整型提升的例子。

// 例2

#include <stdio.h>

unsigned char b=255;
unsigned char c=255;
unsigned char d=255;

int main()
{
    long int a = (b + c) * d; 
    printf("%d \n", a);
	printf("%d \n", (b + c) * d);
    return 0;
}

输出:
130050 
130050

        在例2中,操作数b、c、d在进行运算前都被提升为int类型:所以b+c并不会产生溢出,因为此时进行的是两个int类型相加得到int类型的运算,而不是两个unsigned char类型相加得到unsigned char类型的运算。在进行最后的赋值运算时,由于表达式(b + c) * d是int类型而标识符a是long int类型,会进行类型转换(此处是提升)。

3、类型检测宏

        为了更加直观的分析表达式的类型,下面定义了一个可以检测表达式类型的类函数宏。

​#define TYPEOF(x) _Generic((typeof(x)){0}, \
    char: "char", \
    unsigned char: "unsigned char", \
    short: "short", \
    unsigned short: "unsigned short", \
    int: "int", \
    unsigned int: "unsigned int", \
    long: "long", \
    unsigned long: "unsigned long", \
    long long: "long long", \
    unsigned long long: "unsigned long long", \
    float: "float", \
    double: "double", \
    long double: "long double", \
    default: "unknown")

        下面的例3检测了例2中标识符a和表达式(b + c) * d)的类型,可以看到上面的分析无误。

// 例3
#include <stdio.h>
#define TYPEOF(x) _Generic((typeof(x)){0}, \
    char: "char", \
    unsigned char: "unsigned char", \
    short: "short", \
    unsigned short: "unsigned short", \
    int: "int", \
    unsigned int: "unsigned int", \
    long: "long", \
    unsigned long: "unsigned long", \
    long long: "long long", \
    unsigned long long: "unsigned long long", \
    float: "float", \
    double: "double", \
    long double: "long double", \
    default: "unknown")


unsigned char b=255;
unsigned char c=255;
unsigned char d=255;

int main()
{
    long int a = (b + c) * d; 
    printf("%d , type is %s \n", a, TYPEOF(a));
	printf("%d , type is %s \n", (b + c) * d, TYPEOF((b + c) * d));
    return 0;
}

输出:
130050 , type is long 
130050 , type is int 

4、常见类型转换 (Usual Arithmetic Conversions)

        一个表达式运算前,会先将其操作数先统一转换为其中的最高等级的类型(有些运算不会将某些操作数考虑在内,比如三目运算符的第一个操作数,这会在最后进行说明,我们只考虑一般情况)。

        类型的等级是根据其能表示的最大正数而确定的,如下表所示。

等级(1代表最高)类型
1long double
2double
3float
4unsigned long long
5long long
6unsigned long
7long
8unsigned int
9int

        表中不包含char类型和short类型(包括 signed 和 unsigned),因为他们会因为整型提升而在一开始就进行一次类型转换。

        例4给出了一个常见类型转换的例子。

// 例4
#include <stdio.h>
#include <limits.h>
#define TYPEOF(x) _Generic((typeof(x)){0}, \
    char: "char", \
    unsigned char: "unsigned char", \
    short: "short", \
    unsigned short: "unsigned short", \
    int: "int", \
    unsigned int: "unsigned int", \
    long: "long", \
    unsigned long: "unsigned long", \
    long long: "long long", \
    unsigned long long: "unsigned long long", \
    float: "float", \
    double: "double", \
    long double: "long double", \
    default: "unknown")


int b=INT_MAX;
int c=1;
long d=1;

int main()
{
	printf("%d , type is %s \n", (b + c) , TYPEOF(b + c));
	printf("%lld , type is %s \n", (b + c) * d, TYPEOF((b + c) * d));
    return 0;
}

输出:
-2147483648 , type is int 
-2147483648 , type is long 

        由于此时b的值是int类型能表达的最大值,所以表达式b+c的结果还是一个int类型,且会溢出,产生一个大负数,如第一个printf显示的那样。即使表达式b+c的结果再与一个long类型的操作数进行运算,也无济于事,如我们早就说过的,类型转换是在表达式的范围进行的,此时表达式b+c的结果会类型提升为long类型。

5、一些类型转换的例外

        上面的例子似乎说明了,类型转换就是:首先进行整型提升,最后自底向上进行常见类型转换,统一每次运算的所有操作数的类型。

        但这是错误的,并不是所有操作数之间的常见类型转换都会发生,例如对于三目运算符的第一个操作数,三目运算符的第二个和第三个操作数会因为常见类型类型转换而统一类型,但这与第一个操作数无关(如果第一个操作数本身是一个子表达式,该子表达式的计算过程会进行类型转换,但子表达式的结果不会影响第二个和第三个操作数的类型),例5给出了这种情况的例子。

// 例5
#include <stdio.h>
#include <limits.h>
#define TYPEOF(x) _Generic((typeof(x)){0}, \
    char: "char", \
    unsigned char: "unsigned char", \
    short: "short", \
    unsigned short: "unsigned short", \
    int: "int", \
    unsigned int: "unsigned int", \
    long: "long", \
    unsigned long: "unsigned long", \
    long long: "long long", \
    unsigned long long: "unsigned long long", \
    float: "float", \
    double: "double", \
    long double: "long double", \
    default: "unknown")


unsigned char b=1;
unsigned char c=0;
long d=1;
int e=INT_MAX;
long f=1;

int main()
{
	printf("%d , type is %s \n", d?b:c , TYPEOF(d?b:c));
	printf("%d , type is %s \n", (e+f>0)?b:c , TYPEOF((e+f>0)?b:c)); // 因为e和f之间发生类型转换,所以并未溢出
    return 0;
}

输出:
1 , type is int 
1 , type is int 

        与此类似的还有逗号表达式,其值是最后一个操作数的值,各个操作数之间不会发生常见类型转换,如例6所示。

// 例6
#include <stdio.h>
#include <limits.h>
#define TYPEOF(x) _Generic((typeof(x)){0}, \
    char: "char", \
    unsigned char: "unsigned char", \
    short: "short", \
    unsigned short: "unsigned short", \
    int: "int", \
    unsigned int: "unsigned int", \
    long: "long", \
    unsigned long: "unsigned long", \
    long long: "long long", \
    unsigned long long: "unsigned long long", \
    float: "float", \
    double: "double", \
    long double: "long double", \
    default: "unknown")


long d=0;
int e=INT_MAX;
int a;
int main()
{
	a=(d, e, e+1); // 子表达式e+1并不会因为d发生类型提升
	printf("%d , type is %s \n", a , TYPEOF(a));
    return 0;
}

输出:
-2147483648 , type is int 

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

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

相关文章

【C语言】C语言-学生成绩管理系统(源码+数据文件+课程论文)【独一无二】

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;公众号&#x1f448;&#xff1a;测试开发自动化【获取源码商业合作】 &#x1f449;荣__誉&#x1f448;&#xff1a;阿里云博客专家博主、5…

【面试干货】事务的并发问题(脏读、不可重复读、幻读)与解决策略

【面试干货】事务的并发问题&#xff08;脏读、不可重复读、幻读&#xff09;与解决策略 一、脏读&#xff08;Dirty Read&#xff09;二、不可重复读&#xff08;Non-repeatable Read&#xff09;三、幻读&#xff08;Phantom Read&#xff09;四、总结 &#x1f496;The Begi…

图计算与ID-Mapping

目录 一、图计算&#xff1a; 图计算起源&#xff1a; 图计算特点&#xff1a; 图计算的应用&#xff1a; Spark GraphX图处理库 ID-Mapping 二、总结&#xff1a; 一、图计算&#xff1a; 图&#xff08;Graph&#xff09;是用于表示对象之间关联关系的一种抽象数据结构…

C++初探_右值引用

左值&#xff1a;在内存中有确定的存储地址。 右值&#xff1a;可出现在赋值表达式右边。包括&#xff1a;字面常量、诸如xy等的表达式&#xff0c;以及返回值的函数。 代码&#xff1a; #include <iostream> using namespace std;int main() {int x 10;int y 13;int…

《拯救大学生课设不挂科第二期之Windows11下安装VC6.0(VC++6.0)与跑通Hello World C语言程序教程》【官方笔记】

背景与目标人群&#xff1a; 大学第一次学C语言的时候&#xff0c;大部分老师会选择VC6这个编辑器。 但由于很多人是新手&#xff0c;第一次上大学学C语言。 老师要求VC6.0&#xff08;VC6.0&#xff09;写C语言跑程序可能很多人还是第一次接触电脑。 需要安装VC6这个编辑器…

Spring服务启动后就执行某个方法

下边按照执行顺序前后&#xff0c;测试代码结果截图放到最后&#xff1a; 1、注解PostConstruct 时间&#xff1a;当前bean被创建并且所有的依赖注入完成之后执行&#xff1b; 使用&#xff1a;当前bean 所在类内的某个方法上 添加该注解&#xff1b;该方法没有参数&#xf…

Parasoft C++Test软件静态分析操作指南_编码规范/标准检查

系列文章目录 Parasoft CTest软件安装指南 Parasoft CTest软件静态分析操作指南_编码规范/标准检查 Parasoft CTest软件静态分析操作指南_软件质量度量 Parasoft CTest软件静态分析_自动提取静态分析数据生成文档 Parasoft CTest软件单元测试_操作指南 Parasoft CTest软件单元…

SQL分类——DDL(数据定义语言)

一、DDL&#xff08;数据定义语言&#xff09; &#xff08;1&#xff09;DDL——数据库——操作的相关语法&#xff1a; 查询 可以一次性查询当前数据库服务器中所有的数据库&#xff1a; SHOW DATABASES; 查询当前所处的数据库&#xff1a; SELECT DATABASE(); 创建 一般最简…

基于FPGA的函数信号发生器设计

本科时期的一个课设&#xff0c;现在将他分享出来&#xff0c;写了很详细的文章&#xff0c;可以直接拿去使用&#xff1a;设计采用波形查找表和相位累加器的方法实现DDS&#xff0c;查找表的数据位宽为8位&#xff0c;采样点数为4096。波形产生范围是100Hz-20MHz&#xff0c;最…

上位机图像处理和嵌入式模块部署(mcu定时器配置)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 在mcu开发过程当中&#xff0c;有一种开发模式用的比较多&#xff0c;那就是中断while&#xff08;1&#xff09;。这里面的中断&#xff0c;又是以…

力扣96. 不同的二叉搜索树

Problem: 96. 不同的二叉搜索树 文章目录 题目描述思路复杂度Code 题目描述 思路 一个数字做根节点的话可能的结果为&#xff1a;其左边数字做子树的组合数字乘以其右边数字做子树的个数之积 1.创建备忘录memo&#xff1b; 2.递归分别求取当前数字左边和右边数字做子树的数量&…

【C++入门】—— C++入门 (中)_引用

前言&#xff1a;了解了什么是C&#xff0c;我们进入了C入门知识的命名空间&#xff0c;现在我们接着来讲剩下的C入门知识讲解。 如果前面还有什么不懂的只是不妨仔细阅读上一篇&#xff1a; C入门知识 (命名空间) 本篇主要内容&#xff1a; 缺省参数 函数重载 引用 C入门 1. …

19c数据库19.9以下dg切换打开hang住问题

原主库发起切换请求&#xff0c;原主库正常切换数据库角色&#xff0c;但原从库无法正常打开数据库&#xff0c;尝试关闭重启&#xff0c;依旧无法解决问题。 查看切换过程中原从库数据库后台日志&#xff0c;发现数据库一直不断重试清理 SRLs&#xff0c; 后台alert日志&…

【力扣刷题笔记第三期】Python 数据结构与算法

先从简单的题型开始刷起&#xff0c;一起加油啊&#xff01;&#xff01; 点个关注和收藏呗&#xff0c;一起刷题鸭&#xff01;&#xff01; 第一批题目 1.设备编号 给定一个设备编号区间[start, end]&#xff0c;包含4或18的编号都不能使用&#xff0c;如&#xff1a;418、…

arcgisPro将一个图层的要素复制到另一个图层

1、打开两个图层&#xff0c;如下&#xff0c;其中一个图层中有两个要素&#xff0c;需要将其中一个要素复制到另一个图层中&#xff0c;展示如下&#xff1a; 2、选中待复制要素&#xff0c;点击复制按钮&#xff0c;如下&#xff1a; 3、下拉粘贴按钮列表&#xff0c;选择【选…

学AI绘图【300集SD新课】--Stable Diffusion教程

学AI绘图需要以下步骤&#xff1a; 明确目标和需求&#xff1a;首先明确设计图的目的&#xff0c;是用于展示算法流程、模型结构还是其他目的。选择合适的工具&#xff1a;根据需求选择合适的绘图工具&#xff0c;如Visio、PowerPoint、Adobe Illustrator等。绘制草图&#xf…

【全开源】知识库文档系统源码(ThinkPHP+FastAdmin)

知识库文档系统源码&#xff1a;构建智慧知识库的基石 引言 在当今信息爆炸的时代&#xff0c;知识的有效管理和利用对于企业和个人来说至关重要。知识库文档系统源码正是为了满足这一需求而诞生的&#xff0c;它提供了一个高效、便捷的平台&#xff0c;帮助用户构建、管理、…

【Mac】MWeb Pro(好用的markdown编辑器) v4.5.9中文版安装教程

软件介绍 MWeb Pro for Mac是一款Mac上的Markdown编辑器软件&#xff0c;它支持实时预览&#xff0c;语法高亮&#xff0c;自动保存和备份等功能&#xff0c;并且有多种主题和样式可供选择。此外&#xff0c;MWeb还支持多种导出格式&#xff0c;包括HTML、PDF、Word、ePub等&a…

失落的方舟台服预下载教程 一键下载+账号注册教程

失落的方舟台服预下载教程 一键下载&#xff0b;账号注册教程 是一款今年备受瞩目的游戏&#xff0c;将于5月30日正式上线&#xff0c;这款游戏搭建在虚幻引擎的基础上&#xff0c;为玩家们带来了极佳的视觉体验。这款游戏秉承着MMO类型游戏一贯的玩法&#xff0c;但是制作组在…

小阿轩yx-DNS域名解析服务分离解析

小阿轩yx-DNS域名解析服务分离解析 分离解析介绍 分离解析的域名服务器实际也是主域名服务器这里主要是指根据不同的客户端提供不同的域名解析记录比如来自内网和外网的不同网段地址区域的客户机求解析同一域名时&#xff0c;为其提供不同的解析结果&#xff0c;得到不同的IP…