C语言-数据的存储-整形的存储(8.1)

简介: C语言-数据的存储-整形的存储(8.1)

思维导图:



1.数据类型的基本归类

1.1类型的意义

C语言中的各种类型,所占的内存空间不同。


而类型的意义,就是在创建类型时


使用这个类型开辟的内存空间


以及看待内存的视角(不同类型在内存中存储的方式是不同的)


1.2整形家族

char
        unsigned char
        signed char
short
        unsigned short [int]
        [signed] short [int]
int
        unsigned int
        [signed] int
long
        unsigned long [int]
        [signed] long [int]


char 也是整形家族的一员!!!


平时我们都直接使用short、int 等等,一般我们习惯将[ ]里 的省略,当然,不省也不会报错。


long long 也是同理



1.3浮点数家族

float

double


1.4构造类型

数组类型


结构体类型 struct

枚举类型 enum

联合类型 union


数组类型也属于构造类型


例:


int arr[10]
  int[10]
  int a[11]
  int[11]

int arr[10] 的类型其实是 int [10]


同理int a[11] 的类型时 int [11]


每个不同的数组都是不同的类型。



1.5指针类型

int *p
char *p
float* p
void* p



1.6空类型

void 表示空类型(无类型)

通常应用于函数的返回类型、函数的参数、指针类型


例:


#include 
void test()
{
  printf("hehe\n");
}
int main()
{
  test();
  return 0;
}

输出:


输出:hehe

2. 整形在内存中的存储

大致了解C语言中的数据类型后,我们重点学习整形家族在内存中的存储。



2.1 原码、反码、补码

一个变量的创建是要在内存中开辟空间的,


而开辟空间的大小是有类型决定的,


那么,数据究竟是如何在内存中存储的呢?


计算机中的整数有三种二进制的表示方法:


即:


1.原码


直接将数值按照正负数的形式翻译成二进制就可以得到原码。


int a = 1;//原码:00000000000000000000000000000001

2.反码


将原码的符号位不变,其他位依次按位取反就可以得到反码。


int b = -1;//原码:10000000000000000000000000000001

           //反码:11111111111111111111111111111110

3.补码


反码+1就得到补码。


int b = -1;//原码:10000000000000000000000000000001

           //反码:11111111111111111111111111111110

     //补码:11111111111111111111111111111111

三种表示方法均有符号位和数值位两部分,符号位都是用0表示“正”,用1表示“负”,


第一位就是符号位,剩下是数值位。


正数的原、反、补码都相同。


我们到内存中看看:



我们发现内存是以补码的形式存储的


而且我们发现a的地址有些奇怪,为什么好像是反着存的,又不完全反着。



2.2 大小端介绍

其实,在计算机中是存在大小端的存储方式的


大端模式,是指数据的低位保存在内存的高地址中,而数据的高位,保存在内存的低地址中;



小端模式,是指数据的低位保存在内存的低地址中,而数据的高位,,保存在内存的高地址中。


大小端字节序存储是以字节为单位存储的:


例:



上文中&a在内存中存储的方式是 01 00 00 00 以字节为单位逆序存储,是小端存储的方式。


那我们该如何判断所处环境究竟是大端还是小段呢?


例1:


#include 
int main()
{
  int a = 1;
  char* pa = (char*)&a;
  if (*pa == 1)//通过观察内存中第一个字节是否为1
  {            //从而判断大小端
  printf("小端\n");
  }
  else
  {
  printf("大端\n");
  }
  return 0;
}


我使用的VS2019是小端环境


输出:

输出:小端

例2:

#include 
int check()
{
  int a = 1;
  return *(char*)&a;//如果是小端返回1
}                     //如果大端则返回0
int main()
{
  int ret = check();
  if (ret == 1)
  {
  printf("小端\n");
  }
  else
  {
  printf("大端\n");
  }
  return 0;
}

当然,我们也可以使用函数的方法实现:


/

输出:


输出:小端


2.3 练习、巩固、提高

练习1:


//输出什么?
#include 
int main()
{
  char a = -1;
  signed char b = -1;//在这里的 signed char b 和 char a 其实是相同的
  unsigned char c = -1;
  //signed char 能够存储的大小是 -127 ~ 128
  //unsigned char 能够存储的大小是 0 ~ 255
  //如果遇到-1:
  //原码:10000000000000000000000000000001
  //反码:11111111111111111111111111111110
  //补码:11111111111111111111111111111111
  //截断:11111111
  //对于无符号类型来说,原返补码相同
  //所以最后的大小为 11111111
  //用十进制输出则为 255
  printf("a=%d,b=%d,c=%d", a, b, c);
  return 0;
}


输出:


输出:a=-1,b=-1,c=255

练习2:


#include 
int main()
{
  char a = -128;
  //原码:10000000000000000000000010000000
  //反码:11111111111111111111111101111111
  //补码:11111111111111111111111110000000
  //截断:10000000
  printf("%u\n", a);
  //通过无符号整形的方式打印
  //整形提升:
  //高位补符号位:11111111111111111111111110000000
  return 0;
}


输出:


输出:4294967168

练习3:



/

#include 
int main()
{
  char a = 128;
  //原码:00000000000000000000000010000000
  //反码:00000000000000000000000010000000
  //补码:00000000000000000000000010000000
  //截断:10000000
  printf("%u\n", a);
  //通过无符号整形的方式打印
  //整形提升:
  //高位补符号位:11111111111111111111111110000000
  return 0;
}

输出:


输出:4294967168

练习4:


#include 
int main()
{
  int i = -20;
  //原码:10000000000000000000000000010100
  //反码:11111111111111111111111111101011
  //补码:11111111111111111111111111101100
  unsigned int j = 10;
  //补码:00000000000000000000000000001010
  //补码相加:11111111111111111111111111110101
  //以整形原码打印:10000000000000000000000000001010
  printf("%d\n", i + j);//转成十进制:-10
  return 0;
}


输出:


输出:-10

练习5:


#include 
int main()
{
  unsigned int i;
  for (i = 9; i >= 0; i--)
  {
  printf("%u ", i);//会先打印 9 8 7 6 5 4 3 2 1 0
  }                    //当-1时,用unsigned int形式打印会进入死循环打印很大的数
  return 0;            //i永远无法小于0
}

输出:



练习6:


#include 
int main()
{
  char a[1000];
  int i;
  for (i = 0; i < 1000; i++)
  {                 //char 类型大小:-127~128
  a[i] = -1 - i;//i从0 1 2...128 -127 -126...-3 -2 到-1时,arr[i]=0
  }                 //总共是255个数
  printf("%d", strlen(a));//strlen 遇到'\0'就会停下
  return 0;
}

输出:


输出:255

练习7:


#include 
unsigned char i = 0;
int main()
{
  for (i = 0; i <= 255; i++)
  {
  printf("hello world\n");//因为 unsigned char 的范围是 0~255
  }                           //所以 i 永远无法大于255,导致死循环
  return 0;
}

输出:


输出:死循环打印 hello world

写在最后:

以上就是本篇文章的内容了,感谢你的阅读。


如果喜欢本文的话,欢迎点赞和评论,写下你的见解。


如果想和我一起学习编程,不妨点个关注,我们一起学习,一同成长。



相关文章
|
2月前
|
存储 编译器 C语言
C语言存储类详解
在 C 语言中,存储类定义了变量的生命周期、作用域和可见性。主要包括:`auto`(默认存储类,块级作用域),`register`(建议存储在寄存器中,作用域同 `auto`,不可取地址),`static`(生命周期贯穿整个程序,局部静态变量在函数间保持值,全局静态变量限于本文件),`extern`(声明变量在其他文件中定义,允许跨文件访问)。此外,`typedef` 用于定义新数据类型名称,提升代码可读性。 示例代码展示了不同存储类变量的使用方式,通过两次调用 `function()` 函数,观察静态变量 `b` 的变化。合理选择存储类可以优化程序性能和内存使用。
150 82
|
21天前
|
存储 C语言 C++
深入C语言,发现多样的数据之枚举和联合体
深入C语言,发现多样的数据之枚举和联合体
深入C语言,发现多样的数据之枚举和联合体
|
21天前
|
存储 C语言
深入C语言内存:数据在内存中的存储
深入C语言内存:数据在内存中的存储
|
22天前
|
C语言
回溯入门题,数据所有排列方式(c语言)
回溯入门题,数据所有排列方式(c语言)
|
2月前
|
存储 人工智能 C语言
数据结构基础详解(C语言): 栈的括号匹配(实战)与栈的表达式求值&&特殊矩阵的压缩存储
本文首先介绍了栈的应用之一——括号匹配,利用栈的特性实现左右括号的匹配检测。接着详细描述了南京理工大学的一道编程题,要求判断输入字符串中的括号是否正确匹配,并给出了完整的代码示例。此外,还探讨了栈在表达式求值中的应用,包括中缀、后缀和前缀表达式的转换与计算方法。最后,文章介绍了矩阵的压缩存储技术,涵盖对称矩阵、三角矩阵及稀疏矩阵的不同压缩存储策略,提高存储效率。
298 8
|
27天前
|
存储 C语言
C语言中的浮点数存储:深入探讨
C语言中的浮点数存储:深入探讨
|
20天前
|
C语言 C++
C语言 之 内存函数
C语言 之 内存函数
30 3
|
11天前
|
存储 缓存 C语言
【c语言】简单的算术操作符、输入输出函数
本文介绍了C语言中的算术操作符、赋值操作符、单目操作符以及输入输出函数 `printf` 和 `scanf` 的基本用法。算术操作符包括加、减、乘、除和求余,其中除法和求余运算有特殊规则。赋值操作符用于给变量赋值,并支持复合赋值。单目操作符包括自增自减、正负号和强制类型转换。输入输出函数 `printf` 和 `scanf` 用于格式化输入和输出,支持多种占位符和格式控制。通过示例代码详细解释了这些操作符和函数的使用方法。
30 10
|
4天前
|
存储 算法 程序员
C语言:库函数
C语言的库函数是预定义的函数,用于执行常见的编程任务,如输入输出、字符串处理、数学运算等。使用库函数可以简化编程工作,提高开发效率。C标准库提供了丰富的函数,满足各种需求。
|
9天前
|
机器学习/深度学习 C语言
【c语言】一篇文章搞懂函数递归
本文详细介绍了函数递归的概念、思想及其限制条件,并通过求阶乘、打印整数每一位和求斐波那契数等实例,展示了递归的应用。递归的核心在于将大问题分解为小问题,但需注意递归可能导致效率低下和栈溢出的问题。文章最后总结了递归的优缺点,提醒读者在实际编程中合理使用递归。
33 7