ascii源于电报
换行 \r 本来是纸张前移,x轴不变,y加一
回车 \n 针脚复位,x轴归零,y不变
PC中,不同编译环境不同操作系统对\n有不同理解
微软的回车 列清零 行加一 等于以前融合的\r\n
仍然有些系统沿用电报系统
函数调用
函数解决问题 解决问题依赖关系 后进先出
有点像递归的解决路线
计算机结构 栈结构
每个函数有独立栈环境,不同栈环境的变量相互隔离

通常还要表上零地址的位置,如下图不停压栈的时候作减法,调用方和被调用方需要确定栈结构生长方向,属于体系结构问题。

实际上这些地方都是有参数空间的


-
按调用约定传递参数 调用约定 需要调用方caller和被调方callee作出以下约定: 参数的传递方向 从左往右 从右往左 参数的传递媒介 用栈传递 寄存器传递 函数值返回的位置 栈 寄存器 释放参数空间的负责方 有且仅有一方可以释放参数空间
-
保存返回地址
-
保存调用方栈信息 esp?栈帧? 栈底比较好访问栈内信息,所以保存的是栈底
-
更新当前栈顶到栈底
把当前栈顶作为被调方的栈底
-
为局部变量申请空间 抬高栈顶 调试的时候,一般都是给足栈空间,通常大于所有局部变量等之和。 发布版的时候,一般会小于之和,编译器会做一些优化,例如变量复用等等操作
-
保存寄存器环境 把即将使用的寄存器原值保存在栈中(其实就三个,一共八个寄存器,三个参数传递不用,两个栈寄存器。) 调试版全保存,发布版保存需要的
-
如果编译器有/ZI /Zi等调试选项的 局部变量初始化为0xcccccccc
-
执行函数体
-
要出来了 恢复寄存器环境
-
释放局部变量的空间 调动栈顶指针
-
恢复调用方的栈信息
-
cdecl约定,取出当前栈顶作为返回的流程地址,返回调用方后,由调用方清理参数空间。 其他约定,取出当前栈顶作为返回的流程地址,再由被调方清理参数空间后才返回
递归中,函数返回地址都相同,一眼可以在栈中间看出来
- C约定_cdecl 参数使用栈传递,从右往左,返回值在寄存器,caller负责释放
- 标准库约定_stdcall 栈传递 右往左 寄存器 callee释放
- _fastcall 左数前两个寄存器传 其他栈传递 右往左 返回在寄存器 callee释放 微软独有
此处和编译器选项描述调用约定


来找一个程序试试
#include "stdafx.h"
#include <iostream>
#include <stdio.h>
using namespace std;
int sum(int m, int n)
{
int sum1 = n + m;
return sum1;
}
int main(int argc, char* argv[])
{
int n=1;
int i=0;
for (i=0;i<9;i++)
{
n=n+1;
n=n*2;
}
printf("%d\r\n",n);
int m = 5;
int p = 6;
int c = sum(m,p);
printf("%d\r\n",c);
system("pause");
return 0;
}
下断点下这里

观测到内存空间里
下面就是调用main的时候栈做的一些操作
利用的

1 1 命令行个数 整型
1 2 命令行列表 字符型指针数组
1 3 环境变量列表 字符型指针数组
2 main函数返回地址
3 调用方栈信息 同时此处作为main函数的栈底
4 main函数局部空间
5 三个dword 寄存器环境
然后根据一下走到sum函数里面会是咋样
00000006 00000005 0040130D 0019FF30 很多CC 然后三个dword
40130D根据这里来,是call后面要返回的位置

0019FF30 就是这里,main函数的栈底

分区
data 全局变量静态变量 常量
