15.75.【C语言】表达式求值

news2025/1/23 15:00:59

目录

一.整型提升

1.定义

2.

一.整型提升

1.定义

C语言中整型算术运算总是至少以缺省(默认)整型类型的精度来进行的。为了获得这个精度,表达式中的字符和短整型操作数在使用之前被转换为普通整型,这种转换称为整型提升

2.整型提升的原因:

表达式的整型运算要在CPU的相应运算器件内执行,CPU内整型运算器(ALU)的操作数的字节长度一般就是int的字节长度,同时也是CPU的通用寄存器的长度。
因此,即使两个char类型的相加,在CPU执行时实际上也要先转换为CPU内整型操作数的标准长度
通用CPU(general-purpose CPU)是难以直接实现两个8比特字节直接相加运算(虽然机器指令中可能有这种字节相加指令)。所以,表达式中各种长度可能小于int长度的整型值,都必须先转换为int或unsigned int,然后才能送入CPU去执行运算。

char a;//char本质上是存储的是ASCII值 
short b;

char,short-->int 或 unsigned int

3.方法:

*有符号整数提按照变量的数据类型的符号位来提升至int的位数

char a;-->signed char a;

+5原码=反码=补码=00000000 00000000 00000000 00000101 (作int时)(从8bit-->32bit)

由于char只能存8 bit,所以变量里存储00000000 00000000 00000000 00000101,称为整型截断:一个字节大的整型数据赋值给一个字节小的整型变量时,发生的数据丢失

*无符号整数提升,高位补0至int的位数

 练习:求打印的值

#include <stdio.h>
int main()
{
  char a=5;
  char b=126;
  char c=a+b;
  printf("%d",c);
}

分析:计算(a+b)前char整型提升至int

5:      0000000 00000000 00000000 00000101

126:    00000000 00000000 00000000 01111110

5+126: 00000000 00000000 0000000 10000011

存储至c时,整型截断:00000000 00000000 0000000 100000011

所以c的二进制序列为100000011,c的十进制序列为131,但打印的结果不是131

要充分理解:字符和短整型操作数使用之前被转换为普通整型的含义,printf也算使用!

解释1:

VS2022中char默认按signed char处理,最高位是1,所以高位补1

补码:11111111 11111111 11111111 10000011

符号位不变,其余各位取反再+1:10000000 00000000 00000000 01111101-->-125

解释2:“环绕溢出”

由于8bit存储有符号整数范围-128~+127

所以画图:


练习:代码修改后求打印的值

#include <stdio.h>
int main()
{
  unsigned char a = 5;
  unsigned char b = 126;
  unsigned char c = a + b;
  printf("%d", c);
}

分析指定无符号(unsigned char)整数,范围是0~2的8次方-1即255,显然131<255,所以打印131

二.算术转换

1.定义

如果某个操作符的各个操作数属于不同的类型,那么除非其中一个操作数的转换为另一个操作数的类型,否则操作就无法进行

2.剖析

float a;
double b;
a+b;

a+b,a是float,b是double,两者类型不一样,VS会把float转换为double,执行a+b;具体原因参见第3点

3.寻常算术转换

1. long double
2. double
3. float
4. unsigned long int
5. long int
6. unsigned int
7. int

规则:序号大的转换为序号小的

序号3是float,序号2是double,2<3,所以把float转换为double

4.问题表达式例子分析

表达式的执行看优先级(回顾15.25【C语言】操作符的属性),而且表达式真正计算的时候先看相邻操作符的优先级再决定先算谁,但有了优先级就一定能确定唯一的运算顺序吗?

a*b + c*d + e*f

执行顺序有两种可能:

1.先算完*,后算完+

2.a*b-->c*d-->a*b + c*d-->e*f-->a*b + c*d + e*f

显然不能确定唯一的运算顺序

注意:不同的运算顺序可能答案有所不同,上述a,b,c,d,e,f可以是变量,也可以是表达式


int c=3;
int b=c + --c;

查优先级可知,先--后+ ,但+号两边都含c,+的左操作数在--c之前还是之后准备好的,无从得知,建议再设一个变量


出自《C和指针》

int main()
{
  int i = 10;
  i = i-- - --i * ( i = -3 ) * i++ + ++i;
  printf("i = %d\n", i);
  return 0;
}

理由和上方解释思想一样 ,不同编译器解释出来的答案不一样


下列代码的输出结果是否可求?

#include <stdio.h>
int fun()
{
  static int count = 1;
  return ++count;
}

int main()
{
  int answer=0;
  answer = fun() - fun() * fun();
  printf( "%d\n", answer);
  return 0;
}

注意到 static int 回顾17.【C语言】初识常见关键字 下

摘取:

int a=1;等价于auto int a=1;

static int a=1;修饰局部变量,a不会自动销毁,生命周期变长

总结:static修饰局部变量时改变了变量的存储类型,进而改变了局部变量的生命周期

count会从1到4而且函数的调用先后顺序无法通过操作符的优先级确定


#include <stdio.h>
int main()
{
  int i = 1;
  int result = (++i) + (++i) + (++i);
  printf("%d\n", result);
  printf("%d\n", i);
  return 0;
}

Debug x86环境下编译 ,反汇编

#include <stdio.h>
int main()
{
006B1870  push        ebp  
006B1871  mov         ebp,esp  
006B1873  sub         esp,0D8h  
006B1879  push        ebx  
006B187A  push        esi  
006B187B  push        edi  
006B187C  lea         edi,[ebp-18h]  
006B187F  mov         ecx,6  
006B1884  mov         eax,0CCCCCCCCh  
006B1889  rep stos    dword ptr es:[edi]  
006B188B  mov         ecx,offset _2D923C74_FileName@c (06BC008h)  
006B1890  call        @__CheckForDebuggerJustMyCode@4 (06B132Ah)  
006B1895  nop  
	int i = 1;
006B1896  mov         dword ptr [i],1  
	int result = (++i) + (++i) + (++i);
006B189D  mov         eax,dword ptr [i]  
006B18A0  add         eax,1  
006B18A3  mov         dword ptr [i],eax  
006B18A6  mov         ecx,dword ptr [i]  
006B18A9  add         ecx,1  
006B18AC  mov         dword ptr [i],ecx  
006B18AF  mov         edx,dword ptr [i]  
006B18B2  add         edx,1  
006B18B5  mov         dword ptr [i],edx  
006B18B8  mov         eax,dword ptr [i]  
006B18BB  add         eax,dword ptr [i]  
006B18BE  add         eax,dword ptr [i]  
006B18C1  mov         dword ptr [result],eax  
	printf("%d\n", result);
006B18C4  mov         eax,dword ptr [result]  
006B18C7  push        eax  
006B18C8  push        offset string "%d\n" (06B7B30h)  
006B18CD  call        _printf (06B10D2h)  
006B18D2  add         esp,8  
	printf("%d\n", i);
006B18D5  mov         eax,dword ptr [i]  
006B18D8  push        eax  
006B18D9  push        offset string "%d\n" (06B7B30h)  
006B18DE  call        _printf (06B10D2h)  
006B18E3  add         esp,8  
	return 0;
006B18E6  xor         eax,eax  
}
006B18E8  pop         edi  
006B18E9  pop         esi  
006B18EA  pop         ebx  
006B18EB  add         esp,0D8h  
006B18F1  cmp         ebp,esp  
006B18F3  call        __RTC_CheckEsp (06B124Eh)  
006B18F8  mov         esp,ebp  
006B18FA  pop         ebp  
006B18FB  ret  

int i = 1;以上的汇编指令是栈区的初始化,具体分析见36.【C语言】函数栈帧的创建和销毁

重点分析int i=1;和int result这行代码

总结:即使有了操作符的优先级和结合性,我们写出的表达式依然有可能不能通过操作符的属性确定唯一的计算路径,那这个表达式就是存在潜在风险的,建议不要写出特别复杂的表达式

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

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

相关文章

C++初学者指南-5.标准库(第二部分)--移除元素算法

C初学者指南-5.标准库(第二部分)–移除元素算法 文章目录 C初学者指南-5.标准库(第二部分)--移除元素算法remove / remove_ifremove_copy / remove_copy_ifunique / unique_copyerase / erase_if相关内容 不熟悉 C 的标准库算法&#xff1f; ⇒ 简介 remove / remove…

还有谁分不清Oracle认证里的OCA、OCP、OCM?

在IT行业&#xff0c;Oracle认证是许多专业人士提升技能和职业竞争力的重要途径。 Oracle认证是一套由Oracle公司提供的全球认可的专业资格认证体系&#xff0c;旨在证明个人在Oracle数据库及相关技术领域的专业技能和知识水平。 Oracle认证作为数据库认证中的天花板&#xff0…

目标检测——GDXray数据集转为YOLO格式

关于该数据集的介绍可以看我写的另一篇博客&#xff1a;链接 论文题目&#xff1a;《GDXray: The Database of X-ray Images for Nondestructive Testing》论文链接&#xff1a;https://link.springer.com/article/10.1007/s10921-015-0315-7 Github链接&#xff1a; https:…

JavaScript小本本|JavaScript 对象方法定义的演变

在微信中阅读&#xff0c;欢迎关注公众号&#xff1a;CodeFit。 创作不易&#xff0c;如果你觉得这篇文章对您有帮助&#xff0c;请不要忘了 点赞、分享 和 关注&#xff0c;为我的 持续创作 提供 动力&#xff01; 欢迎订阅《Vue 3.x 必修课&#xff5c;2024》&#xff1a;htt…

JAVA项目基于SpringBoot的外卖点餐管理系统

目录 一、前言 二、技术介绍 三、系统实现 四、论文参考 五、核心代码 六、源码获取 全栈码农以及毕业设计实战开发&#xff0c;CSDN平台Java领域新星创作者&#xff0c;专注于大学生项目实战开发、讲解和毕业答疑辅导。获取源码联系方式请查看文末 一、前言 随着生活节…

C++20中的模块

大多数C项目使用多个翻译单元(translation units)&#xff0c;因此它们需要在这些单元之间共享声明和定义(share declarations and definitions)。headers的使用在这方面非常突出。模块(module)是一种language feature&#xff0c;用于在翻译单元之间共享声明和定义。它们是某些…

追问试面试系列:Dubbo

欢迎来到Dubbo系列,在面试中被问到Dubbo相关的问题时,大部分都是简历上写了Dubbo,或者面试官想尝试问问你对Dubbo是否了解。 本系列主要是针对面试官通过一个点就使劲儿往下问的情况。 面试官:说说你们项目亮点 好的面试官 我们这个项目的技术亮点在于采用了Spring Cloud…

正点原子imx6ull-mini-Linux驱动之Linux I2C 驱动实验(21)

I2C 是很常用的一个串行通信接口&#xff0c;用于连接各种外设、传感器等器件&#xff0c;在裸机篇已经对 I.MX6U 的 I2C 接口做了详细的讲解。本章我们来学习一下如何在 Linux 下开发 I2C 接口器件 驱动&#xff0c;重点是学习 Linux 下的 I2C 驱动框架&#xff0c;按照指定的…

人工智能深度学习系列—探索Jaccard相似度损失:图像分割领域的新利器

文章目录 1. 背景介绍2. Loss计算公式3. 使用场景4. 代码样例5. 总结 1. 背景介绍 在深度学习的各种应用中&#xff0c;图像分割是一项极具挑战性的任务。Jaccard相似度损失&#xff08;Jaccard Similarity Loss&#xff09;&#xff0c;又称为IoU损失&#xff08;Intersectio…

计算机基本理论与程序运行原理概述

目录 计算机的基本表示方法 计算机的组成 程序运行的原理 指令执行的流水线 编译原理 个人理解 面试题总结 计算机的基本表示方法 计算机系统使用高、低电平来表示逻辑1和0。数据在计算机中的存储、传输和处理均以二进制形式进行。数据通过总线作为电信号进行传输&…

Es6常用的一些数组处理方法

在平时的开发中&#xff0c;我们很多时候用到数组结构数据&#xff0c;那么如何高效处理数组是可以提高开发效率的&#xff0c;现在越来越多人使用es6&#xff0c;那么它的很多方法简化了我们对数据的操作&#xff0c;比如以前数组循环用for循环写比较多的代码&#xff0c;现在…

HTML-07.表格标签

一、要制作的表格如下 二、代码如下 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>表格标签<…

探索数据结构:二叉搜索树的递归与非递归实现

✨✨ 欢迎大家来到贝蒂大讲堂✨✨ &#x1f388;&#x1f388;养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; 所属专栏&#xff1a;数据结构与算法 贝蒂的主页&#xff1a;Betty’s blog 1. 二叉搜索树的介绍 二插入搜索树&#xff08;Binary Search Tree&…

第16课 Scratch入门篇:师生问候-广播版

师生问候-广播版 故事背景&#xff1a; 上节课我们完成了师生问候功能&#xff0c;指令罗列的蛮多&#xff0c;写起来很麻烦&#xff0c;而且阅读起来不容易理解&#xff0c;这节课我们把上节课内容进行优化&#xff0c;引入一个新的指令-广播&#xff0c;广播相当于一个事件的…

DFS之迭代加深+双向DFS+IDA*

迭代加深&#xff1a; 搜索范围一层一层扩大&#xff0c;可以快速某些分支比较深&#xff0c;但是答案比较浅的问题。 https://www.acwing.com/problem/content/172/ 通过观察可以发现&#xff1a; 1.搜索时最坏情况可能搜到100层&#xff0c;比较深&#xff0c;但是答案应…

【Javax.Validation】✈️整合 SpringBoot 实现运行时的参数校验

目录 &#x1f44b;前言 &#x1f440;一、Validation 依赖 &#x1f4eb;二、常见注解 2.1 不需要指定注解参数 2.2 需要声明注解参数 &#x1f49e;️三、项目测试注解使用 3.1 依赖引入 3.2 实体类创建 3.3 创建对外接口 3.4 模拟请求 &#x1f331;四、章末 &#x1f…

RSS 2024 清华大学交叉院高阳提出高效的机器人操作技能学习方法

机器人掌握一项新技能需要几步&#xff1f; 一般来说&#xff0c;在传统机器学习方法中&#xff0c;通常使用演示学习的方式教会机器人掌握新技能&#xff0c;然而&#xff0c;收集高质量的演示数据成本高且耗时&#xff0c;一定程度上影响了机器人技能学习进度。尽管视频作为…

⭕️【论文阅读】《Interactive Class-Agnostic Object Counting》

[2309.05277] Interactive Class-Agnostic Object Counting (arxiv.org) code&#xff1a; cvlab-stonybrook/ICACount: [ICCV23] Official Pytorch Implementation of Interactive Class-Agnostic Object Counting (github.com) 目录 Abstract Abstract 我们提出了一个新…

linux学习记录(一)--------目录及文件操作

文章目录 前言Linux目录及文件操作1.Linux目录结构2.常用的Linux命令3.vi编辑器的简单使用4.vi的两个模式 前言 小白学习linux记录有错误随时指出~ Linux目录及文件操作 Linux采用Shell命令->操作文件 1.Linux目录结构 根目录&#xff1a;/ 用户目录&#xff1a;~或者/ho…

float转uint8_t数组

float类型在x64中占4字节&#xff0c;需要占据uint8_t数组大小4字节 数据float类型3.14&#xff0c;在内存中4字节地址应该为0x4048f5c3 如果直接使用memcpy内存复制&#xff0c;0xc3会放在数组下标小的位置