面试专攻

百度面经:软件测试开发

一面:

由于我是做的更多的是嵌入式和电子类相关的项目,跟互联网没多大关系,慌得一P,急忙水了一些了c/c++基础知识,嵌入式、软件测试理论基础以及数据库的基本操作准备面试

上午9点在酒店面试,面试官很nice,在我上去之前他已经把我网上注册的简历仔细浏览了一遍,也知道我的项目和互联网以及软件测试没有太大的联系,所以他就只是看着简历上的项目来问我,没有自我介绍,氛围很轻松,项目问的也很仔细,所以项目上的基础知识一定要清楚。后面他在选择就业这块和我讲了半小时他自己以及朋友的经历和看法,第一份工作及其重要,一定要选择大公司并认真考虑工作方向。全程聊了一个多小时,最后他说按照公司规定还是要考察下我的编程能力,所以给了我一个寻找两个链表的公共交点的题,让我现场手撕代码。思路有但是代码写起来,感觉乱七八糟。不他看了看我的代码,指出了些问题,觉得我思路是对的。就这样愉快地结束一面,说让我回去等消息。

二面:面试官是一个小姐姐,首先进行了自我介绍,这一面问的全是基础知识,还好提前看了面经准备了,问的都是差不多的知识点;(1)http协议:get和post的区别?(2)域名解析(DNS)(2)链表、数组,栈和队列的区别(3)嵌入式的基本命令(4)重写和重载的区别(5)new和malloc的区别  还问我会不会数据库,我说了解,让我写了一些基本的语句,她说有些问题。 后面给了两个代码题,又是和一面一样的链表找公共交点的题,我不知道她是特意问的还是不知道,反正我还是没写好真尴尬,没有考虑环;然后又给了我一个数组,将奇数排在偶数后面的题,剑指offer中的原题,当时没清楚她的要求直接以为奇数排在偶数前还需要保持原来的相对顺序(其实她没这么要求),写了一下,感觉还行把。后面又给了一个冰箱测试问题?怎么去测试;对测试开发的理解;讲自己优缺点,她还选了一个优点让我举例说明下;最后问我还有什么问题没我说没了。期待三面

更新三面:

三面还是一个小姐姐,进去先进行自我介绍,这一面先介绍自己的项目,然后她挑项目中的一些细节问了一下,问遇到了些什么困难,怎么解决的?在公司实习经历的感受和收获?给自己的人生画一条跌宕起伏的线,然后讲述一下?作为测试开发工程这个职业,在素质和技能上你有什么有待提升的?工作地点意向?手上offer的情况?三面下来,我深刻地感受了到百度员工有礼貌,谦逊的面貌,面试氛围也很轻松。

顺丰科技面试:软件测试开发

一面:还要跑到春熙路累,结果20多分钟的面试,面试官人很好,刚开始自我介绍,然后看着我的简历说主要基于硬件的项目,所以也没问什么,问我对测试理论基础的一些知识,功能测试,自动化测试,知道些什么自动化测试工具(我不知道),然后给了我一个智力题,24个人,排成6排,每排5个人。其实就是一个六边形的排列;后面就跟我介绍一些关于测试工程师的工作,让我清楚都是干什么?自己想清楚,我说我目标很明确,讲了下自己对测试开发的理解和未来的职业规划;最后面试官问2哦有什么问题,我问了几个,感觉对我不感兴趣,就这样结束了。

汇顶:软件测试

一面:首先自我介绍,c++的多态机制?重载和重写的区别?指针与引用的区别?手写冒泡排序?RAM和ROM还有FLASH、OTP的区别?有线通信与无线通信的区别?同步时钟与异步时钟的区别?数据库的了解,查询表中31到40的记录?一只钢笔怎么去测试?怎么调查成都市共享单车的数量?讲下项目的应用背景和创新?为什么喜欢成都?

一些问题:

嵌入式的常用命令都有哪些:

chmod: 修改文件(用户、用户组、其他用户)权限;读权限:r(4)  写权限:w(2)   执行权限:x(1)  a:所有用户   u:用户    g:用户组 

chmod 754 文件  

chown : 修改文件所有者和用户组   

chown users:runoob file1.txt   将文件 file1.txt 的拥有者设为 users 群体的使用者 runoob;     

chown -R lamport:users * 将目前目录下的所有文件与子目录的拥有者皆设为 users 群体的使用者 lamport 

chgrp   改变文件或目录的所属组   

chgrp zou  file1.txt  改变了文件file1.txt所属组为zou

ps:  查看进程

kill: 杀死进程

kill -9强制杀死进程

crtl+c:结束前台进程

ls:列出文件下所有的文件

1. ls -a 列出文件下所有的文件,包括以“.“开头的隐藏文件(linux下文件隐藏文件是以.开头的,如果存在..代表存在着父目录)。

2. ls -l 列出文件的详细信息,如创建者,创建时间,文件的读写权限列表等等。

3. ls -F 在每一个文件的末尾加上一个字符说明该文件的类型。"@"表示符号链接、"|"表示FIFOS、"/"表示目录、"="表示套接字。

4. ls -s 在每个文件的后面打印出文件的大小。  size(大小) 

5. ls -t 按时间进行文件的排序  Time(时间)

6. ls -A 列出除了"."和".."以外的文件。

7. ls -R 将目录下所有的子目录的文件都列出来,相当于我们编程中的“递归”实 

8. ls -L 列出文件的链接名。Link(链接)

9. ls -S 以文件的大小进行排序

pwd:显示当前所在工作目录的全路径

find:查找文件

find ./ -name test  查找名字为test的文件或目录

find ./ -regex .*so.*.gz查找名字符合正则表达式的文件,注意前面的‘.*’(查找到的文件带有目录)
find ./ -mtime -2/2/+2 查找文件更新日时在距现在时刻二天以内/一天以上二天以内/二天以上的文件
find ./ -empty查找空文件或空目录
find ./ -perm 664查找权限为644的文件或目录(需完全符合)
find ./ -size +10c查找文件size大于10个字节的文件或目录
grep 字符串匹配

cat  /etc/file 打开文件

head  -n 10 /etc/file  查看文件前10行

tail -n 10 /etc/file   查看文件最后10行

sed -n '5,10p' 查看文件5到10行

mv 移动文件

rm 删除文件

cp 复制文件

mkdir 创建文件夹

touch 创建文件 

tar -czvf file.tar  file压缩文件 /  tar -xzvf file.tar 解压文件

为了准备二面的知识:

1,进程与线程的联系与区别?

首先进程是CPU进行系统资源分配与调度的基本单位,它是程序执行的一个实例。启动一个新的进程系统需要分配独立的地址空间和数据段,堆栈段等,导致系统开销很大。而线程是进程中独立执行的最小单位,它在进程中被创建,拥有独立的堆栈,与进程共享地址空间和数据段,所以线程的开销要比进程小很多,一个进程可以有多个线程,但至少得有一个。不同的进程之间和同一进程中的不同线程都可以并发执行,从而提高系统的并发性,能更有效地使用用系统资源和提高系统吞吐量。

进程拥有独立的地址空间,所以不同进程间的数据传递需要通过通信的方式

进行有名管道:半双工模式通信模式(固定的读端和写端,只能用于有亲缘关系的进程间通信

无名管道:半双工模式通信模式(固定的读端和写端,用于无关系的进程间通信

信号:信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生

信号量:信号量是一个计数器,可以用来控制多个进程或同一进程的多线程对共享资源进行同步访问

消息队列:消息队列是一个消息链表,存放在内核中并由消息队列标识符表示,消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。

共享内存:共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。需要配合其他通信机制如信号量,来实现进程间的同步和通信

套接字:可以用于不同机器间的进程通信。

线程间可以通过全局变量通信。

2、怎么理解面向过程的c和面向对象的c++?

就像生活中做菜一样,面向过程就是按照做菜的步骤先洗菜,切菜,再炒菜,最后装盘,强调过程的实现;而面向对象,则是找一个会做菜的人作为对象,把菜给他,他调用方法实现,我们直接等着吃就可以了。c++具有三种特性:多态性,封装性,继承性。所谓封装就是将某些东西包装并隐藏,使用者不必了解具体的实现细节,通过某些特定的方式才能访问。封装的目的是增强安全性和简化编程;多态性:子类可以通过虚函数重新定义父类的成员函数,实现新的功能。;继承性:继承可以使得子类具有父类的各种属性和方法,而不需要再次编写相同的代码,在子类继承父类的同时,可以重新定义某些属性和方法,实现不同的功能。

3、变量的引用和指针的区别

首先引用只是原变量的一个别名,必须进行初始化,且初始化后就不能再进行改变,引用作为函数形参传递时,实际修改的是实参。指针是一个地址变量,指向一个内存单元,可以初始化也可以不进行初始化,可以更改初始值,指针传递参数,传递的是一个地址,如果对地址进行改变,不会影响原指针指向的实参变量。

 4、栈和队列的区别?链表和数组的区别

栈是先进后出,而队列是先进先出,栈由编译器自动分配和释放,用来存放的函数的参数等局部变量;两者插入与删除元素的时间复杂度都是哦o(1), 删除数据元素的位置不同,栈的删除操作在表尾进行,队列的删除操作在表头进行。

数组的存储空间是连续的,在初始化时必须定义大小,它可以随机读取,在插入或删除数据时,需要将后面的数据进行前移,时间复杂度为o(n),读取时间复杂度为o(1)。链表的存储空间可连续也可不连续,只能顺序读取时间复杂度为o(n),查找效率低,插入和删除数据效率较高,时间复杂度为o(1)。它的大小可以动态拓展。

5、重载,重写(覆盖)、隐藏的区别?

重载:发生同一个类中,其函数名相同,函数参数的类型、个数不同;

重写:发生在不同类中,就是子类通过虚函数重新定义父类的函数,其函数名和函数参数都相同,需要virtual修饰;

隐藏:发生在不同的类中,其函数名相同,函数的参数类型、个数可以相同也可以不同;

6、static的作用?

c中:修饰全局变量和函数时限制作用域;修饰局部变量时延长变量的生命周期;

c++中:除了上述两个作用,它还修饰成员变量和成员函数,表示属于一个类而不再是类中某个对象的变量和函数,对类来说是唯一的。静态成员函数只能访问静态变量和静态函数,不能访问非晶态成员函数和变量,静态成员不包含指向具体对象的this指针,不能为虚函数,也不能声明为const或voltatile。

7、变量的定义和声明?

定义会分配地址和存储空间,声明不分配地址,一个变量可以有多个声明,但是只能有一次定义。

8、为什么析构函数要用虚函数而构造函数不能用?

一般子类经由父类指针被删除,如果析构函数不是虚函数的话,就不能正确识别对象类型从而不能正确调用析构函数,从而发生未定义,导致资源泄露。

因为构造函数的作用就是初始化类的对象,子类在调用构造函数时还不能确定对象类型,它会直接调用父类的构造函数。所以构造函数不能为虚函数。

9、c++调用父类构造函数的规则

构造方法用来初始化类的对象,与父类的其它成员不同,它不能被子类继承(子类可以继承父类所有的成员变量和成员方法,但不继承父类的构造方法)。因此,在创建子类对象时,为了初始化从父类继承来的数据成员,系统需要调用其父类的构造方法。

如果没有显式的构造函数,编译器会给一个默认的构造函数,并且该默认的构造函数仅仅在没有显式地声明构造函数情况下创建。

构造原则如下:
1. 如果子类没有定义构造方法,则调用父类的无参数的构造方法。

2. 如果子类定义了构造方法,不论是无参数还是带参数,在创建子类的对象的时候,首先执行父类无参数的构造方法,然后执行自己的构造方法。

3. 在创建子类对象时候,如果子类的构造函数没有显示调用父类的构造函数,则会调用父类的默认无参构造函数。

4. 在创建子类对象时候,如果子类的构造函数没有显示调用父类的构造函数且父类自己提供了无参构造函数,则会调用父类自己的无参构造函数。

5. 在创建子类对象时候,如果子类的构造函数没有显示调用父类的构造函数且父类只定义了自己的有参构造函数,则会出错(如果父类只有有参数的构造方法,则子类必须显示调用此带参构造方法)。

6. 如果子类调用父类带参数的构造方法,需要用初始化列表的方式。

10、什么是纯虚函数?

纯虚函数最父类定义提供派生接口,在子类中被实现,起到定义规范标准作用。

11、new和malloc的区别?

new是c++关键字,需要编译器支持,初始化时不需要定义大小,会调用构造函数,并返回对象指针,当内存分配失败时会抛出异常,delete释放相应的内存,它允许被重载。

malloc是库函数,需要头文件支持,初始化时需要指定大小,返回无类型指针,需要强制转换成对象指针,当分配内存失败时会返回空指针,free释放内存,不允许重载。

12、深拷贝和浅拷贝区别?

我理解的浅拷贝就是创建一个新指针,指向被复制的内存,当修改新指针指向的数据时,原对象的数据也会跟着变化;深拷贝就是创建一个新的指针并复制内存数据段,新指针指向复制的数据,与原始数据独立,当修改新指针指向的数据时,原数据不会发生变化。

13、c和c++中的struct结构有什么区别?

多态性:c中的struct不能有成员函数,但可以有构造函数,默认初始化为0,没有析构函数和this指针,没有多态性;c++中有成员函数,可以通过虚函数实现多态性

封装性:c中的struct中只能是一些变量的集合体,可以封装数据却不可以隐藏数据;c++中把数据以及一些算法封装成属性和方法。

继承性:c中的struct不能被继承;c++可以继承.

访问权限:c中的struct没有访问权限;c++中默认为public访问权限。

14、c++中struct和类的区别?

C++中的struct对C中的struct进行了扩充,它已经不再只是一个包含不同数据类型的数据结构了,它已经获取了太多的功能。
 struct能包含成员函数吗? 能!
 struct能继承吗? 能!!
 struct能实现多态吗? 能!!!

默认的继承访问权限:struct是public的,class是private的。 “class”这个关键字还用于定义模板参数,就像“typename”。但关键字“struct”不用于定义模板参数。

15、字符设备注册函数和字符设备驱动设计流程(桑达无线笔试题)

int register_chrdev(unsigned int major, const char *name,struct file_operations *fops)//主设备号,驱动程序名称、操作集 

void cdev_init(struct cdev*cdev,struct file_operations *fops); //设备结构体、操作集

流程:1、分配设备描述结构; 2、初始化设备描述结构; 3、注册设备结构; 4、硬件初始化、5 实现设备操作;6、参数分析、7驱动注销

16、实现两个字符串函数的比较(桑达无线笔试题)

#include<stdint.h>
#include<stdlib.h>
int strcmp(char *s1, char *s2)
{
int result, i=0;
while(*(s1+i)!= '' && *(s2+i)!= '') {

if (*(s1+i) ==*(s2+i))
{
i++;
continue;
}

else if (*(s1+i) > *(s2+i))
return 1;
else if (*(s1+i) < *(s2+i))
return -1;
i++;
}

if(*(s1+i)==''&&*(s2+i)=='')
return 0;
else if (*(s1+i)!= '' && *(s2+i)== '')
return 1;
else
return -1;
}

 找出字符串中最长数字连续子串(输入adn123djuvb12345djk346,输出12345)

#include<iostream>
#include<string>
using namespace std;

int main()
{
string str;//定义str为某个要输入的字符串
cin>>str;

string tmp;//定义一个临时字符串数组用来存放数字串
string maxstr;//只存放最长的数字串
int maxlength=0;

for(int i=0;i<str.length();i++)
{
if(str[i]>='0' && str[i]<='9')
{
//次数的while循环和if中的判断语句一样,但是千万不可以少
//while是为了将所有的数字串都加进tmp当中
while(str[i]>='0' && str[i]<='9')
{
tmp+=str[i++];
}
if(tmp.size()>maxlength)
{
maxstr=tmp;
maxlength=tmp.size();
}
}
tmp.clear();
}
cout<<maxstr;
return 0;
}

17、内存分配问题:(紫光同创笔试题)

(1)void GetMemory(char *p)
{     p = (char *)malloc(100);  }

void Test(void) 
{
char *str = NULL;
GetMemory(str); 
strcpy(str, "hello world");
printf(str);
}

请问运行Test函数会有什么样的结果?
答:程序崩溃。
因为GetMemory并不能传递动态内存,Test函数中的 str一直都是 NULL。strcpy(str, "hello world");将使程序崩溃。
(2)char *GetMemory(void)

char p[] = "hello world";
return p;
}
void Test(void)
{
char *str = NULL;
str = GetMemory(); 
printf(str);
}

请问运行Test函数会有什么样的结果?
答:可能是乱码。因为GetMemory返回的是指向“栈内存”的指针,该指针的地址不是 NULL,但其原现的内容已经被清除,新内容不可知。
(3)void GetMemory2(char **p, int num)
{
*p = (char *)malloc(num);
}
void Test(void)
{
char *str = NULL;
GetMemory(&str, 100);
strcpy(str, "hello"); 
printf(str); 
}
请问运行Test函数会有什么样的结果?
答:
(1)能够输出hello(2)内存泄漏(没释放掉已经申请的动态内存)


(4)void Test(void)
{
char *str = (char *) malloc(100);
 strcpy(str, “hello”);
 free(str);     
 if(str != NULL)
 {
   strcpy(str, “world”); 
printf(str);
}}
请问运行Test函数会有什么样的结果?
答:篡改动态内存区的内容,后果难以预料,非常危险。因为free(str);之后,str成为野指针,if(str != NULL)语句不起作用。

18、虚函数的定义要遵循以下重要规则: 
1.如果虚函数在基类与派生类中出现,仅仅是名字相同,而形式参数不同,或者是返回类型不同,那么即使加上了virtual关键字,也是不会进行滞后联编的。 
2.只有类的成员函数才能说明为虚函数,因为虚函数仅适合用与有继承关系的类对象,所以普通函数不能说明为虚函数。 
3.静态成员函数不能是虚函数,因为静态成员函数的特点是不受限制于某个对象。 
4.内联(inline)函数不能是虚函数,因为内联函数不能在运行中动态确定位置。即使虚函数在类的内部定义,但是在编译的时候系统仍然将它看做是非内联的。 
5.构造函数不能是虚函数,因为构造的时候,对象还是一片位定型的空间,只有构造完成后,对象才是具体类的实例。 
6.析构函数可以是虚函数,而且通常声名为虚函数

19、什么是守护进程?其创建过程?

守护进程是一种后台运行的特殊进程,它独立与控制端并且周期性执行某种任务或者等待某些发生的事件;

创建过程: 1、后台运行:在进程中创建子进程(fork()),让daemon在子进程中运行,终止父进程;

      2、脱离控制端、登录会话和进程组:进程属于一个进程组,进程组号(GID)就是进程组长的进程号(PID)。登录会话可以包含多个进程组。  这些进程组共享一个控制终端。这个控制终端通常是创建进程的登录终端。 控制终端,登录会话和进程组通常是从父进程继承下来的。我们的目的就是要摆脱它们,使之不受它们的影响。调用setsid()使子进程成为会话组长:

      3、禁止进程重新打开控制终端

      4、关闭打开的文件描述符:进程从创建它的父进程那里继承了打开的文件描述符。如不关闭,将会浪费系统资源

                   5、 改变当前工作目录:进程活动时,其工作目录所在的文件系统不能卸下。一般需要将工作目录改变到根目录。

                   6、 重设文件创建掩模:进程从创建它的父进程那里继承了文件创建掩模。它可能修改守护进程所创建的文件的存取位。

                   7.   处理SIGCHLD信号

20、判断链表是否有环?判断链表是否有交点,找出第一个交点?怎么找有环链表的出口?

(1)判断链表是否有环:设置快慢指针,两个指针同时从链表头出发,慢指针每次移动一个点,快指针每次移动2个点,如果有环,两个指针一定会相交某个点B;当快慢指针相遇时,假设慢指针走了t步,那么快指针一定走了2t步。当慢指针进入环以后,快指针一定会在慢指针走完一圈之前追上它。因为最坏的情况是慢指针进入环时快指针刚好落后它一整圈,这样慢指针走完一圈快指针刚好追上慢指针。

struct ListNode
{
    int key;
    ListNode * next;
};
bool HasCircle(ListNode * pHead)
{
    ListNode * pFast = pHead; // 快指针每次前进两步
    ListNode * pSlow = pHead; // 慢指针每次前进一步
    while(pFast != NULL && pFast->next != NULL)
    {
        pFast = pFast->next->next;
        pSlow = pSlow->next;
        if(pSlow == pFast) // 相遇,存在环
            return true;
    }
    return false;
}

(2)判断链表是否有交点:

1.判断两个链表是否都不含有环,若都是不带环的单链表:

方法一:首先遍历两个链表直到链尾,如果链尾相等的话就有交点,否则没有交点;将长链表的长度K1-短锻表的K2,指向长链表的指针先先走k1-k2步,然后两个链表同时遍历进行比较,相等就为第一个交点;

方法二:首先保存第二条链表的第一个节点,然后第二个链表整个接到第一个链表的后面,从第二个链表的第一个节点往后遍历,如果能再走到第二条链表的第一个节点位置,说明第二条链表的后部分节点与第一条链表共用,这样才会是后移的指针重新指向第二条链表的第一个节点。

方法三:方法我们可以把其中一个链表的所有节点地址信息存到数组中,然后把另一个链表的每一个节点地址信息遍历数组,若相等,则跳出循环,说明链表相交。进一步优化则是进行hash排序,建立hash表。

方法四:首先保存第二条链表的第一个节点,然后把第二条链表首尾链接形成一个大环,从第一条链表开始走,如果指针能够指向到第二条链表的第一个节点,说明链表相交。


2.若一个链表不带环,一个带环,则这两个链表一定不相交,因为相交之后,链表之后部分是共用,若有环,则都带环,且环的长度相等,相交点即为环的入口。 

3.若都带环,求出其中一个链表的相遇点,然后遍历另一个链表,若相遇点的地址信息存在另一个链表上,则表明链表相交。原理如果相交的话,两链表的环上必定都是共用的节点,因为环是由尾部指向另一个节点造成的,而相交的部分必定包含尾部,所以环上必定是共有的节点,相遇点正好在环上,所以遍历另一链表,若找到相同的地址信息,必相交!(写程序时,前提是链表头里的数据存放了该链表的节点数,否则遍历链表时会进入死循环,所以还得计算出链表的节点数或者找到链表的尾结点)

(3)怎么找有环链表的出口

一个慢指针从链头出发,一个快指针从在判断有环时两个指针相遇的交点B出发,两者以相同的速度走,它们第一次相遇的点就是链表环的出口;

具体请见https://blog.csdn.net/puss0/article/details/78462375

 21、域名解析器(DNS)

域名解析是把域名指向网站空间IP,让人们通过注册的域名可以方便地访问到网站的一种服务。IP地址是网络上标识站点的数字地址,为了方便记忆,采用域名来代替IP地址标识站点地址。域名解析就是域名到IP地址的转换过程。域名的解析工作由DNS服务器完成。

22、如何测试一个冰箱

功能性:是否能插电能正常使用,外观是否美观,表面是否光洁、平整、无刮碰和,内部构造是否合理,保鲜度、低温水平、保温水平、耗电量、噪音分贝、额定容量等;(中间也插入了性能测试)

易用性:功能操作是否简单,清洁方不方便,移动性怎么样,性价比等。

安全性:是否漏电

23、c++多态机制

多态分静态多态和动态多态;静态多态:是指在编译期间可以确定函数的调用地址,有函数重载和模板(泛型编程)。动态多态:函数的调用地址在编译期间不能确定,必须在运行时才能确定,继承+虚函数(重写)。

24、无线通信与有线通信的区别

无线通信:非导向性传输媒体(自由在空间传播),覆盖范围大。

有线通信:通过电线或光纤作为传输介质传输信号,传送稳定,快速、安全、抗干扰能力强

25、同步通信和异步通信

同步通信:是一种比特同步,要求接收方具有同频同相的时钟信号,可以实现高速度、大容量的数据传送

异步通信:可以有多个时钟信号,发送端可以任意时间发送一个帧,帧与帧间任意间隔,发送前不需要和接收端协调,只需要接收端提前做好接收准备。通信设备简单、便宜、信道利用率低(开始位和停止位开销大)

26、ram、rom、flash、otp

ram:随机存储器,掉电不保存数据,可分为:静态SRAM和动态DRAM。  SRAM: 速度快、价格高、只要不断电,数据就一直存在,主要用来做高速缓存(Cache)。动态存储器(DRAM)就是通常所说的内存。DRAM是靠MOS电路中的栅极电容来保存信息,由于电路漏电,因此需要设置刷新电路,每隔一段时间对DRAM进行刷新,以确保信息不丢失。DRAML比SRAM集成度高、功耗低、价格低。

rom:只读存储器,掉电保存数据,它相当于PC机上的硬盘,不能随意更新数据,可以任意时刻读取,

flash:结合了rom和ram的优点,不仅具备可擦除和可编程性能,还掉电保存数据同时可以快速读取,比如U盘,嵌入式中的存储BOOTLOADER和操作系统。Flash主要有两种NOR Flash和NADN Flash ,价格高。

otp:一次性可编程,程序烧入后不能再修改。1能被改写为0,但0永远也不能写成1。

原文地址:https://www.cnblogs.com/ffdln/p/9676163.html