首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

C语言:底层剖析——函数栈帧的创建和销毁

经典的计算机科学,栈被定义为一种特殊的容器,用户可以将数据压入栈(入栈,push),也可 以将已经压入栈的数据弹出(出栈,pop),但是栈这个容器必须遵守一条规则:先入栈的数据出 栈(First...,这三个寄存器的函数随后执行可能会被修改,所以于谦保存寄存器原有的值,以便于退出函数能及时恢复。...4.3 main函数调用Add函数前,为什么call指令执行时,需要存储call指令的下一个地址?    ...五、对 二 的问题进行解释       通过对函数栈帧的创建和销毁学习,对于这个函数的底层知识有了更深刻的理解。以此们可以解决目录二提到的问题。...函数的返回值会被存储寄存器

22710
您找到你想要的搜索结果了吗?
是的
没有找到

CC++ volatile

1.volatile的作用 定义为volatile的变量是说这变量可能会被意想不到地改变,即在你程序运行过程中一直会变,你希望这个值被正确的处理,每次从内存中去读这个值,而不是因编译器优化从缓存的地方读取...反之如果你不是对此端口反复写操作,而是反复读操作,其结果是一样的,编译器优化,也许你的代码对此地址的读操作只做了一次。然而从代码角度看是没有任何问题的。...函数调用dosomething函数,但是,由于编译器判断main函数里面没有修改过i,因此可能只执行一次对从i到某寄存器的读操作,然后每次if判断都只使用这个寄存器里面的“i副本”,导致dosomething...如果将变量i加上volatile修饰,则编译器保证对变量i的读写操作都不会被优化,从而保证了变量i被外部程序更改能及时原程序得到感知。 (3)多线程应用中被多个任务共享的变量。...当多个线程共享某一个变量时,该变量的值会被某一个线程更改,应该用 volatile 声明。作用是防止编译器优化把变量从内存装入CPU寄存器,当一个线程更改变,未及时同步到其它线程中导致程序出错。

1.8K31

初级程序员面试不靠谱指南(三)

可以看到swap1,传入两个参数的地址就是main函数两个变量的地址,而swap2的两个参数地址是新的,和原始变量的地址没有任何关系(而且还很远,有兴趣的话这里也可以继续研究下去,但是我想一起放在函数的时候再写...那为什么要加一个const呢?从2也可以看到,如果不采用const的话,传入的变量有会被改变,所以使用const可以保证不会被误操作而发生改变。...可以看到,输出的a的值并不正确了,查看一下各个函数地址信息,a所得到的值正是第一个函数返回的引用的值(地址相同),但是第二个函数调用以后,可以看到x也用了第一个函数i的地址,此时a所表示的变量也在这个地址之中...其原因是函数里面的变量函数结束之后(局部变量)就消失(析构)了,它原来的地址下一次仍然会被使用,从输出也可以看到这一点,所以不要返回一个局部变量的引用。...引用不能不初始化,指针不考虑出问题的情况下至少是允许不初始化的。

71190

Java的final

3.3、final修饰一个成员变量(属性),必须要显示初始化声明时初始化构造函数初始化),同时一旦被初始化赋值之后,就不能再被赋值了。...由于a,b之间没有数据依赖性,普通域(普通变量)a可能会被重排序到构造函数之外,线程B就有可能读到的是普通变量a初始化之前的值(零值),这样就可能出现错误。...问题:为什么final引用不能从构造函数“溢出” 一个比较有意思的问题:上面对final域写重排序规则可以确保我们使用一个对象引用的时候该对象的final域已经构造函数初始化过了。...但是这里其实是有一个前提条件的,也就是:构造函数,不能让这个被构造的对象被其他线程可见,也就是说该对象引用不能在构造函数“逸出”。...因为构造函数操作1和2之间没有数据依赖性,1和2可以重排序,先执行了2,这个时候引用对象referenceDemo是个没有完全初始化的对象,而当线程B去读取该对象时就会出错。

54130

C语言】汇编角度剖析函数调用的整个过程

dword ptr [ebp-20h],0 我们其实只要用局部变量的值覆盖掉main函数栈帧刚开始初始化的内容,这样就完成了局部变量的内容初始化和空间的分配这个步骤了 2.3 函数调用前的准备工作...,我们将ebp指针地址压栈到了栈帧空间当中,而这个edp其实就是指向main函数栈帧底部的指针,随后我们又进行了调整ebp和esp位置的汇编指令操作,其目的就是重新改变ebp和esp所维护的函数栈帧空间...-8地址处进行了变量c的内容初始化和分配空间,将其内容初始化为0 然后我们将ebp+8处的值放到eax寄存器当中,再将eax的值加上ebp+0ch处的值,再次给到eax寄存器当中,然后我们把eax的值...,这个值正是烫烫烫的原因,所以我们局部变量的创建,是函数栈帧开辟好的前提下,在里面寻找一个地址,把这个地址的空间分配给我们的变量,如果你想初始化这个变量,就把(0ccccccch)用你想要的值将其给覆盖掉...2.为什么局部变量的值是随机值? 因为函数栈帧开辟,会先对函数栈帧进行内容初始化初始化为0CCCCCCCCh。这正是随机值的原因 3.函数是怎么传参的?传参的顺序是怎样的?

1K10

fishhook详解

我回顾的dyld的加载流程dyld::_main函数做的第二步就是加载共享缓存库。共享缓存库是什么呢?...此时,如果我们想要HookC函数的话,就只能改变C函数地址,而C函数地址在编译期间就已经确定了,这些地址被存储machO二进制可执行文件,而MachO是不会更改的,因此C函数是没有办法被Hook...现在我们回到OC程序,毫无疑问NSLog是一个C函数,但是在编译这个OC程序的时候,是不能确定其地址的。...而C语言函数在编译的时候是必须要确定一个地址的,而我们的iOS程序在编译的时候又不能知道系统库C函数的具体地址这个时候就进行不下去了啊,程序没法编译了啊。...程序定义一个my_nslog函数和一个sys_nslog函数,这两个函数会被编译进machO里面,所以我们是可以获取到其地址的。

1.5K10

C++引用

使用场景 做参数 这个上面演示过,就是交换数 #include void TestRef(int& x, int& y)//这里就不是临时拷贝了,而是main函数的a和b { int...n就不再静态区了,就到了栈里面,因为函数结束栈就会销毁,所以n的数据就不会被保护。...因为函数用完之后栈会销毁,原来的地址就可能成为下一个函数地址的空间,看,打印出来20就是被www函数的成员覆盖了,www函数地址和Count函数n的地址一模一样。...引用在初始化时引用一个实体,就不能再引用其他实体,而指针可以在任何时候指向任何一个同类型实体 没有NULL引用,但有NULL指针。...sizeof含义不同:引用结果为引用类型的大小,但指针始终是地址空间所占字节个数(32位平台下占4个字节)。 引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小。

30400

Golang函数传参存在引用传递吗?

Go slice 传入函数时到底是不是引用传递?如果不是,函数为什么能修改其值?...vFoo 的形参 b vFoo 的内部,b 会被当作局部变量栈上分配空间,并且完全拷贝 a 的值。...代码执行,我们看到的结果便是:a、b拥有完全不同的内存地址, 说明他们虽然值相同(b拷贝的a,值肯定一样),但是分别在内存不同的地方,也因此函数 vFoo 内部如果改变 b 的值,a 是不会受到影响的...函数 pFoo ,形参 p 的地址与实参 pa 的地址并不一样,但是他们在内存的值都是变量 a 的地址,因此可以通过指针相关的操作来改变a的值。 ?...小结 Go 函数传参仅有值传递一种方式; slice、map、channel都是引用类型,但是跟c++的不同; slice能够通过函数传参,修改对应的数组值,是因为 slice 内部保存了引用数组的指针

2.2K20

云风coroutine协程库源码分析

即:改变EIP寄存的内容,指向其他指令地址改变线程栈的内存内容等等。 这样的话,当前线程运行的程序也就完全改变了,是一个全新的程序。...ucontext_t main; 主协程的上下文,方便后面协程执行完切回到主协程。 char stack[STACK_SIZE]; 这个非常重要,是所有协程的运行时栈。...那么,为什么不直接传struct schedule*呢,而要这么做,通过先拆两半,再在函数拼起来?...接下来调用了swapcontext函数这个函数比较简单,但也非常核心。作用是将当前的上下文内容放入S->main,并将C->ctx的上下文替换到当前上下文。...coroutine, 也就是开始执行mainfunc这个函数。(mainfunc是对用户提供的协程函数的封装)。

1.5K50

iOS App启动过程

App启动的时候,程序会被影射到逻辑的地址空间,这个逻辑的地址空间有一个起始地址,而ASLR技术使得这个起始地址是随机的。如果是固定的,那么黑客很容易就可以由起始地址+偏移量找到函数地址。...当你的程序要调用printf的时候,会先在__DATA段建立一个指针指向printf,通过这个指针实现间接调用。dyld这时候需要做一些fix-up工作,即帮助应用程序找到这些符号的实际地址。...ObjC 加载时可以通过 fix-up 动态类改变实例变量的偏移量,利用这个技术可以改变dylib的情况下添加另一个 dylib 类的方法,而非常见的通过定义类别(Category)的方式改变一个类的方法...: Initializers Objc SetUp 结束,Dyld 便开始运行程序的初始化函数,该任务由 initializeMainExecutable 函数执行。...初始化需要做的事情包括: 调用 Objc 类的 + load 函数 调用 C++ 带有 constructor 标记的函数 非基本类型的 C++ 静态全局变量的创建 所谓执行监控启动crash的思路都是在这里构建的

2.1K30

C语言——F函数的栈帧的创建和销毁

经典的计算机科学,栈被定义为一种特殊的容器,用户可以将数据压入栈(入栈 push),也可以将已经压入栈的数据弹出(出栈 pop),但是栈这个容器必须遵守一条规则:先入栈的数据出栈(First...,这3个寄存器的函数随后执行可能会被修改,所以先保存寄存器原来的值,以便在退出函数时恢复。...//下面的代码是初始化main函数的栈帧空间。 //1. 先把ebp-24h的地址,放在edi //2. 把9放在ecx //3. 把0xCCCCCCCC放在eax //4....call指令之前先会把call指令的下一条指令的地址进行压栈操作,这个操作是为了解决当函数调用结束要回到call指令的下一条指令的地方,继续往后执行。  ...答:因为有call 指令,执行call 指令之前先会把 call 指令的下一条指令的地址进行压栈操作,这个操作是为了解决当函数调用结束要回到call指令的下一条指令的地方,继续往后执行,因此完成了带回

7910

C++基础语法重点总结

C++取名的时候,是将函数名和参数类型的首字符结合起来对函数的取名,这样就可以区分函数的不同了。 拓展:说说函数重载、函数重写、函数重定义区分: 作用域中:函数重载需要在同一个作用域中。...函数重定义和函数重写的两个函数必须一个父类,一个子类,而且函数重写必须是虚函数。...比如 int a = 10; int& ra = a; 定义的时候必须进行初始化,而且初始化不能改变引用对象。 说一说引用和指针的区别 ①引用在定义时必须初始化,而指针不需要。...②引用不能初始化为空引用,而指针可以初始化为空指针。③引用初始化不能改变指向对象,而指针可以改变指向。...,成员变量被修饰,是属于所有类的,所有类的对象都可以调用它,而且是不需要this指针去引用。

18430

一文了解 final 关键字的特性、使用方法以及实现原理

一旦将引用声明为final,将无法再改变这个引用。final关键字还能保证内存同步,本博客将会从final关键字的特性到从java内存层面保证同步讲解。这个内容面试也有可能会出现。...//这样就可以让每个实例都有一个不同的变量,并且这个变量每个实例会被初始化一次 //于是这个变量单个实例里就是常量了。...//final只修饰了Fi类型,即Fi实例化的对象内存地址是不可变的。 //虽然内存地址不可变,但是可以对内部的数据做改变。...final变量一旦被初始化不能再次赋值。 本地变量必须在声明时赋值。因为没有初始化的过程 匿名类中所有变量都必须是final变量。...JMM 可以确保读线程 C 至少能看到写线程 A 构造函数对 final 引用对象的成员域的写入。即 C 至少能看到数组下标 0 的值为 1。

1.3K20

C语言】函数——栈帧的创建和销毁

经典的计算机科学,栈被定义为一种特殊的容器,用户可以将数据压入栈(入栈,push),也可 以将已经压入栈的数据弹出(出栈,pop),但是栈这个容器必须遵守一条规则:先入栈的数据出 栈(First...,这3个寄存器的函数随后执行可能会被修改,所以先保存寄存器原来的值,以便在退出函数时恢复。...//下面的代码是初始化main函数的栈帧空间。 //1. 先把ebp-24h的地址,放在edi //2. 把9放在ecx //3. 把0xCCCCCCCC放在eax //4....esp,8 00BE1860 mov dword ptr [ebp-20h],eax call 指令是要执行函数调用逻辑的,执行call指令之前先会把call指令的下一条指令的地址进行压栈 操作,这个操作是为了解决当函数调用结束要回到...为函数分配好栈帧空间之后,栈帧空间初始化一部分之后,给局部变量栈帧中分配空间,这就是局部变量的创建 为什么局部变量不初始化内容是随机的?

53610

动态内存管理

(数组越界访问属于这种); 2.当一个内存被释放,其存的值并不会被改变,只是其不能再访问了。...• 与函数 malloc 的区别只在于 calloc 会在返回地址之前把申请的空间的每个字节初始化为全 0。...返回值则是新空间的首位地址 如果后面有足够大空间,就较简单了,就原有内存之后直接追加空间,原来空间的数据不发⽣变化(原本的开辟空间并不会改变,追加的空间并不会被初始化), 返回值则为原空间首位地址...getmemory创建了数组p,而后使用完该函数就销毁了该函数开辟的栈帧(空间)。使str接受的地址变为未开辟的空间,该指针变为野指针。...从而在后续用printf函数时其开辟的空间肯定会与getmemory之前开辟的空间有重叠,其printf函数使用时可能就会重置到数组p所在的空间,其中的值就会被改变,从而打印str时出现上述这种情况。

10610

C++_引用

,实现了函数向外的输出,实现了”多返回值” 如果一个参数只是输入到函数,在内部的改变不会影响外部变量的改变的就是输入型参数;一个参数函数内发生的改变影响函数外变量的改变的就是输出型参数 void Add...(int a, int b, int &c) { a += a; b += b; c = a + b; } int main() { int a, b; a = 1;...b,传入到A n时局部变量,A,且n是返回值return。...return作为函数结束标志,此时会产生一个临时变量tem作为n的拷贝,然后将tem传向main 为何要产生一个临时变量,因为离开函数A作用域之后n的内存会被释放,无法被访问,因此在此之前要生成临时变量...引用在定义时必须初始化,指针没有要求 引用在初始化时引用一个实体,就不能再引用其他实体,而指针可以在任何时候指向任何一个同类型 实体 没有NULL引用,但有NULL指针 sizeof含义不同:引用结果为引用类型的大小

26130

干货——聊聊内存那些事(基于单片机系统)

代码区存放着程序编译的CPU指令 函数名称是一个指针,可以通过查询函数名称所处的内存地址,查询函数存放的区域 //函数声明 void dong(); //主函数定义 int main(void)...代码虽然看似初始化c变量三次,其实实际只有第一次有效。 ? 堆区 ? 堆区是调用malloc函数来申请的内存空间,这部分空间使用完要调用free()函数来释放申请的空间。...内存释放(使用free函数之后指针变量p本身保存的地址并没有改变),需要将p的值赋值为NULL(拴住野指针)。...为什么Rom还要有RW,因为掉电RAM中所有的数据都丢失了,每次上电RAM的数据是被程序赋值的,每次这些固定的值就是存储ROM的,为什么不包含ZI段呢,是因为ZI数据都是0,没必要包含,只要查询运行之前将...ZI也是变量,同理:变量不能存储ROM程序运行的最初阶段,RO的指令完成了这两项工作C程序才能正常访问变量。否则只能运行不含变量的代码。

61410

C++基础闯关100题,你能闯多少?【2021超硬核大厂高频面试题】

全局/静态存储区,全局变量和静态变量被分配到同一块内存以前的C语言中,全局变量又分为初始化的和未初始化的,C++里面没有这个区分了,他们共同占用同一块内存区。...指针是一个变量,存储的是一个地址,引用跟原来的变量实质上是同一个东西,是原变量的别名 指针可以有多级,引用只有一级 指针可以为空,引用不能为NULL且定义时必须初始化 指针初始化可以改变指向...,但不是同一个变量,函数改变这个变量的指向不影响实参,而引用却可以。...C初始化发生在代码执行之前,编译阶段分配好内存之后,就会进行初始化,所以我们看到C语言中无法使用变量对静态局部变量进行初始化程序运行结束,变量所处的全局内存会被全部回收。...C++初始化执行相关代码时才会进行初始化,主要是由于C++引入对象,要进行初始化必须执行相应构造函数和析构函数构造函数或析构函数中经常会需要进行某些程序需要进行的特定操作,并非简单地分配内存

1.9K20

c++入门】引用,内联函数,auto

当打印a和b的地址时,会看到它们的地址是相同的 b就是a的别名 1.1引用特性 引用必须被初始化 C++,声明引用时必须同时进行初始化。...通过引用参数,可以直接修改传入的变量,而无需担心指针解引用和地址操作,这使得代码更加安全、清晰 但是由于引用定义不能改变指向,引用不能替代指针 当然,这里swap函数取名字也可以取x,y,因为他们不同作用域...这意味着,函数 func 外部,我们无法安全地访问变量 a 当函数被调用时,一个栈帧(stack frame)就会被分配给这个调用。...这个返回值副本通常是通过寄存器传递给函数的调用者, main 函数, int ret = func(); 一句捕获了 func 返回的 a 的副本,并将其存储 main 的局部变量 ret 。...; return 0; } 引用底层是用指针实现的 引用必须初始化,指针可以不初始化 引用不能改变指向,指针可以 引用相对更安全,没有空引用,但是有空指针 sizeof含义不同:引用结果为引用类型的大小

8210
领券