● 请说一说你理解的stack overflow,并举个简单例子导致栈溢出
栈溢出指的是程序向栈中某个变量中写入的字节数超过了这个变量本身所申请的字节数,因而导致栈中与其相邻的变量的值被改变。
1、 局部数组变量空间太大
局部变量是存储在栈中的,因此这个很好理解
一是增大栈空间,二是改用动态分配,使用堆(heap)而不是栈(stack)。
2. 递归调用层次太多。
递归函数在运行时会执行压栈操作,当压栈次数太多时,也会导致堆栈溢出。
3.数组越界
● 请你回答一下栈和堆的区别,以及为什么栈要快
区别 | 堆 | 栈 |
地址扩展 | 由低地址向高地址扩展 | 由高地址向低地址扩展 |
内存 | 堆中的内存需要手动申请和手动释放 | 栈中内存是由OS自动申请和自动释放,存放着参数、局部变量等 |
分配方式 | 堆都是动态分配的 | 静态分配和动态分配。静态分配是由操作系统完成的,比如局部变量的分配。动态分配由alloca函数进行分配,但是栈的动态分配和堆是不同的,他的动态分配是由操作系统进行释放,无需我们手工实现。 |
内部碎片 | 堆中频繁调用malloc和free,会产生内存碎片,降低程序效率 | 而栈由于其先进后出的特性,不会产生内存碎片 |
分配效率 | 堆则是由C/C++提供的库函数或运算符来完成申请与管理,实现机制较为复杂,频繁的内存申请容易产生内存碎片。 | 栈由操作系统自动分配,会在硬件层级对栈提供支持:分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行,这就决定了栈的效率比较高。 |
堆的内部碎片,
堆不存在顺序,由程序员自行决定什么时候释放
频繁malloc,free由于所申请内存块的大小不定,
频繁申请释放内存时, 危害就是虽然你仍有很多空余内存可用,但没有一块足够大的能满足需要,再次申请大内存时就会失败
内存池;
内存池则是在真正使用内存之前,先申请分配一定数量的、大小相等(一般情况下)的内存块留作备用。当有新的内存需求时,就从内存池中分出一部分内存块,若内存块不够再继续申请新的内存。这样做的一个显著优点是尽量避免了内存碎片,使得内存分配效率得到提升。
数据结构中的栈和堆
栈是一种运算受限的线性表,其限制是指只仅允许在表的一端进行插入和删除操作,这一端被称为栈顶(Top),相对地,把另一端称为栈底(Bottom)。把新元素放到栈顶元素的上面,使之成为新的栈顶元素称作进栈、入栈或压栈(Push);把栈顶元素删除,使其相邻的元素成为新的栈顶元素称作出栈或退栈(Pop)。这种受限的运算使栈拥有“先进后出”的特性(First In Last Out),简称FILO。
堆是一种常用的树形结构,是一种特殊的完全二叉树,当且仅当满足所有节点的值总是不大于或不小于其父节点的值的完全二叉树被称之为堆。堆的这一特性称之为堆序性。因此,在一个堆中,根节点是最大(或最小)节点。如果根节点最小,称之为小顶堆(或小根堆),如果根节点最大,称之为大顶堆(或大根堆)。堆的左右孩子没有大小的顺序。下面是一个小顶堆示例:
时间复杂度还是O(N * logN)。
PS:刚刚面经看到的问题:
sizeof
a. 对基本数据类型,取得的是数据类型的大小
b. 对结构体类型,结构体类型变量所占的空间,注意字节对齐
c. 数组变量,数组的大小
d. 指针变量,指针的大小(4字节)
sizeof VS strlen
1.sizeof是运算符,strlen是函数,意味着编译时就将sizeof计算过了
2.sizeof可以求变量或者类型所占的空间,strlen求字符串的大小
● 手写代码:两个栈实现一个队列
front记录队列首元素,每次pop进行更新
class MyQueue {
public:
stack<int> a;
stack<int> b;
int front;
/** Initialize your data structure here. */
MyQueue() {
}
/** Push element x to the back of queue. */
void push(int x) {
if(a.empty()) front=x;
a.push(x);
}
/** Removes the element from in front of queue and returns that element. */
int pop() {
int len=a.size();
int res;
if(len==1){
res=a.top();
a.pop();
return res;
}
for(int i=0;i<len-1;i++){
b.push(a.top());
a.pop();
}
front=b.top();
res=a.top();
a.pop();
for(int i=0;i<len-1;i++){
a.push(b.top());
b.pop();
}
return res;
}
/** Get the front element. */
int peek() {
return front;
}
/** Returns whether the queue is empty. */
bool empty() {
return a.empty();
}
};