首页 > 其他分享 >15.75.【C语言】表达式求值

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

时间:2024-08-04 14:23:33浏览次数:13  
标签:int mov C语言 char 00000000 整型 求值 15.75 ptr

目录

一.整型提升

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这行代码

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

 

标签:int,mov,C语言,char,00000000,整型,求值,15.75,ptr
From: https://blog.csdn.net/2401_85828611/article/details/140843783

相关文章

  • 关于C语言中素数的求解
    什么是素数?一个大于1的自然数,除了1和它自身外,不能被其他自然数整除的数叫做素数。(素数=质数)在C语言中求解素数的几种方法。方法一:直接试数法(从1开始逐一试数)例:求解100到200之间的素数。#include<stdio.h>intmain(){ inti=0; intcount=0; for(i=100;i<=......
  • C语言关于函数的基本介绍
    目录一、前言二、为什么需要函数?三、什么是函数?四、函数的作用及分类。五、函数的基本用法。六、主函数的简单介绍。七、函数原型以及一些常用的系统函数的介绍。一、前言    这些都是作者学习C语言过程中了解到的只是,有的地方可能不是写的特别清楚,同时这也是......
  • 【C语言】C语言期末突击/考研--函数
    目录一、函数的声明与定义-嵌套调用1.1.函数的声明与定义1.2.函数的分类与调用二、函数的递归调用三、局部变量与全局变量3.1.全局变量解析形参实参解析3.2.局部变量与全局变量四、练习题及解析一、函数的声明与定义-嵌套调用1.1.函数的声明与定义    函数间......
  • 【C语言】字符函数和字符串函数详解
    ......
  • 【C语言】结构体内存布局解析——字节对齐
    ......
  • C语言:动态内存管理
    动态内存管理一、动态分配内存的必要性普通内存分配动态内存分配二、动态内存分配函数(一)malloc(二)calloc(三)realloc(四)free三、常见的错误(一)对空指针进行解引用操作(二)对动态分配空间越界访问(三)free释放动态分配空间的一部分(四)动态开辟内存忘记释放四、柔性数组(一)柔性数组......
  • C语言基础8数组
    什么是数组数组是相同类型,有序数据的集合。数组的特征数组中的数据被称为数组的元素,是同构的数组中的元素存放在内存空间里  衍生概念:下标(索引)下标或索引代表了数组中元素距离第一个元素的偏移位置数组中元素的地址值,下标越大,地址值越大。(每一块内存空间都有一个独有的......
  • C语言基础7循环结构
    什么是循环        代码的重复执行,就叫做循环。循环的分类无限循环:程序设计中尽量避免无限循环。(程序中的无限循环必须可控)有限循环:循环限定循环次数或者循环的条件循环的构成循环体循环条件当型循环的实现while语法:while(循环条件){循环语句;}......
  • C语言基础6分支结构
    分支结构分支结构:又被称之为选择结构概念选择结构:根据条件成立与否,选择相应的操作条件建构关系表达式:含有关系运算符的表达式(<,>,>=,<=,!=,==)逻辑表达式:含有逻辑运算符的表达式(&&,||,!),往往用来构建复杂的符合条件:比如:常量/变量:值是否非0,取值(0|1)注意:类似以下写法,没有意义......
  • C语言运算符优先级口诀
    口诀内容(优先级自上而下递减;由逗号分隔的,优先级自左到右递减。)圆方括号,箭头句号。单目增减非反负,针强址长,从右。乘除求模,加减,位移,大小,等不等。位与异或,逻辑与或。条件赋值均右。真逗。解释(斜体字是补全;加粗字是对整行的说明;代码块即是所对应操作符。)圆括号()方括号[]......