C语言之整数_数据存储篇(1)

简介: C语言之整数_数据存储篇(1)

数据类型

在初C语言中,我们已经学习过基本内置类型以及它们所占存储空间的大小。我们再回顾一下。

每一个类型所占空间的大小都不一样,这么多丰富的类型,那它们存在的意义是什么呢?


类型的意义:

  • 使用这个类型开辟内存空间的大小(大小决定了使用范围)。
  • 如何看待内存空间的视角。

这样我们就会在合适的场景选择合适的数据类型。


整形家族

char
      unsigned char
      signed char
short
      unsigned short [int]
      signed short [int]
int
      unsigned int
      signed int
long
      unsigned long [int]
      signed long [int]
long long
//关于long long我们酌情使用。
signed     有符号的
unsigned   无符号的

为什么char字符会归为整形家族?

字符在内存中的存储的是字符的ASCII码值,ASCII码值是整形。

signed和unsigned 修饰的整形有什么区别吗?

signed是有符号的;unsigned int 是无符号的。

int a;//== sined int a
signed int a;
unsigned int a;

我们平时在编译器上创建一个变量,相当于创建一个有符号的变量。

当然short   long   long long 均是创建变量相当于创建一个有符号signed 的变量。(除了char)


那char呢?

char是否是相当于signed char?C语言标准并没有规定,取决于编译器。

大部分编译器是,不排除小部分编译器是unsigned int

ASCII码表中规定的ASCII码值的范围是0~127(字符均是正数)。

只有小部分负数是可以存储在 signed char



浮点型家族

float
double
long double
//建议酌情使用

构造类型

//自定义类型
> 数组类型
> 结构体类型 struct
> 枚举类型 enum
> 联合类型 union

指针类型

int *pi;
char *pc;
float* pf;
void* pv;//无具体类型的指针

空类型

void test(void)
//第一个void 表示test函数不会返回任何值
//第二个void 表示test函数没有参数
{
  //
}
int main(void)//
int main(int argc,char* argv[],char* envp[])

整形在内存中的存储(原反补)

一个变量的创建是要在内存中开辟空间的。空间的大小是根据不同的类型而决定的。

NO1.

那接下来我们来谈谈数据在所开辟内存中到底是如何存储的呢?

计算机能够处理的是二进制的数据。

整型和浮点型数据在内存中也是以二进制的形式进行存储的。

整型在内存中存储的是补码的二进制序列。


NO2.

整型的二进制表示形式是怎样的呢?


计算机中的整型有三种二进制表示:原码,反码,补码。


  • 分为有 有符号整数 和 无符号整数。
  • 无符号整数:原码,反码,补码相同。
  • 有符号整数:三种表示方法均有 符号位 和 数值位 两部分。
  • 正数:原码,反码,补码相同。
  • 负数:原码,反码,补码要进行计算的。
  • 符号位:0 表示正
  • 符号位:1 表示负
  • 数值位:正数的原,反,补码相同。
  • 数值位:负数的原,反,补表示方法各不同。


NO3.

signed int 和 unsigned int?

在内存中,对于存储一个整数需要4个字节,也就是32个比特位。


一个整数写出二进制序列的时候,也就是32个比特位。


有符号整数:最高位就是符号位。符号位是1,则表示是负数。符号位是0,则表示是正数。


无符号整数:没有符号位,所有位都是数值位。


同一个数无论是有符号整数,还是无符号整数的 数值均相同。


有符号整数的最高位符号位还是会参与运算。

如下:

#include<stdio.h>
int main()
{
  int a = 10;//== signed int
  //0 0000000 00000000 00000000 00001010
  unsigned int b =10;
    //00000000 00000000 00000000 00001010
  return 0;
}

站在a的角度,第一个0就是符号位。

站在b的角度,第一个0就是数值位。

NO4.

负整数的原反补怎样转化呢?

NO5.

进制间的转化?这里我们不专门去讲解,可以自己学一学。


对于整形来说,数据存放内存中其实存放的是补码。为什么呢?


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

NO6.

#include<stdio.h>
int main()
{
  int a = -10;//== signed int
  //1 0000000 00000000 00000000 00001010
  //1 1111111 11111111 11111111 11110101
  //1111 1111 1111 1111 1111 1111 1111 0110——补码
  //十六进制
  //0x ff ff ff f6
  unsigned int b =-10;
    //10000000 00000000 00000000 00001010
  return 0;
}


整形在内存中是以二进制的补码存储的,在编译器中为了观瞻,是以十六进制的方式去展示给我们,请问为什么是以十六进制倒叙放置的呢?那接下来我们就要介绍我们的大小端字节序存储。

大端小端字节序

关于内存中二进制/十六进制,单位 比特位和字节的转换,如下图。

NO.1

大端小端产生?

int a=0x11223344
//1 2 3
//百位 十位 各位
//高位      低位


根据字节存储来解释。


我们可以有无数中存储方式,只要按照存进去的方式,在需要使用时拿出来按照原来的顺序放置还原。怎么存都可以!


但是为了简便我们只采用了两种存储方式:


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

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

除了字符整型,都是这两种存储方式,char1个字节不需要顺序。

NO.2

什么是大端小端?


NO.3

为什么有大端小端?

NO.4

笔试题:请简述大端字节序和小端字节序的概念,设计一个小程序来判断当前机器的字节序?

根据上图就有如下代码:

#include<stdio.h>
int main()
{
  int a = 1;//
  char* p = (char*)&a;//强制转化
  if (*p == 0)
    printf("大端\n");
  if(*p == 1)
    printf("小端\n");
  return 0;
}
//用函数包装呢?
#include<stdio.h>
int check_sys(void)
{
  int a = 1;
  char* p =(char*) &a;//强制类型转化
  if (*p == 0)
    return 0;
  if (*p == 1)
    return 1;
}
int main()
{
  int ret = check_sys();
  if (ret == 0)
    printf("大端\n");
  if (ret == 1)
    printf("小端\n");
  return 0;
}
//简化
#include<stdio.h>
int check_sys()
{
  int a = 1;
  return *(char*)&a;
}
int main()
{
  int ret = check_sys();
  if (ret == 0)
    printf("大端\n");
  if (ret == 1)
    printf("小端\n");
  return 0;
}

练习题

//以下主要讲解的是char类型的signed 和unsigned 
//可以推广到short/long等等
%d 是十进制的形式打印有符号整数
%u 是十进制的形式打印无符号整数

NO1.

请问下面程序a,b,c分别是多少?

1.
//输出什么?
#include <stdio.h>
int main()
{
  char a= -1;
  signed char b=-1;
  unsigned char c=-1;
  printf("a=%d,b=%d,c=%d",a,b,c);
  return 0;
}
#include <stdio.h>
int main()
{
  char a = -1;
  //有符号的 负数 char类型
  // 10000000 00000000 00000000 00000001原码
  // 11111111 11111111 11111111 11111110反码
  // 11111111 11111111 11111111 11111111补码
  //存储char  11111111补码
  signed char b = -1;
  //有符号的 负数  char类型同上
  //存储char  11111111补码
  unsigned char c = -1;
  //无符号的  负数  char类型
  //无符号——没有符号位的概念
  // 无符号整数一般放置正数,如果放置负数还是按照负数的原反补来计算
  // 10000000 00000000 00000000 00000001原码
  // 01111111 11111111 11111111 11111110反码
  // 01111111 11111111 11111111 11111111补码
  // 存储char 11111111补码
  //存储char  11111111
  printf("a=%d,b=%d,c=%d", a, b, c);
  //%d 是十进制的形式打印有符号整数
  // 即便没有符号,当作有符号去打印
  //整型提升
  // 有符号位提升符号位
  // 无符号位补0,提升0
  // 有符号ab
  //11111111 11111111 11111111 11111111补码
    //10000000 00000000 00000000 00000001原码
  //打印-1
  //b同理-1
  //无符号c
  //1111111
  //00000000 00000000 00000000 11111111补码原码反码
  //正数的原码反码补码相同
  //打印-c
  //225
  return 0;
}

NO2.

请问下面三端程序分别输出什么?

2.
#include <stdio.h>
int main()
{
char a = -128;
printf("%u\n",a);
return 0;
}
//
#include <stdio.h>
int main()
{
char a = 128;
printf("%u\n",a);
return 0;
}
//
#include <stdio.h>
int main()
{
char a = 384;//128+256
printf("%u\n",a);
return 0;
}
//2.
#include <stdio.h>
int main()
{
  char a = -128;
  //有符号整型 char 负数
  //10000000 00000000 00000000 10000000原码
  //11111111 11111111 11111111 01111111反码
  //11111111 11111111 11111111 10000000补码
  //存储a char        10000000
  printf("%u\n", a);
  //%u 是十进制打印无符号整型
  //整型提升——变量的类型(有符号/无符号)
  //1111111 111111111 11111111 10000000补码
  // 打印——看(u/d)——(u_原反补相同/d_正原反补相同_负的计算)
  //%u把a看成无符号整数
  // 1就不是a的符号位了
  //打印
  //无符号整型原反补相同
  //4,294,967,168
  return 0;
}
//
#include <stdio.h>
int main()
{
  char a = 128;
  //原反补相同
  //00000000 00000000 00000000 10000000补码
  //存储     10000000
  printf("%u\n", a);
  //提升     11111111 11111111 11111111 10000000补码
  //打印——看成无符号的——原反补相同
  //所以还是同上
  return 0;
}

经过我们计算和分析,发现上面三段 代码输出的结果是一样的。why?


  • 有符号的char类型(signed char)取值范围:-128~127
  • 无符号的char类型(unsigned char)取值范围:0~225
  • 截断:超过以上的范围,有一部分数据会被截断,只要存储在内存中的数据必须是在以上范围内。

NO3.

请问下面这段代码输出什么?

3.
int i= -20;
unsigned int j = 10;
printf("%d\n", i+j);
//按照补码的形式进行运算,最后格式化成为有符号整数
#include<stdio.h>
int main()
{
int i = -20;
//10000000 00000000 00000000 00010100
//11111111 11111111 11111111 11101011
//11111111 11111111 11111111 11101100补码
unsigned int j = 10;
//00000000 00000000 00000000 00001010原反补相同
printf("%d\n", i + j);
//11111111 11111111 11111111 11110110补码
//10000000 00000000 00000000 00001010原码
//-10
return 0;
}

NO4.

下面这段代码输出什么? unsigned int的范围

4.
unsigned int i;
for(i = 9; i >= 0; i--)
{
printf("%u\n",i);
}

NO5.

下面这段代码输出什么?signed char的范围

5.
int main()
{
char a[1000];
int i;
for(i=0; i<1000; i++)
{
a[i] = -1-i;
}
printf("%d",strlen(a));
return 0;
}

NO6.

下面这段代码输出什么?unsigned char的范围

6.
#include <stdio.h>
unsigned char i = 0;
int main()
{
for(i = 0;i<=255;i++)
{
printf("hello world\n");
}
return 0;
}

总结

有符号整型signed char

无符号整型unsigned int



  • 有符号整型signed char的取值范围:127 ~  -128
  • 无符号整型unsigned char的取值范围:0~255

同理我们可以将以上代码应用于其他整型short/int/long等等。总结出它们的取值范围。特别注意千万别掉进无符号整型的陷阱了!!🆗🆗🆗


例如:short两个字节 16个比特位


  • 有符号整型signed short的取值范围:-32768~32767
  • 无符号整型unsigned short的取值范围:0~65635

同理大家自己动手总结一下整型类型的取值范围。


关于浮点数的存储会在下篇博文讲解,同时也会去总结二进制中的知识和易错混点。


✔✔✔✔最后,感谢大家的阅读,若有错误和不足,欢迎指正。

代码-----------------→【gitee:https://gitee.com/TSQXG

联系-----------------→【邮箱:2784139418@qq.com】

目录
相关文章
|
6月前
|
C语言
【C语言刷题每日一题#牛客网BC6】输入三个整数,输出第二个整数
【C语言刷题每日一题#牛客网BC6】输入三个整数,输出第二个整数
|
6月前
|
C语言
C语言:从键盘读入三个整数,按从大到小输出
C语言:从键盘读入三个整数,按从大到小输出
|
6月前
|
存储 C语言
【C语言刷题系列】求一个数组中两个元素a和b的和最接近整数m
【C语言刷题系列】求一个数组中两个元素a和b的和最接近整数m
|
6月前
|
C语言
【C语言刷题每日一题】——求1到100中包含数字9的整数的个数
【C语言刷题每日一题】——求1到100中包含数字9的整数的个数
|
6月前
|
C语言
C语言---试计算在区间1 到n 的所有整数中,数字x(0 ≤ x ≤ 9)共出现了多少次?
C语言---试计算在区间1 到n 的所有整数中,数字x(0 ≤ x ≤ 9)共出现了多少次?
|
7月前
|
存储 安全 编译器
C语言中的枚举类型与整数常量:差异与处理
C语言中的枚举类型与整数常量:差异与处理
132 2
|
6月前
|
C语言
【C语言刷题系列】交换整数的奇数位和偶数位
【C语言刷题系列】交换整数的奇数位和偶数位
|
6月前
|
存储 C语言
【C语言进阶篇】整数在内存的存储——原码、反码、补码
【C语言进阶篇】整数在内存的存储——原码、反码、补码
|
6月前
|
C语言
【C语言刷题系列】计算整数的二进制位中1的个数 (三种方式)
【C语言刷题系列】计算整数的二进制位中1的个数 (三种方式)
|
6月前
|
C语言
C语言-----计算两个int(32位)整数m和n的二进制表达中,有多少个位(bit)不同?
C语言-----计算两个int(32位)整数m和n的二进制表达中,有多少个位(bit)不同?