金融直播间网站开发,商业网站导航怎么做,北京专业网站制作介绍,微信营销案例首先要明确两个概念
函数实参的入栈从右向左栈区从高地址向低地址偏移
接下来看下面一段代码
void fun(int a,int b,int c){std::couta b cstd::endl;
}
int main(){fun(1,2,3);
}…首先要明确两个概念
函数实参的入栈从右向左栈区从高地址向低地址偏移
接下来看下面一段代码
void fun(int a,int b,int c){std::couta b cstd::endl;
}
int main(){fun(1,2,3);
}这段代码看起来很简单但是你在不同环节下运行起来可能就会有问题了windows下vs linux下vscode 可以看出一个是从左向右递增一个是从左向右递减你觉得这个是为什么呢
我也不卖关子了出现这种情况的原因是不同编译器将帧地址传递给函数后对函数局部变量的处理顺序不同。
函数从调用开始到结束过程
创建栈帧
在编译期编译器会根据类中局部变量的使用情况确定栈帧的大小
栈主函数帧指针主函数内局部变量…返回地址函数的帧指针指向主函数帧指针函数的局部变量…栈指针
以上就是一个主函数调用一个函数时栈内的情况栈帧可以理解为一个函数的活动空间或者说运行环境下面简述几个概念。
帧指针函数的主心骨局部变量的创建都是在它的地址基础上偏移它内部存函数调用者的帧指针地址栈指针就是栈顶返回地址存储调用函数完成后需要返回到的下一条指令的地址
栈帧的大小是在编译期间确定的形参也处于局部变量的范畴将实参从调用者的栈帧内移动到被调用者的栈帧内就涉及到了几种传参方式
值传递址传递引用传递也是通过址传递
栈帧和代码区函数的交互
函数的运行在代码区平时看汇编的时候如果你注意就会发现发生函数调用时一定会传一个东西这个东西就是帧指针比如下面这一坨
#includeiostream
using namespace std;
void fun(){int nums[1024];
}int main() {fun();
}fun():push rbpmov rbp, rspsub rsp, 3976nopleaveret
main:push rbpmov rbp, rspcall fun()mov eax, 0pop rbpret这个rbp寄存器里面存的就是这个帧指针sub rsp, 3976这个条指令就是进行栈指针的偏移这个3976是栈帧内为局部变量预留的空间至此可以说栈帧的创建完成了接下来该使用了局部变量的创建可以看到都是通过在rbp的基础上进行偏移的需要注意的是编译器会进行各种优化你需要编译时加上-0o关闭优化还有就是栈的对齐方式是16字节。
所以说当面试官问你栈区有没有产生内存碎片的可能时你可以从容的回答栈的对齐大多数采用的是16字节可能会因为对齐方式而产生比较多的内存碎片这些碎片显然是用不了的高性能的代价往往是高内存消耗。
返回值的处理
一般来说对于返回值是函数的调用者需要关注的东西现在的大部分编译器返回值的传递其实都是通过寄存器完成的在不进行各种优化的情况下编译器会根据返回值的数据进行选择如果数据量较大就使用寄存器返回地址如果数据量较小则直接使用寄存器将结果返回寄存器返回后会调用者的栈帧内拷贝一份函数的返回值这个过程发生在函数栈帧被释放前这个过程结合我之前的一篇文章C完美转发和移动语义看会比较清楚。 上面那个过程显然不太合理首先返回值不是被调用者需要考虑的东西如果说我们直接在函数调用前在调用者的栈帧内创建好被调用者的返回值然后使用寄存器将这个地址传过去在这个地址的基础上函数再进行处理不就行了吗实际上这个就是RVO优化的原理此外RVO还会判断返回值有没有被用到如果没用的话也是会优化掉的。 RVO发生的条件通常是在函数中创建了一个局部对象并将其作为函数返回值而调用方直接使用了这个返回值。在这种情况下编译器可以直接将该局部对象放置到调用方期望的位置而不需要进行复制或移动操作。 RVO失效情景
返回多个对象: 如果函数返回多个对象而不是一个单独的临时对象那么RVO优化可能不适用。RVO通常仅适用于返回单个对象的情况
return pairpairint,int,pairint,int();虚函数调用: 当函数是虚函数时编译器可能无法确定函数返回的对象的确切类型从而阻止了RVO优化的应用
struct A{virtual A fun(){return *this;}};
struct B:public A{virtual B* fun(){return *this;}};
A* bnew B;
b-fun();复杂的返回逻辑
if(a) return a;
else if(b) return b;
return c;观测RVO优化的过程直接从上下文地址的变化非常直观
A get(){A a;coutaendl;return a;
}int main() {A a;coutaendl;aget();A b;coutbendl;
}可以看到当发生RVO优化时会在调用者的栈帧内临时申请一块空间生命周期过了就被下一个变量覆盖了现在你应该对RVO和临时对象有了新的认识
总的来说移动语义的引入可以说从根源上缓解了临时对象对程序性能的影响就业务中可能出现的各种情况编译器可能不能很好的进行各种优化所以合适的代码书写还是很有必要的不要寄希望于各种优化了
栈帧的释放
返回值处理完成后首先帧指针跳转到上一个节点然后栈指针向高地址偏移直到遇到返回地址程序接着运行
回归到开头时候的问题现在你大概已经直到答案了没错就是不同操作系统在不同编译器下函数对参数的处理顺序不同造成的。