不同数据在存储中的存储方式(上半部分)

一.表达式求值

1.1 表达式求值

求值的顺序一般根据操作符的优先级和结合性来决定。但是有些表达式的操作符在求值的过程中可能需要转换为其他类型

1.2 隐式类型转换

c的整型算术运算总是以缺省(默认)整型的精度来进行的

为了获得这个精度,表达式中的字符型和短整型操作数在使用之前需要转换为普通整型(int),这种转换称为整型提升

1.3 整型提升的意义

 表达式的整型运算要在CUP的相应运算器件内执行,CPU内整型运算器(ALU)的字节长度一般就是int的字节长度,同时也是CPU的通用寄存器的长度

因此两个char类型的相加,在CPU执行时实际上也要先转换为CPU内整型操作数的标准长度

(虽然计算机中可能有这种字节相加的指令(Adc指令))。表达式中各种长度可能小于int长度的整型值,都必须先中转换为一个int或unsigned int,然后送入CPU中区执行运行

1.4 如何整型提升

如何整型提升?

正数的整型提升

char a = 1;

1的补码存在char所开辟的空间中,即 : 00000001(因为char是一个字节只能存8个bit,即将5的补码后8位截取(从低位开始截取))

整型提升后:00000000000000000000000000000001(有符号类型高位补原符号)

负数的整型提升

char a = -1;

同上

-1的补码:10000001

整型提升后:11111111111111111111111110000001

无符号整型,整型提升高位补0

例1:

char a = 5;
char b = 126;
char c = a + b;
printf("%d", c);

  

因为a,b在进行整形运算,又因为a,b是两个字符,一个字符在内存中开辟的空间是容纳不下一个整型的,所以这时候就需要系统将a,b整型提升之后在运算

没整型提升之前a,b,c在内存中存储的内容为(以补码的形式):

5的补码存在char所开辟的空间中,即 : 00000101

同理,126的补码: 01111110

当a和b开始整型计算的时候,他们需要先整型提升后在进行运算

即:

5整型提升之后:00000000000000000000000000000101

126整型提升之后:00000000000000000000000001111110

在将他们相加得到:00000000000000000000000010000011

因为我们的操作完之后放在char c中所以这时又需要截断

c得:10000011(-3)

又因为题目让我们以%d得形式打印,所以先将a整型提升得:11111111111111111111111110000011(signed char 补原符号)

因为负数得原码得算所以(1.对补码取反加1得到原码  2.对补码减1取反也可以                                  )

原码得:10000000000000000000000001111101(-125)

例2:

#include <stdio.h>
int main()
{
    char c = 1;
    printf("%zu\n", sizeof(c));
    printf("%zu\n", sizeof(+c));
    printf("%zu\n", sizeof(-c));
    return 0;
}

1. sizeof(c)

因为c就是char类型的数据所以得到的结果为:1

2. sizeof(+c)/sizeof(-c)

因为c参与了表达式运算,即c进行了整型提升,所以sizeof(+c)/(-c)得:4

1.5 寻常算术转换加操作符优先级表格

(signed) long double

(signed) double

(signed) float

unsigned long int

(signed) long int

unsigned int

(signed) int  //从下往上向大类型转换,表达式求值同一个类型时 signed ——> unsigned

操作符优先级表格

优先级运算符名称/含义使用形式和用法示例结合性说明是否控制求值顺序
1()圆括号 (聚组)(表达式) | (x + y)左 --> 右--
()函数调用函数名(形参表) | add(x, y)--
[ ]下标引用数组名[常量表达式] | array[5]--
.成员选择(对象)对象.成员名 | stu.a--
->用指针访问结构体对象指针.成员名 | stu->a--
2++后置++变量名++ | num++左 --> 右单目运算符
--后置--变量名-- | num--
逻辑非!表达式 | !a右 --> 左
~按位取反(二进制位)~表达式 | ~a
+正号运算符+表达式 | +1
 -负号运算符-表达式 | -1
++前置++表达式++ | num++
--前置--表达式-- | num--
*间接访问(解引用)*指针变量 | *p
&取地址运算符&变量名 | &a
sizeof

求变量的长度,单位字节

以数据类型为准,不计算内部表达式

sizeof(表达式) | sizeof(int)
(类型)强制类型转换(数据类型)表达式 | (char)a
3/表达式/表达式 | a / b左 --> 右双目运算符
*表达式*表达式 | a * b
%余数(取模,两边只能是整数)表达式%表达式 | a % b
4表达式+表达式 | a + b
-表达式-表达式 | a - b
5<<左移变量<<表达式 | a << 1
>>右移变量>>表达式 | a >> 1
6>大于表达式>表达式 | a > b左-->右双目运算符
>=大于等于表达式>=表达式 | a >= b
<小于表达式<表达式 | a < b
<=小于等于表达式<表达式 | a <= b
7==等于表达式==表达式 | a == b
!=不等于表达式!=表达式 | a != b
8&按位与(二进制位)(有0为0,不同为1)表达式&表达式 | a & b左-->右双目运算符
9^按位异或(二进制位)(相同为0,不同为1)表达式^表达式 | a ^ b
10|按位或(二进制位)(有1为1,没1为0)表达式|表达式 | a | b
11&&逻辑与(左边为0,右边断路)表达式&&表达式 | a && b
12||逻辑或(左边为真,右边断路)表达式||表达式 | a || b
13?:条件运算符

表达式1?表达式2: 表达式3

三目运算符
14=赋值变量=表达式 | a = 2右 --> 左双目运算符
/=除等于变量/=表达式 | a /= 2
*=乘等于变量*=表达式 | a *= 2
%=取余等于变量%=表达式 | a %= 2
+=加等于变量+=表达式 | a += 2
-=减等于变量-=表达式 | a -= 2
<<=左移后赋值变量<<=表达式 | a <<= 2
>>=右移后赋值变量>>=表达式 | a >>= 2
&=按位与后赋值变量&=表达式 | a &= 2
^=异或后赋值变量^=表达式 | a ^= 2
|=按位或后赋值变量|=表达式 | a |= 2
15,逗号运算符(表达式都执行,结果为最后一个表达式的值)表达式,表达式,.......左 --> 右--

如果我们写出得表达式不能跟据操作符(运算符)的优先级确定唯一路径,那我们写出的表达式就是有错的

二. 深度刨析数据在内存中的存储

1.1 数据类型介绍

c语言提供的一些基本的内置类型

类型                  开辟内存空间的大小(字节)
char (字符型)                1           
short(短整型)                2
int  (整型)                  4
long (长整型)           32位/64位 4/8
long long (更长的整型)        8
float (单精度浮点型)          4
double (双精度浮点型           8

    

1.2 整型的基本归类

整型家族(int类型在limits.h的头文件中定义):

char(字符型,因为本质是ASCII码值,所以划分到整型家族)

unsigned char    signed char

int 

unsigned int

signed int

short

unsigned short [int]

signed short [int]

long

unsigned long [int]

signed long [int]

long long

unsigned long long [int]

signed long long [int]

int a; --->  signed int a; 是一样的

1.3 unsigned 和 signed 的区别以及用法

例:生活中有些数据是没有负数的如:身高,体重,长度... 像这些数据就可以用unsigned加以修饰

signed int a = 0;//有符号     00000000000000000000000000000000 最左边的0就是符号位(1表示负,0表示正)

unsigned int high = 0;//无符号     00000000000000000000000000000000 此时这个值就没有符号位了最左边的数为有效数加入计算

总结:如果创建的数据没有负数就用unsigned,有负数就用signed

1.4 浮点型的介绍和基本归类

浮点型家族:只要表示小数就可以用浮点型

float的精度低,储存的数值范围小(精确到后六位)

double的精度高,储存数值范围大 (精确到后十五位)

1.5 构造类型:自定义类型 - 用户创建新类型

数组类型   int a[5]  //根据需求可以创建不同的数组
结构体类型 struct
枚举类型   enum
联合类型   union

1.6 指针类型

int* pi;
char* pa;
float* pc;
void* pd;

1.7 空类型

void 表示空类型(无类型)

通常用于函数的放回类型,函数参数,指针参数

void test (void)

函数无返回值,函数无参数,参数表中有void,传参会报警告。无void传参就不会报警告

作为指针类型

void* pa;

2.0 整型在内存中的储存

整数是以补码的二进制序列存于内存中的

正负整数均有原码,反码,补码

正:三码和一(三码一致) 负:三码都不同,所以需要算

算法:原码取反加1就得到补码

为什么整数以补码得形式存在内存中了?

在计算机系统中,数值一律用补码表示和存储,原因在于使用补码可以将符号位和数值域统一处理(CPU只有加法器)此外,补码与原码互相转换其原理是相同的,不需要额外的硬件电路

1+(-1)用补码计算

用8位表示

00000001  +  11111111  得:100000000 多出一位,最高位丢弃为:00000000(0)

2.1 大小端的认识

例:

20的十六进制补码为:0x00000014

-10的十六进制补码为:0xfffffff6

以 20 和 -10 为例它们在内存中以十六进制补码形式存储在内存中,那它们为什么会以这些的顺序存储了,这里就开始进入了大小端的概念

上图低高地址位置随意,不止倒叙和正序,其他存放顺序没意义,所以这里以倒叙和正序为例

十六进制一个数字代表4bit两个数字就是1byte

11 22 33 44 ——>大端字节序存储(正序)

44 33 22 11 ——>小端字节序存储(倒序)

大端字节序存储:把一个数据的高位字节序的内容存放在低地址处,低字节序的内容存放在高地址处

小端字节序存储:把一个数据的高位字节序的内容存放在高地址处,低字节序的内容存放在低地址处

例:百度2015系统工程师笔试题

判断这个机器是大端还是小端:

#include <stdio.h>
int check_sys()
{
    int a = 1;
    return *(char*)&a;
}
int main()
{
    ret = check_sys;
    if(ret)
    {
     printf("小端");
    }
    else
    {
     printf("大端");
    }
    return 0;
}

  • 20
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值