数据的存储(c语言进阶)

数据的存储
数据的存储
学习重点:1.数据类型详细介绍 2.整形类型在数据中的存储:原码,反码,补码3.大小端字节序介绍及判断 4.浮点型在内存中的存储解析
1.;类型的意义:(1)使用这个类型决定了开辟空间的大小,范围
(2)类型的不同决定了内存空间视角的不同
int a=10; float a=10.0//申请的都是4个字节


#include<stdio.h>
int main()
{
  int a = 10; 
  float b = 10.0;
  printf("%p\n %p\n", &a,&b);//打印啊a,b的地址
  //010FF91C int a的地址
  //010FF910 float b 的地址 由此可见类型的不同空间视角不同
  return 0;
}
#include<stdio.h>
int main()
{
  char a[] = {1,2,3,4,5,6,7 };
  //char* p =(char *)&a;//这里因为char类型和int* 类型不兼容所以要强制类型转换
  int* p = (int*)&a;
  printf("%d\n", *a);//这里打印的是数组的首元素
  printf("%d\n", *(a+1));//首元素地址加一,再解引用,表示第二个元素
  printf("%d\n", *(p));
  printf("%p\n", a);
  printf("%p\n", a+1);
  printf("%p\n", p);
  printf("%p\n", p+1);
  //打印结果如下:
  // 1
  /*2
    67305985
    00D5F9B4
    00D5F9B5
    00D5F9B4
    00D5F9B8 由此可以就看出,通过样是首元素加一的地址,但是p加一要比a+1要大四个字节,由此可见不同类型访问的空间大小不相同*/
  //
  return 0;
}
int main()
{
  return 0;
}
整型家族:char类型,char类型在内存中是以ASCLL码值的形式存进去的,ASCLL是整型就相当于字符是以整数的方式表示与储存的,所以归类于整型
xhar 类型,分为无符号(unigned char)与有符号(signed char)其中signed可省略,无符号数没有负数的存在,有符号数分为正数与负数
eg 0101001有符号数有符号位,即最高位为0为正为1表示负数 其他位为 数值位用来计算大小,在无符号数里面,因为没有负数这一说法,所以全部位数都是数值为
那么其他的类型像short ,int,long 情况也是如此!
比如unsinged int long 其中int可以省略

再看看浮点型家族:float(单精度浮点型)double(双精度浮点型) 了解一些,以后会评讲
构造类型:数组类型(自定义类型)int arr[10]这里arr是数组名,其余部分是数组类型 int [12] int [4] 
指针类型 void* p void表示无需返回的类型
void test(void)//表示不需要接受人任何返回类型,也不需要返回任何类型
{
  ;
}
int main()
{
  test();
  return 0;
}
整型在数据存储,变量的创建是要在内存开辟空间的,空间大小由不同类型决定,那么内存中数据是怎么开辟空间呢首先就需要了解计算机有符号数(整数)
三种方法,原反补码(之前在前面讲过了)
符号位有正负数,还有0,无符号为没有负数的存在,
整型是以补码的形式存进内存里去的,再通过转化为十六进制存入

#include<stdio.h>
int main()
{
  //对于有符号数而言
  int* a = (int*)10;
  int* b = a
    ;
  //00000000 00000000 00000000 00001010 正数原反补码都相同哦
  //00 00 00 0A 补码的十六进制十六进制表示就是0x0000000A
  printf("%p\n", a);//0000000A这是最终打印的结果
  //printf("%p\n", b);
  return 0;
}
#include<stdio.h>
int main()
{
  int a = -10;
  //100000000 00000000 00000000 00001010 原码
  //111111111 11111111 11111111 11110101 反码,原码符号位不变,其余全部按位取反
  //111111111 11111111 11111111 11110110 补码,反码+1
  //FF FF FF F6
  printf("%p\n", (int*)a);//打印出来的依然是FFFFFFF6,而且由此看出整型是以补码的形式存进内存里去的

  return 0;
}
总结一下,正数原反补码都一样,负数反码就是原码符号位不变,数值位按位取反,补码是反码加一
整型是以补码的形式存储进入的内存,并且是是十六进制的形式存入
但是为什么存进去的是补码呢,因为在计算机系统使用补码可以将符号位和数值统一处理,同时加法与减法统一处理,cpu只有加法器,此外补码与源码相互转换其原理是相同的,无需额外的硬件电路
由此可以看出,加减法的运算都是靠补码运算的
大小端字节序介绍及判断,当我们通过窗口监视输入a的内存大小时会发现a在计算机系统里存入的内存有的是跟打印出来的顺序是相反的,这是怎么费事呢
原因是数据存入内存时存在着大小端存储,所谓大端存储就是数据低位保存在高地址中,数据高位保存在低地址中(也可以说是低内存)例如-10储存在内存中就是FFFFFFF6
小端存储:数据低位储存在低地址中,数据高位储存在高地址中,eg F6FFFFFF
那么怎么测试编译器是小端存储还是大端存储呢,我们可以写下面这一段代码
为什么会有大小端模式之分呢?这是因为在计算机系统中,我们是以字节为单位的,每个地址单元
都对应着一个字节,一个字节为8
比特就业课
比特就业课 - 专注IT大学生就业的精品课程
bit。但是在C语言中除了8 bit的char之外,还有16 bit的short型,32 bit的long型(要看具体的编
译器),另外,对于位数大于8位
的处理器,例如16位或者32位的处理器,由于寄存器宽度大于一个字节,那么必然存在着一个如
何将多个字节安排的问题。因此就
导致了大端存储模式和小端存储模式。
例如:一个 16bit 的 short 型 x ,在内存中的地址为 0x0010 , x 的值为 0x1122 ,那么 0x11 为
高字节, 0x22 为低字节。对于大端
模式,就将 0x11 放在低地址中,即 0x0010 中, 0x22 放在高地址中,即 0x0011 中。小端模式,
刚好相反。我们常用的 X86 结构是
小端模式,而 KEIL C51 则为大端模式。很多的ARM,DSP都为小端模式。有些ARM处理器还可以
由硬件来选择是大端模式还是小端
模式。
百度2015年系统工程师笔试题:
请简述大端字节序和小端字节序的概念,设计一个小程序来判断当前机器的字节序。(10分)
//代码1
#include <stdio.h>
int check_sys()
{
  int i = 1;
  return (*(char*)&i);
}
int main()
{
  int ret = check_sys();
  if (ret == 1)
  {
    printf("小端\n");
  }
  else
  {
    printf("大端\n");
  }
  return 0;
}
#include<stdio.h>
int main()
{
  int a = 1;//在内存中补码为0x00000001
  char* b = (char*)&a;//前面提到过,char类型一次只能拜访一个字节,并且从低地址开始访问
  //printf("%d\n", *b);
  //printf("%p\n",(char*)*b);
  //printf("%p\n", b);
  if (*b == 1)
  {
    printf("xiaoduan\n");//01000000
  }
  else
  {
    printf("daduan\n");//00000001
  }
  //printf("%p", b);
  return 0;
}
巩固一下,下列输出的结果是多少?
int main()
{
  char a = -1;
  //先假装它是整型
  // 10000000 00000000 00000000 00000001 原码
  // 11111111 11111111 11111111 11111110 反码
  // 11111111 11111111 11111111 11111111 补码
  // 得出char类型补码为11111111(记住整型提升只是补码的整型提升,因为在cpu处理器中运用的是补码)
  //以上步骤只是为了求他的整型类型补码,然后再截断
  //补码整型提升:补符号位补到32位为止相当于求这个数的整型的逆过程
  signed char b = -1;//这个同上,signed char就是char

  unsigned char c = -1;
  // 10000000 00000000 00000000 00000001 原码
  // 11111111 11111111 11111111 11111110 反码
  // 11111111 11111111 11111111 11111111 补码
  //因为无符号类没有负数,所以全部位数都是数值位
  // char c补码为11111111
  //整型提升 00000000 00000000 00000000 11111111 补码,也是原码,以为正数正反补都是一样的
  //所以最后提升的结果为00000000 00000000 00000000 11111111 也就是225
  printf("%d %d %d", a, b, c);//-1.-1.225
  return 0;
}
这里补充一下前面漏掉的知识点;隐式整型提升: C的整型算术运算总是至少以缺省整型类型的精度来进行的。
为了获得这个精度,表达式中的字符和短整型操作数在使用之前被转换为普通整型,这种转换称为整型
提升。
整型提升的意义:
表达式的整型运算要在CPU的相应运算器件内执行,CPU内整型运算器(ALU)的操作数的字节长度
一般就是int的字节长度,同时也是CPU的通用寄存器的长度。
因此,即使两个char类型的相加,在CPU执行时实际上也要先转换为CPU内整型操作数的标准长
度。
通用CPU(general - purpose CPU)是难以直接实现两个8比特字节直接相加运算(虽然机器指令
中可能有这种字节相加指令)。所以,表达式中各种长度可能小于int长度的整型值,都必须先转
换为int或unsigned int,然后才能送入CPU去执行运算。
b和c的值被提升为普通整型,然后再执行加法运算。
};
void set_age1(struct Stu stu)
{
  stu.age = 18;
}
void set_age2(struct Stu* pStu)
{
  pStu->age = 18;//结构成员访问
}
int main()
{
  struct Stu stu;
  struct Stu* pStu = &stu;//结构成员访问
  stu.age = 20;//结构成员访问
  set_age1(stu);
  pStu->age = 20;//结构成员访问
  set_age2(pStu);
  return 0;
}
//实例1
char a, b, c;
...
a = b + c;
比特就业课
比特就业课 - 专注IT大学生就业的精品课程
比特主页:https ://m.cctalk.com/inst/s9yewhfr
加法运算完成之后,结果将被截断,然后再存储于a中。
如何进行整体提升呢?
整形提升是按照变量的数据类型的符号位来提升的
整形提升的例子 :
实例1中的a, b要进行整形提升, 但是c不需要整形提升
a, b整形提升之后, 变成了负数, 所以表达式 a == 0xb6, b == 0xb600 的结果是假, 但是c不发生整形提升, 则表
达式 c == 0xb6000000 的结果是真.
所程序输出的结果是 :
  c
  //负数的整形提升
  char c1 = -1;
变量c1的二进制位(补码)中只有8个比特位:
1111111
因为 char 为有符号的 char
所以整形提升的时候,高位补充符号位,即为1
提升之后的结果是:
11111111111111111111111111111111
//正数的整形提升
char c2 = 1;
变量c2的二进制位(补码)中只有8个比特位:
00000001
因为 char 为有符号的 char
所以整形提升的时候,高位补充符号位,即为0
提升之后的结果是:
00000000000000000000000000000001
//无符号整形提升,高位补0
//实例1
int main()
{
  char a = 0xb6;
  short b = 0xb600;
  int c = 0xb6000000;
  if (a == 0xb6)
    printf("a");
  if (b == 0xb600)
    printf("b");
  if (c == 0xb6000000)
    printf("c");
  return 0;
}
总结一下,关于char类型,有符号数与无符号数的区别与联系。有符号数的首位(符号位是用来判断正负的)1负0真所以有符号数的范围为-128到127
11111111:-128 01111111:127
而无符号全部位都是来计算数值大小的,大小为0-225 即11111111这样就形成了一个闭环
还有一个问题,就是关于溢出的的问题,既然无符号数最大值225即11111111那如果在加上一怎么办呢,那么就会变为零,就相当于假设在再前面一位加上一个最高位进一,但是那个一又不存在所以它的值为零
关于溢出的问题可以参照这里 https://blog.csdn.net/u012782268/article/details/40021887 
接着我们来看下面这组代码:
#include<windows.h>
int main()
{
  unsigned int a = 0;
  for(a=9; a>= 0; a--)
  {
    printf("%u\n", a);//u代表打印无符号数
    Sleep(1000);
  }
  return 0;
}//打印出的结果为 9,8,7,6,5,4,3,2,1,0,4294967295,4294967294,4294967293...........以此无限循环,因为unsigned是没有负数的
那么4294967295是怎么来的呢这时我们通过计算得知4294967295转化为二进制就是11111111 11111111 11111111 11111111 刚好就是无符号溢出哦
再看一个代码
#include<windows.h>
#include<stdio.h>
int main()
{
  char a[1000];
  int i = 0;
  for (i = 0; i <1000; i++)
  {
    a[i] = -1 - i;
    printf(" %d ", a[i]);
    Sleep(5);
  }
  printf("%d", strlen(a));//字符串长度为225,srrlen遇到\0就会停下来
  return 0;
}
打印出结果为-1-2-3-4.........-128.127.126.........0...这里的0表示为\0

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值