1、 字符串数组打印(指针的步长)1.1 指针变量 1char *p = NULL;
printf('%d\n',p); // 0
printf('%d\n',p 1); // 1
int *p2 = NULL;
printf('%d\n',p2); // 0
printf('%d\n',p2 1); // 4
1.2 字符串数组的步长
则显示'gh' 为什么呢: 首先要知道存放的是char* 类型的数组,所以str 4 也就是数组的第5个元素:“ij”,最后得到的是第5个元素的首地址,我们在去数组的[-1]索引也就是“gh”的首地址,最终打印的就是gh 1.3 跨行加⭐⭐⭐⭐⭐main()
{
//例子[1]
int a[5]={1,2,3,4,5};
int *ptr=(int *)(&a 1);//&a相当于变成了行指针,加1则变成了下一行首地址
printf('%d,%d,%d',*(a 1),*(ptr-1));
//例子[2]
int * ptr1 = (int *)( (int)a 1);
int * ptr2 = (int *)( (int)a 4);
printf('%d,%d\n', ptr[-1],*ptr2);
}
例子[1]
例子[2]首先看里面的语句 (int)a 1 、(int)a 4
这个意思是我们把a的地址得到,然后把a的地址 1和 4,
那么ptr1肯定是一个乱的值,因为取的是不对的地址
而ptr2得到的是数组第二个元素的地址,再转换成(int *)类型
这样我们又可以进行后续的 -操作进行指针的引用了。
2、大端小端小端:低位字节数据存储在低地址 3、异步IO和同步IO区别如果是同步IO,当一个IO操作执行时,应用程序必须等待,直到此IO执行完,相反,异步IO操作在后台运行, 4、变量a的不同定义一个整型数 int a; 一个有10个指针的数组,该指针指向一个函数,该函数有一个整型数参数并返回一个整型 int (*a[10])(int); 5、关于char越界的数值
返回值为2;因为i=c=-128;如果c=0x7f,则i=c=127 6、利用移位、与实现模a=b*2;a=b/4;a=b%8;a=b/8*8 b%4;a=b*15;实现效率最高的算法
a=b*2 -> a=b<<1;
a=b/4 -> a=b>>2;
a=b%8 -> a=b&7;
a=b/8*8 b%4 -> a=((b>>3)<<3) (b&3)
a=b*15 -> a=(b<<4)-b
7、无符号与有符号相加结果为无符号类型
c=1,但a b=-14;如果a为int类型则c=0。 如果a = 1,b = -2 结果为2^32 -1 8、实现某一位置0或置1操作,保持其它位不变 #define BIT3 (0x1<<3)
static int a;
void set_bit3(void)
{
a |= BIT3;
}
void clear_bit3(void)
{
a &= ~BIT3;
}
实现多位置1与置0
a &= ~( 1 << 3 | 1 << 4); //置0
a |= (1<< 3 | 1 << 4); //置1
9、设置一绝对地址为0x67a9的整型变量的值为0xaa66
10、中断函数中的注意问题__interrupt void compute_area (void)
{
double area = PI * radius * radius;
printf(' Area = %f', area);
return area;
}
1、 ISR不可能有参数和返回值的! 10.1 什么是不可重入函数
不可重入函数在实现时候通常使用了全局的资源,在多线程的环境下,如果没有很好的处理数据保护和互斥访问,就会发生错误,常见的不可重入函数有:
10.2 如何写出可重入的函数?⭐⭐⭐⭐⭐
11、关于malloc申请大小问题
malloc申请一段长度为0的空间,malloc依然会返回一段地址,还有一段地址空间,所以ptr不等于NULL。 结果如下图所示 这个阈值会随着编译器的不同而不同 如果申请一个负数,那么返回的是0,如下图 这是因为malloc规定不可以申请一个负数 12、变量全置0与全置1unsigned int zero = 0;
unsigned int compzero = 0xFFFF;
对于一个int型不是16位的处理器为说,上面的代码是不正确的。应编写如下: 13、实现strcpy已知strcpy函数的原型是: char * strcpy(char * strDest,const char * strSrc); 1.不调用库函数,实现strcpy函数。 2.解释为什么要返回char *。 13.1 代码实现
错误的做法[1]: (A) 不检查指针的有效性,说明答题者不注重代码的健壮性。 (B) 检查指针的有效性时使用((!strDest)||(! strSrc))或(!(strDest&&strSrc)),说明答题者对C语言中类型的隐式转换没有深刻认识。在本例中char *转换为bool即是类型隐式转换,这种功能虽然灵活,但更多的是导致出错概率增大和维护成本升高。所以C 专门增加了bool、true、false三个关键字以提供更安全的条件表达式。 © 检查指针的有效性时使用((strDest0)||(strSrc0)),说明答题者不知道使用常量的好处。直接使用字面常量(如本例中的0)会减少程序的可维护性。0虽然简单,但程序中可能出现很多处对指针的检查,万一出现笔误,编译器不能发现,生成的程序内含逻辑错误,很难排除。而使用NULL代替0,如果出现拼写错误,编译器就会检查出来。 错误的做法[3]: (A)忘记保存原始的strDest值,说明答题者逻辑思维不严密。 错误的做法[4]: (A)循环写成while ( * strDestCopy = * strSrc );,同[1](B)。 (B)循环写成while (*strSrc!=’\0’) * strDest = * strSrc ;,说明答题者对边界条件的检查不力。循环体结束后,strDest字符串的末尾没有正确地加上’\0’。 13.2 strcpy能把strSrc的内容复制到strDest,为什么还要char *类型的返回值?返回strDest的原始值使函数能够支持链式表达式,增加了函数的“附加值”。同样功能的函数,如果能合理地提高的可用性,自然就更加理想。 链式表达式的形式如: int iLength=strlen(strcpy(strA,strB)); 又如: char * strA=strcpy(new char[10],strB); 14、写一个“标准”宏MIN,这个宏输入两个参数并返回较小的一个#define Min(a,b) (a) <= (b) ? (a) : (b)
15、说明关键字volatile有什么含意,并给出例子volatile表示被修饰的符号是易变的。告诉编译器不要随便优化我的代码!! 15.1 实现硬件寄存器15.2 中断中用到的变量15.3 线程之间共享变量16、位反转实现一个8位数据反转
unsigned char bit_reverse(unsigned char c)
{
unsigned char buf;
int bit = 8;
while(bit)
{
bit--;//最后需要移动0位,所以要先bit--,最后才能到0,如果在后面最后0的时候就会退出了
buf |= ( (c&1) << bit);
c >>= 1;
}
return buf;
}
17、字符串翻转18、引用和指针的区别(1). 指针是一个实体,而引用仅是个别名; 19、-1,2,7,28,126请问28和126中间那个数是什么?为什么?答案应该是4^3-1=63 规律是n3-1(当n为偶数0,2,4)n3 1(当n为奇数1,3,5) 20、一级指针无效传参先看这个例子: 进入test02后栈上给s分配了一个空间,然后进入getstring,首先常量区给helloworld开辟了空间,然后str这个局部变量,在栈上也存放了helloworld内容,最后返回str指针的地址0x002,但是栈区的空间在执行完getstring后已经被回收了,所以打印的是乱码的东西。 答:结果可能是乱码。因为getstring返回的是指向“栈内存”的指针,该指针的地址不是 NULL,
结果为0; 同理这里进入allo函数后函数为char *p形参在栈上分配空间,然后p指向堆中一块内存,再把helloworld内容拷到p指向内存的地址,但是这个函数执行完后,p内容处的内容就释放掉了,并不会打印。 解决方法:改用二级指针或者返回char*21、写出float x 与“零值”比较的if语句if(x>0.000001&&x<-0.000001) 22、修改野指针void Test(void)
{
char *str = (char *) malloc(100);
strcpy(str, “hello”);
free(str);
if(str != NULL)
{
strcpy(str, “world”);
printf(str);
}
}
篡改动态内存区的内容,后果难以预料,非常危险。因为free(str);之后,str成为野指针, if(str != NULL)语句不起作用。 野指针不是NULL指针,是指向被释放的或者访问受限内存指针。 造成原因:指针变量没有被初始化,任何刚创建的指针不会自动成为NULL; 23、void* 类型任何数据都是有类型的,告诉编译器分配多少内存 void val; //错误的 但是void* 可以 ,四个字节 void* 是所有类型指针的祖宗
从int* 到char* 系统会警告,可以使用类型转换: int * pInt = NULL;
char *PChar = (char *)pInt;
或者用void * 接收
任何类型的指针,都可以不经过强制转换。转换成void* 类型,所以可以理解为void* 是所有类型指针的祖宗。 void * 主要用于数据结构的封装 24、sizeof的返回类型(0)sizeof是操作符,sizeof测量的实体大小在编译阶段就已经确定 (1)sizeof返回的是unsigned int 所以 sizeof(int) - 5 其实结果是 > 0的
(2)当数组作为参数传入的时候,退化为首元素的指针 结果为28,4 25、typedef的作用26、浮点数首先我们如何表示一个浮点数,例子: 1011.01 8 4 2 1 . 0.5 0.25 按照这个规则,得到上面二进制的结果为11.25 单精度一共32位,我们也可以按照这个规律去定义 其中最高位为1为S符号位,中间8位为指数(E),表示小数点在第几位上,后面23位小数(M)表示二进制
结果为: 浮点数:0X41360000 0100 0001 0011 0110 000 0 0000 0000 0000 M = 011 0110 0000 0000 0000 0000 ,E = 100 0001 0
double类型: 27、i 操作27.1 i 的操作顺序
c = 12; 优先级比 高,所以先 后 27.2 可以 &(i ) 吗?为什么见:34 28、什么是左值与右值左值就是出现在表达式左边的值(等号左边),可以被改变,他是存储数据值的那块内存的地址,也称为变量的地址; 右值是指存储在某内存地址中的数据,也称为变量的数据。 左值可以作为右值,但右值不可以是左值。 因此也只有左值才能被取地址。 一句话概括就是:左值就是可以被寻址的值 28.1 举例:int i = 0;
(i ) =i; //错误
( i) =i; //正确
int *ip = &(i ); //错误
int *ip = &( i); //正确
28.2 i 为什么不能作为左值我们来看i 和i 的实现
//后缀形式:
const int int::operator (int) //函数返回值是一个非左值型的,与前缀形式的差别所在。
{//函数带参,说明有另外的空间开辟
int oldValue = *this; // 取回值
(*this); // 增加
return oldValue; // 返回被取回的值
}
简单得到理解,就是i 返回的是一个临时变量,函数返回后不能被寻址得到,它只是一个数据值,而非地址,因此不能作为左值。 更简单的代码解释
C 部分1、C 异常抛出异常throw 和 捕获异常try catch try 块中的代码有可能抛出异常。它后面通常跟着一个或多个catch 块在想要处理问题的地方,通过异常处理程序捕获异常 foo()
{
//[1]
...
...
.A.
.B.
//[2]
throw
//[3]
...
...
...
//[4]
}
如果在throw处发生了异常,会干两件事: 1、程序首先会回到foo()函数的调用处,[3]底下的代码都不去运行了, 2、上面[1] - [2]之前的代码都会进行清理,一个回转的操作,如果调用了A或B,则回去调用A和B的析构函数,避免资源泄露 也可以理解成代码调转到[4]上结束运行这个函数,栈上的数据都销毁把A和B进行析构。 代码示例:通过抛出异常返回一个 字符串'my exception' 在catch中捕捉到了这个字符串打印输出。 不可以滥用异常,销毁数据会造成性能消耗!只有你实在没有办法解决的时候,比如一个除法,分母为0的时候,我不知道该怎么,我不能返回0和-1,因为结果并不是0和-1,我就可以抛出一个异常 const *str的字符串,并且你可以catch这个const *str 2、引用2.1 什么是引用?别名:同样的内存地址 2.2 引用 VS 指针引用的内存意义:同样的内存地址!!! 而指针是int i = 5; int * p = & i; 明显需要开辟一个新的地址存放 i 的地址,指向 i 主要有三个不同: 1、不存在空引用!引用必须连接到一块合法的地址 2、一旦引用被初始化为一个对象,就不能被指向到另一个对象。指针可以在任何时候指向到另一个对象。 3、引用必须在创建时被初始化。指针可以在任何时间被初始化。 2.3 函数传参用到引用
执行到[1] - [2]之间的时候,堆栈上为 a ,b分配空间,c指向a ,运行到foo()后,堆栈会继续分配一个a的空间,以及一个b的空间,而c并不占空堆栈空间,直接指向了第一次堆栈上为a分配空间的地址2000上 堆栈:
2.4 引用的优点和需要遵守的规则将“引用”作为函数返回值类型的格式、好处和需要遵守的规则? string& foo()
{
string* str = new string('abc');
return *str;
}
void main()
{
//[1] 不会内存泄漏
string& str1=foo();
delete &str1;
//[2] 内存泄漏
string str = foo();
}
str1是局部变量、出了作用域就没了。你申请的内存还在、但是“载体”没了。引用是“载体”的引用、 它本身不是“载体” 3.可以返回类成员的引用,但最好是const 3、 C 函数中的默认值
4、register存储类register定义存储在寄存器中而不是内存中的局部变量(只是希望而已) 然而,完全取决于编译器! Best Practice:主要为了优化性能。注意,不要过早优化不要因为C 可以做什么就做什么! 优化什么呢:我想频繁的使用这个变量,从寄存器里面读肯定会快! 但是也有问题: register int b;
int * a = & b;
这个时候b肯定不能放在寄存器里面,因为你想取地址b,寄存器哪有地址,编译器就不会这么做。 5、内联函数inline如何一个函数比较小巧,经常调用,可以减少函数调用用到堆栈的开销 优点:
缺点:
6、const和指针C 之父推荐的读法是:从右往左读 6.1 char * const cp;
6.2 const char * p;//example 2:
const char * p
// cp is pointer to cosnt char 说明他是一个指针,指向的内容为 const char
// 因此它的指针指向的内容是不可以更改的,如果 *p = x;
//试图将 p的指针指向的内容进行修改这是不可以的,因为它不可以修改。
6.3 char const * p;char const跟 const char是一样的
6.4 char const * const p;//example 3:
const char * p
// cp is a const pointer to cosnt char
//说明他是一个cosnt指针,指向的内容为 const char
7、构造函数1、一个构造函数包含构造函数和析构函数这是最基本的 2、构造函数没有返回值,构造函数初始化成员在后面用:给成员初始化(使用初始化列表的构造函数是显式的初始化类的成员),而不是{ sz = size};在构造函数里面,因为如果这个数据类型比较大的话,开销就大了。
3、构造函数可以有很多个,但是会有一个默认的构造函数 Vector(){}
explicit构造函数解决隐式类型转换问题
initializer_list的用法Vector(initializer_list<double> lst)
:elem(new double[lst.size()])
{
std::copy(lst.begin(),lst.end(),elem);
}
Vector v2 {1,2,3,4,5}; //写一个这样的构造函数就可以实现使用列表初始化
8、拷贝构造函数接着7中的例子: 8.1 浅拷贝
此时v2 = v1; 在栈中v2和v1分别在两个地方保存, v1中st和v2中的st存在于不同的地址空间,但是指针他们指向的确实同一块空间。 8.2 实现拷贝构造函数classname (const classname &obj){...}
//在...中实现自己的拷贝构造函数
如何去做: 首先建立对象,并调用其构造函数,然后成员被拷贝。 用A初始化B的完成方式是内存拷贝,复制所有成员的值 指针虽然复制了,但所指向的空间并没有复制,而是由两个对象共用了
8.3 拷贝构造函数被调用的情况
8.4 深拷贝与浅拷贝⭐⭐⭐⭐⭐浅拷贝是增加了一个指针,指向原来已经存在的内存。而深拷贝是增加了一个指针,并新开辟了一块空间让指针指向这块新开辟的空间。浅拷贝在多个对象指向一块空间的时候,释放一个空间会导致其他对象所使用的空间也被释放了,再次释放便会出现错误。 8.5 不会进入到拷贝构造函数,而是进入赋值构造函数的情况⭐⭐⭐⭐
此时就不会进入拷贝构造函数!!! 9、赋值构造函数如何进入赋值构造函数,见8.4 Vector& Vector::operator=(const Vector& a)
{
if( &a == this )
return *this;
double *p = new double[a.size()];
for(int i = 0; i!=sz; i)
p[i] = a.elem[i]; //记住C 中分配空间和初始化写在一起。
delete[] elem;
elem = p;
sz = a.sz;
return *this;
}
10、左值、右值引用是什么?左值指的是既能够出现在等号左边也能出现在等号右边的变量(或表达式)
右值指的则是只能出现在等号右边的变量(或表达式)。如运算操作(加减乘除,函数调用返回值等)所产生的中间结果。 10.1 左值引用MyType &引用名 = 左值表达式;
10.2 右值引用右值的引用,就是给右值取别名 Type &&引用名 = 右值表达式;
10.3 为什么要有右值引用呢?左值引用我们知道可以用于函数传参,减少不必要的对象拷贝,提升效率;或者用于替代指针的使用。 C 11引入右值引用主要是为了实现移动语义和完美转发。 10.3.4 移动语义首先实现string类⭐⭐⭐⭐⭐
测试一下代码: size_t MyString::Ctor = 0;
size_t MyString::CCtor = 0;
int main(){
vector<MyString> vecStr;
vecStr.reserve(1000); //先分配好1000个空间
for (int i = 0; i < 1000; i ) {
vecStr.push_back(MyString('hello'));
}
cout << '构造次数:' << MyString::Ctor << endl;
cout << '拷贝构造次数:' << MyString::CCtor << endl;
}
结果:
我们每次拷贝一个临时变量都需要深拷贝即进入拷贝构造函数中,这样会降低效率,因此拷贝构造函数每次都是重新分配一块新的空间,同时将要拷贝的对象复制过来。 要是我们能够减少这个拷贝的次数,效率就提升了,所以这个时候右值引用就派上用场了 右值引用可以引用并修改右值,但是通常情况下,修改一个临时值是没有意义的。然而在对临时值进行拷贝时,我们可以通过右值引用来将临时值内部的资源移为己用,从而避免了资源的拷贝 增加移动构造函数: size_t MyString::MCtor = 0; //统计调用移动构造函数的次数
// 移动构造函数
MyString(MyString&& str)
:m_data(str.m_data) {
MCtor;
str.m_data = nullptr; //不再指向之前的资源了
}
测试结果:
成功减少了临时对象拷贝的次数。 11、移动构造函数移动构造函数与拷贝构造函数的区别是,拷贝构造的参数是const Mytype& str,是常量左值引用,而移动构造的参数是Mytype&& str,是右值引用 移动构造函数与拷贝构造不同,它并不是重新分配一块新的空间同时将要拷贝的对象复制过来,而是'拿'了过来,将自己的指针指向别人的资源,然后将别人的指针修改为nullptr,这一步很重要,如果不将别人的指针修改为空,那么临时对象析构的时候就会释放掉这个资源,那么就没有“拿”过来。 拷贝构造函数中,对于指针,我们一定要采用深层复制 而移动构造函数中,对于指针,我们采用浅层复制 注意:指针的浅层复制危害性极大! 之所以危险,是因为两个指针共同指向一片内存空间,若第一个指针将其释放,另一个指针的指向就不合法了,所以我们要重设指针为nullptr(因为a我们不再使用了,如果不设为nullptr那么指针还指向新创建的空间,一旦出现内存被释放了以后,指针却还指向该内存,就会出现“野指针”的问题,容易出现内存非法访问错误。) 用a初始化b后,a我们就不需要了,最好是初始化完成后就将a析构(不能用了)所以移动构造函数,专门处理这种,用a初始化b后,就将a析构的情况 移动构造函数 在初始化的时候就是作了一个浅拷贝 然后把指针指向了空。 move方法来将左值转换为右值,从而调用移动构造函数而不是拷贝构造函数。 使用移动构造函数会提升函数的效率,见下面代码中return z; Vector foo()
{
Vector x(2000);
Vector y(2000);
Vector z(2000);
z = x; // 执行赋值构造函数
y = std::move(x); //执行移动构造函数
return z; // 我们返回z,再想一个问题之前提到8.3的第四条中,在栈中返回,它是一个将亡之人,如果我们调用拷贝构造函数的话会效率很低,因为这个z我后面并不需要它了,因此我们用浅拷贝(移动构造函数)就可以了。
}
12、拷贝构造函数的参数为什么必须是引用?拷贝构造函数的参数为什么必须是引用?_nwd0729的专栏-CSDN博客 13、 静态成员与静态成员函数
创建了一个对象a,但是foo并不属于a
操作系统部分0、什么是内核?0.1 Linux内核系统体系结构5大模块:进程调度模块、内存管理模块、文件系统模块、进程间通信模块和网络接口模块。 **进程调度模块:**控制进程被CPU资源的使用,采取的策略是不同进程能够公平合理的访问CPU,同时保证内存能够及时的执行硬件操作 **内存管理模块:**保证所有的进程能够安全的来共享机器上的内存区,同时这个内存管理的模块还支持虚拟内存的管理方式,能够使得支持进程使用比实际使用内存的空间更大,并且可以利用文件系统把这些暂时不用的内存数据块交换到外部的存储设备上。 **文件系统模块:**支持对外部设备的驱动和一些存储,虚拟文件系统VFS(这里面的回答很重要⭐⭐⭐⭐⭐)这个模块通过所有的外部设备提供一个通用的文件接口,他隐藏了各种各样硬件设备以及实现细节 0.1.1虚拟文件系统: 虚拟文件系统是一套代码框架(framework),它处于文件系统的使用者与具体的文件系统之间,将两者隔离开来。这种引入 一个抽象层次的设计思想,即“上层不依赖于具体实现,而依赖于接口;下层不依赖于具体实现,而依赖于接口”,就是著名的“依赖反 转”,它在 Linux内核中随处可见。 1、 为上层的用户提供统一的文件和目录的操作接口,如 open, read, write 2、 为下层的具体的文件系统,定义一系列统一的操作“接口”, 如 file_operations, inode_operations, dentry_operation,而 具体的 文件系统必须实现这些接口,才能融入VFS框架中。 **进程间通信模块:**用于多种进程间通信 **网络接口模块:**支持各种网络通信的标准 0.2 Linux内核结构体0.3 内核的内存管理的基本单位是页(page),源码下面 /include/linux/mm_types 1、cache1.1 cache是什么Cache存储器:电脑中为高速缓冲存储器,是位于CPU和主存储器DRAM之间,规模较小,但速度很高的存储器,通常由SRAM静态存储器组成。 高速缓冲存储器最重要的技术指标是它的命中率。CPU要访问的数据在Cache中有缓存,称为“命中” (Hit),反之则称为“缺失” (Miss)。 现在 CPU 的 Cache 又被细分了几层,常见的有 L1 Cache, L2 Cache, L3 Cache,其读写延迟依次增加,实现的成本依次降低。 现代系统采用从 Register ―> L1 Cache ―> L2 Cache ―> L3 Cache ―> Memory ―> Mass storage的层次结构,是为解决性能与价格矛盾所采用的折中设计。 下图描述的就是CPU、Cache、内存、以及DMA之间的关系。程序的指令部分和数据部分一般分别存放在两片不同的cache中,对应指令缓存(I-Cache)和数据缓存(D-Cache)。 1.2 为什么需要cacheCPU缓存(Cache Memory)位于CPU与内存之间的临时存储器,它的容量比内存小但交换速度快。在缓存中的数据是内存中的一小部分,但这一小部分是短时间内CPU即将访问的,当CPU调用大量数据时,就可避开内存直接从缓存中调用,从而加快读取速度。 使用Cache改善系统性能的依据是程序的局部性原理,包括时间局部性和空间局部性。即最近被CPU访问的数据,短期内CPU 还要访问(时间);被 CPU 访问的数据附近的数据,CPU 短期内还要访问(空间)。因此如果将刚刚访问过的数据缓存在Cache中,那下次访问时,可以直接从Cache中取,其速度可以得到数量级的提高。 1.3 cpu与cache 内存交互的过程CPU接收到指令后,它会最先向CPU中的一级缓存(L1 Cache)去寻找相关的数据,然一级缓存是与CPU同频运行的,但是由于容量较小,所以不可能每次都命中。这时CPU会继续向下一级的二级缓存(L2 Cache)寻找,同样的道理,当所需要的数据在二级缓存中也没有的话,会继续转向L3 Cache、内存(主存)和硬盘. 1.4 cache写机制Cache写机制分为write through和write back两种。 Write-through(直写模式)在数据更新时,同时写入缓存Cache和后端存储。此模式的优点是操作简单;缺点是因为数据修改需要同时写入存储,数据写入速度较慢。 Write-back(回写模式)在数据更新时只写入缓存Cache。只在数据被替换出缓存时,被修改的缓存数据才会被写到后端存储。此模式的优点是数据写入速度快,因为不需要写存储;缺点是一旦更新后的数据未被写入存储时出现系统掉电的情况,数据将无法找回。 贯穿读出式(Look Through) 该方式将Cache隔在CPU与主存之间,CPU对主存的所有数据请求都首先送到Cache,由Cache自行在自身查找。如果命中。 则切断CPU对主存的请求,并将数据送出;不命中。则将数据请求传给主存。 该方法的优点是降低了CPU对主存的请求次数,缺点是延迟了CPU对主存的访问时间。 旁路读出式(Look Aside) 在这种方式中,CPU发出数据请求时,并不是单通道地穿过Cache。而是向Cache和主存同时发出请求。由于Cache速度更快,如果命中,则Cache在将数据回送给CPU的同时,还来得及中断CPU对主存的请求;不命中。则Cache不做任何动作。由CPU直接访问主存。它的优点是没有时间延迟,缺点是每次CPU对主存的访问都存在,这样。就占用了一部分总线时间。 1.5 cache 一致性⭐⭐⭐⭐⭐2、Norflash与Nandflash的区别(1)、NAND闪存的容量比较大 3、反码、补码反码:对原码除符号位外的其余各位逐位取反就是反码 补码:负数的补码就是对反码加1 正数的原码、反码、补码都一样 4、内存管理MMU的作用
5、SRAM、DRAM、SDRAMSRAM:CPU的缓存就是SRAM,静态的随机存取存储器,加电情况下,不需要刷新,数据不会丢失 6、主宰操作系统的经典算法7、同步与异步7.1 中断的同步与异步
7.2 同步与异步信号
常见的IIC跟SPI就是同步信号 差别:
8、实时操作系统和非实时操作系统的区别9、ioremap实现物理地址到虚拟地址的映射函数原型
作用:把物理地址开始的一段空间大小为 (size),映射为虚拟地址;返回值是该段虚拟地址的首地址。 实际上,它是按页4096字节进行映射的,是整页整页地映射的。 |
|