目录
一、数组
1.基本概念
是一个固定长度的存储相同数据类型的数据结构,数组中的元素被存储在一段连续的内存空间中。
2.数组的描述
描述应该包含:类型和大小,不能只包含一个,只包含一个就是错误说法。
3.计算数组所占字节个数
使用sizeof()函数,sizeof()函数是用来计算类型或者变量的字节个数,1字节=>8bit。
4.数组下标
数组的下标必须是常量,也必须是整型量。
在这个例子中,n必须是常量,必须是整型量。
const int n = 3;
int ar[n] = {1,2,3};
如果数组下标是变量的话,不同的编译器的情况不同,c99可以允许这种现象,vs2019不通过。一般情况下,我们将数组下标设置为整型常量。
5.数组之间传值
Q:可不可以将一个数组的值给另一个数组?
A:不可以。
在下面代码中,我们可以看到br = ar这一行代码报错,原因是此时的ar代表的是数组第一个元素的地址(首地址),br代表的也是数组第一个元素的地址,由于地址是常量,所以不能够被改变,也就不能进行赋值操作,所以br = ar报错。
Q:数组怎么传值?
A:循环。
int main() { const int n = 5; int ar[n] = { 1,2,3,4,5 }; int br[n]; for (int i = 0; i < n; i++) { br[i] = ar[i]; } }
6.示例代码
示例代码一:
sizeof(stra)用来计算数组的大小,sizeof(stra[0])用来计算数组中元素的字节大小。两者相除得出数组元素的数量。
int main()
{
char stra[] = { 'y','h','p','i','n','g' };
char strb[] = { "yhping" };
int an = sizeof(stra) / sizeof(stra[0]);
int bn = sizeof(strb) / sizeof(strb[0]);
printf("stra_size:%d strb_size:%d", an, bn);
return 0;
}
代码输出的结构为:
Q:为什么这里元素个数不一样?
A:因为对于字符串而言,它的结尾会被加上'\0',所以相对于字符输入,字符串输入多了一个字符'\0'。
示例代码二:
int main()
{
char stra[] = { 'y','h','p','i','n','g' };
char strb[] = { "yhping" };
int an = sizeof(stra) / sizeof(stra[0]);
int bn = sizeof(strb) / sizeof(strb[0]);
//printf("stra_size:%d strb_size:%d", an, bn);
printf("%s\n", stra);
printf("%s\n", strb);
return 0;
}
输出结果如下:
Q:产生该现象的原因是什么?
A:printf("%s",变量),输出时遇到第一个‘\0’时才输出。因为数组没有‘\0’,所以没有办法输出。
7.不同数组的表示
const int ar[5] = {1,2,3,4,5};//常性数组,只可读,不可写,包含5个整型类型的元素
int *par[10] ={NULL};//10个整型类型的指针
解释的方法都是从右向左解释。
数组的定义必须注意两个要素:数组大小和数组类型。
8.数组作为形参
Q:下面代码的形参是数组,它的输出结果为多少?
void func(int br[10])
{
printf("br_size:%d\n",sizeof(br));
}
int main()
{
const int n = 10;
int ar[n] = { 12,23,34,45,56,67,78,89,90,100 };
func(ar);
return 0;
}
A: 在函数 func
中,参数 br
是一个指向整型数组的指针。当你使用 sizeof(br)
来获取 br
的大小时,它会返回指针的大小,而不是数组的大小。在大多数系统上,指针的大小通常是 4 字节或 8 字节,具体取决于系统的架构(32位或64位)。
如果数组作为形参,那么它会退化为指针。
上述代码等价于下面代码:
void func(int *br,int n)
{
printf("br_size:%d\n",sizeof(br));
}
int main()
{
const int n = 10;
int ar[n] = { 12,23,34,45,56,67,78,89,90,100 };
func(ar,n);
return 0;
}
在数组进行函数调用时,并不应该把所有的值传递,而是应该传递地址和数组中元素的个数。这样的话讲问题模型简化,无论其他的影响因素怎样变化,我们自始至终只用传递这两个属性就能找到对应的值。
9.数组的访问
下标方式和指针方式都可以访问数组。只不过下标方式会自动转换成指针方式 。
void func(int *br,int n)
{
printf("br_size:%d\n",sizeof(br));
for (int i = 0; i < n; i++)
{
printf("%d %d\n", br[i],*(br+i));
}
}
int main()
{
const int n = 10;
int ar[n] = { 12,23,34,45,56,67,78,89,90,100 };
func(ar,n);
return 0;
}
数组下标方式访问:br[i]
指针方式访问:*(br+i), 含义是数组的首地址+偏移量
Q:请问可以i[br]这样输出吗?
A:可以,因为会自动转化为*(i+br)的形式,所以无论是br[i]还是i[br],在运行的时候都会转化为*(br+i),输出对应的值。
void func(int *br,int n) { printf("br_size:%d\n",sizeof(br)); for (int i = 0; i < n; i++) { printf("%d %d %d\n", br[i],*(br+i),i[br]); } } int main() { const int n = 10; int ar[n] = { 12,23,34,45,56,67,78,89,90,100 }; func(ar,n); return 0; }
10.Q&A
Q:如果数组定义的长度大于你所初始化的数组元素,数组会怎样存储?
示例代码如下:n=10,数组长度为10,但是集合里只3个元素
const int n = 10; int ar[n] = {1,2,3};
A:此时会将剩余的位置补0。
Q:ar是一个数组,ar++;的表述正确吗?
A: 不正确。ar此时代表的是一个地址,地址不能++。
等价与如下代码:
int a = 10;
&a;
a++;
直接将地址++是错误的。
二、const在C++和C语言的差异
const在C语言和C++中的区别_c语言和c++ 的const的区别_Mr.LeoLu的博客-CSDN博客
在C++和C语言中,他们执行const的方式有一定的差异。
1.C语言的const
用const修饰的变量本质上还是个变量,所以如果对被修饰的变量进行不法修改的话,变量的值是可以被修改的。
示例代码如下:
int main()
{
const int n = 10;//定义一个常变量n = 10
int b = 0;//定义一个整型变量b的值为0
int* ip = (int*)&n;//定义一个可以存储整型类型的指针变量*ip,将n的地址强转成int类型赋给*ip
*ip = 100;//*ip指的是存储地址对应的变量,此时它是变量n,这里想要改变n的值为100
b = n;//再将n的值赋值给b
printf(" n = %d b = %d *ip = %d\n", n, b, *ip);
return 0;
}
编译器得到的结果是
我们可以看到n的值从10被修改到了100,因此,我们需要知道,const修饰的变量的值在C语言中并非是无法修改的,我们可以通过指针来修改该变量的值。
2.C++中的const
在C++中,程序执行时,const修饰的变量是常变量,会直接在使用到该常变量的时候将常变量的值替换到该位置,即使在程序有对常变量值的修改操作,该值实际上也不会发生变化,替换值还是会按照常变量定义处的初始值。
同样运行上述代码:
int main()
{
const int n = 10;
int b = 0;
int* ip = (int*)&n;
*ip = 100;
b = n;
printf(" n = %d b = %d *ip = %d\n", n, b, *ip);
return 0;
}
运行结果如下:
这里我们可以看到C++程序下的n值没有被修改。
在C++中编译器直接将变量的值和变量的符号对应起来,存储到符号表中。在编译时,调用变量符号,会直接将变量的值替换掉该变量符号。
在C++中,使用const修饰的变量不会被分配存储空间,但是在C语言中会被分配空间,因此,在C语言中可以通过指针修改const修饰的变量的值。
3.作业
尝试int类型换成double类型,看看在C语言和C++下有没有差异。
将以下代码在c++和c程序下运行
#include<stdio.h>
int main()
{
const double n = 10;
double b = 0;
double*ip = (double*)&n;
*ip = 100;
b = n;
printf(" n = %lf b = %lf *ip = %lf\n", n, b, *ip);
return 0;
}
C语言下的const运行结果
C++下的运行结果
可以看出,将常变量的类型改变,在C++和C语言下的运行结果仍旧不同。
三、C++编译链接过程
首先.cpp文件生成.i文件,再生成.s文件,.cpp、.i、.s文件都是文本文件。
接下来.s文件生成.obj文件,这是一个二进制文件,也是不能够执行的。
最后生成的.exe文件才能执行。
在.obj到.exe的过程我们称为链接过程。
首先我们需要理解一个概念,就是所有的函数声明都被存放在一块,但是它并不能执行函数的功能,如果想要执行函数的功能就需要将函数声明链接到对应的函数库中。
.obj到.exe的链接过程就是将函数链接到静态库,从而实现函数的功能。这也进一步说明了,为什么.obj文件依旧不能被执行。
四、运算符
操作数是指数据实体,运算符是指各种运算的符号。
只需要一个操作数:单目运算符
需要两个操作数:双目运算符
需要三个操作数:算目运算符
运算符需要记住优先级最高的和最低的。优先级最高的运算符包括:数组下标[ ],圆括号(),点 . 以及 ->。优先级最低的运算符是逗号表达式。
1.最高优先级的运算符
优先级别最高的运算符包括:数组下标[ ],圆括号(),点 . 以及 ->
(1)数组下标
数组下标必须是整数 ,而且必须是常数。
常见的常整型都有:宏常量、枚举、const修饰的常变量以及常数。
(2)圆括号
一般用来写表达式,例如:
for(int i = 0 ; i < 10 ; i ++)
{
//循环体
}
圆括号还会在函数调用时写出形参位置对应的实参。
int add(int a, int b)
{
return a+b;
}
int main()
{
int c = 0;
c= add(2,3);//调用函数时给对应的实参
printf("%d",c)
}
2.最低优先级的运算符
(1)逗号表达式
逗号表达式:优先级最低
表达式,表达式
运行过程从左到右依次运行
a=(1,2,3)
最终把最右边的值给a
总结:提供逗号表达式的目的是为了在C语言要求只能有一个表达式的情况下可以使用两个或多个表达式,换句话说,逗号表达式可以将两个表达式粘贴在一起。
3.单目运算符
凡是单目运算符,都是从右向左进行结合。
数学不要和编程混淆。因为有时候16<age<28可以运行,有些编译器会通过,但是有些会报错,所以不要这样写。
简洁与(截断与)
参与运算的表达式都为真时,结果才为真,否则为假。
常考题如下:问输出的结果是多少?
结果为3,因为a>b为假,所以不执行后面的++c操作,所以c值不变。
简洁或(截断或)
参与运算的表达式只要有一个为真,结果就为真;两个表达式都为假,才是假的。
4.三目运算符
在C语言中唯一一个三目运算符?::
在下面示例中,它表达的意思是:
如果a>b的表达式为真,就把a的值赋给c,否则,把b的值赋给c。
5.取余运算符
(1)概念
在C++和许多其他编程语言中,取余运算符(Modulus Operator)用于计算一个数除以另一个数后的余数。在C++中,取余运算符使用百分号(%)来表示。
(2)应用
取余运算在C语言中的应用:
①判断某个数能否被整除
②判断奇偶性,判断素数或质数
③计算一个范围。形成循环,如例2
④求最大公约数
(3)例题
①判断是否是闰年
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<math.h>
int main()
{
int year = 0;
bool tag = false;
scanf("%d", &year);
if (year % 4 == 0 && year % 100 == 0 || year % 400 == 0)
{
tag = true;
printf("%s", "yes");
}
else
{
printf("%s", "no");
}
return 0;
}
输出结果如下:
②输出'A-Z'到'Z-A'
要求最终控制台输出的结果如下所示:
代码如下:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<math.h>
int main()
{
const int n = 27;//取27的原因是,最后一个位置要存储空字符串
char str[n] = { "ABCDEFGHIGHLMNOPQRSTUVWXYZ" };
for (int i = 0; i < n; i++)
{
int k = i;
for (int j = 0;j < n; j++)
{
printf("%c", str[k]);
k = (k + 1) % n;
}
printf("\n");//每一行遍历完,换行
}
}
第一次遍历是确定有多少行,第二次遍历是确定每一行有多少元素,关键代码为k=(k+1)%n,这行代码的实现效果可以对应输出后面的元素。例如,k=23,也就是w,k+1=24,再对26取余,还是等于24,所以再次被遍历输出的下一个元素就是k=24的X啦!简单理解,就是没有到一个回合,继续向下读取元素。
输出结果如下所示:
(4)取余和取模
Q:为什么C语言中的运算叫取余,其他语言会把这种运算叫做取模,二者有什么差异?
A:C语言中的 %
运算符执行的操作与数学中的余数运算一致。例如,如果你计算 10 % 3
,结果将是 1
,因为 10
除以 3
的余数是 1
。这与数学中的余数运算一致。
而"取模"运算通常用来确保结果为正数,不管被除数是正数还是负数。某些编程语言中的取模运算符(如Python中的 %
)确保结果始终为正数。
例如,Python 中的 (-10) % 3
结果为 2
,而不是 -1
。这种行为使得取模运算更适合某些数学和算法应用。
Q:震惊:为什么(-10) % 3
结果为 2
A:符号问题:在Python中,取模运算的规则是:a % b
的结果始终具有与 b
相同的符号。也就是说,结果的符号与除数 b
的符号相同。
数值原因:因为Python在除法运算中是向上取整的。
C语言中,10 % (-3)= 1 的计算结果如下:r = 10 - (10 / -3) * -3 = 10 - (-3 x -3) = 1
Python中,10 % (-3)= -2 的计算结果如下:r = 10 - (10 / -3) * -3 = 10 - (-4 x -3) = -2
主要原因是:(10 / -3) 向上取整,使其等于-4。
(5)作业
求最大公约数,求最大公约数的常见方法是欧几里得算法,又称辗转相除法,其计算原理依赖于定理:gcd(a,b) = gcd(b,a mod b)。
#include <stdio.h>
int gcd(int a, int b) {
if (b == 0) {
return a; // 当b为0时,a就是最大公约数
} else {
return gcd(b, a % b); // 否则,递归调用gcd函数
}
}
int main() {
int num1, num2;
printf("请输入两个整数:");
scanf("%d %d", &num1, &num2);
int result = gcd(num1, num2);
printf("最大公约数是:%d\n", result);
return 0;
}
输出结果为:
6.运算符优先级
优先级如下:不需要刻意记忆,直接按照正常思维做就好了。
五、左值、右值、将亡值
1.左值
能够取地址的就是左值。
下面的例子中,const的修饰的变量是可以取地址的,所以被称为左值。
const int n = 10;
&n;
2.右值
不能取地址的就是右值 。
下面的示例尝试对常数取地址,常数没有地址,只有变量才有地址。
&10;
3.将亡值
只存在在计算过程中,计算结束就会消亡。在C++中,将亡值(Rvalue)是一种表示临时对象或表达式的值,通常用于右值引用(Rvalue reference)的上下文中。将亡值通常是一些临时生成的中间结果,其生命周期通常很短暂,可以被移动而不是复制。
计算机的内置类型有:int,char,long,short……,凡是由内置类型产生的将亡值,都是右值,不可被取地址,不可被改变。
(1)用户自定义数据类型
与内置类型相对的是用户自己定义的值:
除了C语言中的内置基本数据类型(整数、浮点数、字符等),C语言还提供了一些其他的数据类型和数据结构,这些类型和结构通常需要通过用户定义来创建。以下是一些常见的C语言中的其他数据类型和数据结构:
数组:数组是一组相同类型的元素的集合,可以通过下标访问每个元素。例如,
int myArray[5]
定义了一个包含5个整数的数组。指针:指针是变量,用于存储其他变量的内存地址。指针允许对内存进行间接访问。例如,
int *ptr
定义了一个指向整数的指针。结构体:结构体是一种用户定义的数据类型,可以包含多个不同类型的成员变量。结构体允许将多个相关数据项组合成一个单独的数据结构。例如,
struct Point { int x; int y; }
定义了一个表示二维点的结构体。联合体:联合体是一种数据类型,允许在相同的内存位置存储不同类型的数据。与结构体不同,联合体的成员共享相同的内存空间,只能同时包含一个成员值。例如,
union Data { int i; float f; }
定义了一个整数或浮点数的联合体。枚举:枚举是一种用户定义的数据类型,用于创建具有一组可能值的类型。例如,
enum Days { SUN, MON, TUE, WED, THU, FRI, SAT }
定义了一个表示星期几的枚举类型。指向函数的指针:C语言允许创建指向函数的指针,这使得可以在运行时动态选择要调用的函数。例如,
int (*funcPtr)(int, int)
定义了一个指向接受两个整数参数并返回整数的函数的指针。typedef:
typedef
关键字允许用户为已有的数据类型创建新的名称,以增加代码的可读性和可维护性。例如,typedef int Length;
可以为int
创建一个新的名称Length
。位字段:位字段是一种用于表示数据的特殊数据类型,允许将多个位字段组合成一个整数,以节省内存空间。例如,
struct Flags { unsigned int flag1: 1; unsigned int flag2: 1; }
定义了一个包含两个位字段的结构体。
举例说明:在a+b运算过程时,10 + 20-->30存储的这30就是一个将亡值,是个常量,是不能取地址的,也就是说它是右值。
int a =10,b = 20;
int c = 0;
c = a+ b ;
这个例子告诉我们为什么在C语言中不能写a+b = c,因为在运算过程时,a+b会产生一个将亡值,不能被更改,所以无法将c的值给他们。
(2)右值引用
右值引用是C++11引入的一个重要特性,用于处理将亡值(Rvalue)和支持移动语义。它可以显著提高程序的性能,特别是在处理大量数据或大型数据结构时。通过有效地管理资源,可以避免不必要的资源分配和释放开销。
&&x
表示一个名为 x
的变量,它是一个右值引用。右值引用可以绑定到将亡值,也可以用于实现移动构造函数和移动赋值运算符,以避免不必要的数据复制,从而提高程序性能。
以下是一个示例,演示如何使用右值引用:
int main() {
int a = 42; // 普通整数变量
int &&b = 10; // 右值引用绑定到将亡值 10
int c = std::move(b); // 使用 std::move 将将亡值 b 移动给 c
std::cout << "a: " << a << std::endl; // 输出 a 的值
std::cout << "b: " << b << std::endl; // 输出 b 的值
std::cout << "c: " << c << std::endl; // 输出 c 的值
return 0;
}
在这个示例中,b
是一个右值引用,绑定到将亡值 10
。然后,使用 std::move
将 b
的值移动给 c
,而不是复制它。这可以提高性能,特别是当操作大型数据结构时。在移动之后,b
不再拥有有效的值。
当使用右值引用移动数据时,源对象的资源通常会被"窃取",不会占据额外的数据空间,因为没有发生数据的复制。在C++中,通常会有大量的对象复制操作,这可能会导致性能问题,尤其是在处理大型数据结构时。右值引用引入了移动语义,允许将资源的所有权从一个对象转移到另一个对象,而不进行数据复制。
六、指针
1.概念
是一种用于存储和管理内存地址的变量类型。指针一定要记住,它有两个值,一个是自身的值,一个是所指之物的值。指针所指之物的值,也被称为解引用。
int *ip:开辟一个可以用来存储int类型地址的指针变量,变量名称为ip
*ip:指的是在ip指针下存储的地址所对应的变量 (*ip是指存储地址对应的变量 )
2.符号 * 的几种用法
①乘法
②修饰指针变量
③单目运算符:指针
3.代码示例
示例一:
int main()
{
int a = 10,b = 20;
int *ip = NULL;
ip =&a;
*ip = 100;
ip = &b;
*ip = 200;
return 0;
}
上述代码主要进行的操作为:
第一行代码:定义两个整型类型的变量a和b,a = 10,b=20。
第二行代码:定义一个可以存储整型地址的指针变量ip。
第三行代码:将a的值给变量ip。
第四行代码:*ip指的是a,将100赋值给a。
第五行代码:此时将b的地址存储到ip指针变量下。
第六行:此时的*ip为b,将200赋值给b。
输出:此时a = 100,b = 200。
几个概念
&*ip=>&b:*ip指的是b,再加上&,就是&b
*&ip=>ip:&ip是ip的地址,ip地址对应的变量还是ip
测试下面代码是否可以执行
&*ip = &a;
*&ip=&a;
第一个不能通过,左边是个地址,地址是个常量,只有变量可以被赋值。
第二个可以通过,左边是个变量,其实就是ip变量,可以把a地址给左边变量
示例二:
int main()
{
int a = 10, b = 20;
int* ap = &a;
int* bp = &b;
if (ap > bp)
{
}
if (*ap > *bp)
{
}
}
上述代码中,第一个if比的是a的地址和b的地址。第二个if比的是a和b两个变量的值。因为*ap指的是a,*bp指的是b。ap指的是这个指针变量内部存储的地址,bp也一样存储的b的地址。
上述代码也告诉我们,首先要明确指针自身的值,才能明确指针所指向的值。也就是说,首先要知道指针变量里存储的地址是谁,才能知道地址指向的变量。
4.指针为空判断
在指针使用时,我们需要判断指针是否为空。
判断方式有两种,一种是直接通过if语句判断,另外一种是通过断言的方式判断。
断言
1.概念
C语言中的断言是一种用于在程序运行时检查条件是否满足的机制。
如果 assert()中止了程序, 它会显示失败的测试、 包含测试的文件名和行号。
C语言中的断言由<assert.h>
头文件提供支持。
#include <assert.h>
assert(expression);
断言表达式是bool表达式,为真向下执行,为假不执行,为假会终止程序,并且提示。
2.示例代码
#include<assert.h>
int main()
{
int x = -5;
assert(x > 0);
printf("x is positive.\n");
}
3.注意事项
断言只在debug版本起作用,release版本不起作用。
debug是调试版本,不会对代码进行优化。
release是交付版本,会对代码进行优化。
5.指针的地址大小
x86地址(32位):4字节
64位地址:8字节
七、地址
举两个例子:
有一栋宿舍,房号依次为301、302--305,房号相加没有任何意义。
有两个人的出生日期,将他们相加也没有任何意义。
但是房号相减有意义,可以知道中间有几间房,日期相减也有意义,可以知道相差几年、几月、几日。所以地址也是一样,相加没有任何意义,但是相减可以知道中间相差几个存储空间。因此,我们说地址不能相加,但是可以相减。
1.使用%p输出地址
当使用 %x
格式化字符串来输出内存地址时,输出的位数可能会受到影响,具体取决于操作系统和编译器的架构。在不同的架构下,内存地址的位数可能会有所不同。
在x86架构下,一个内存地址通常是32位的,因此 %x
可以正常地将这个32位的内存地址显示为16进制数,不会丢失信息。
但在64位架构下,一个内存地址通常是64位的,超过了 %x
的默认宽度。因此,如果你使用 %x
来输出64位地址,可能只会显示其中的一部分,这会导致信息丢失。
因为4个2进制占16进制的一个字符,%x只能表示8个16进制字符,也就是32位2进制,但是一般64位系统地址是64位2进制表示的,超出了%x的默认长度,因此使用%x输出地址会显示不全,示例如下所示:
为了正确地显示64位地址,你可以使用 %p
,这将会显示完整的64位地址。
2.小端存放
小端存放中,最低有效字节(即最右边的字节)存储在最低内存地址处,而最高有效字节(即最左边的字节)存储在最高内存地址处。
inter处理器使用小端存放,而且将低地址作为首地址,作为地址存放。(因为四个字节会被分配4个存储空间,都会有地址,但是只取低地址作为地址存放。)
八、计算机的存储设备
计算机的处理数据的方式:计算机从磁盘读取到内存,CPU在内存里读取和计算,原因内存读取速度快,如果直接在磁盘处理的话,比较慢,CPU将长期处于闲置状态。
寄存器、高速缓存、内存和磁盘都是计算机系统中用于存储数据的不同层次的存储设备,它们按照存取速度和容量从高到低排列。越往上速度越快,价格越贵,空间越小。
每个层次都有不同的特点和用途。
寄存器(Registers):
- 寄存器是位于CPU内部的非常小型的存储单元,用于存储临时数据和执行计算。
- 寄存器的访问速度非常快,通常比其他存储设备快几个数量级。
- 寄存器的数量非常有限,通常只有几十个,用于存储CPU指令和数据。
高速缓存(Cache):
- 高速缓存是位于CPU和主内存之间的中间层次存储器,用于存储最近使用的数据和指令。
- 高速缓存的目标是加速对内存的访问,因为它的访问速度介于寄存器和内存之间。
- 高速缓存通常分为多个级别(L1、L2、L3),根据距离CPU的远近以及容量大小不同。
主内存(Memory):
- 主内存是计算机中的主要存储设备,用于存储程序代码和数据。
- 主内存的容量通常较大,可以容纳程序的所有数据和指令。
- 主内存的访问速度介于高速缓存和磁盘之间,通常以纳秒级别计算访问时间。
磁盘(Disk):
- 磁盘是计算机的永久性存储设备,用于存储操作系统、应用程序、文件和数据。
- 磁盘的容量通常非常大,可以存储大量的数据。
- 磁盘的访问速度相对较慢,通常以毫秒级别计算访问时间。
这些存储设备在计算机系统中协同工作,构成存储层次结构。寄存器和高速缓存用于提供快速的数据访问,以满足CPU的要求。主内存用于存储正在运行的程序和数据。磁盘用于永久性存储,以便长期保存数据。
在计算机系统中,数据通常从磁盘加载到主内存,然后从主内存加载到高速缓存和寄存器,以供CPU进行处理。这个层次结构的设计旨在实现高性能和有效的数据访问。
我们需要记住数据存储容量单位的常见定义:
1K = 2^10
1M = 2^20
1G = 2^30
1T = 2^40