C语言05-函数及函数的调用过程

C语言函数

  • 函数声明
    有声明没实现的函数会出现链接错误
  • 函数实现
    函数实现和声明可以在一个文件也可以不在一个文件。
  • 函数调用
  • 实参、形参
    形参是声明中占位的参数
    实参是调用函数的作者,给的具体的值
  • return与返回值
    return语句用于函数返回,并带回返回值(如果有)。
    对于函数调用的一方,可以将函数调用的返回值直接存储下来,也可以直接放弃。
    比如:

C语言的变参函数

集中处理非常朴素的道理,实践以下代码,变参函数的话题。。。

函数调用过程

函数本质

有意义机器码
通过反汇编窗口确认

另外,也可以从打印函数名确认:

void FunTest()
{
    printf("hello");
    return;
    printf("hello, world");
    printf("hello, world");
    printf("hello, world");
    printf("hello, world");
    printf("hello, world");
}
void FunValArg(char* arg1, ...)
{
    
}
void MyAddVal(...)
{
    
}
int main(int argc, char* argv[])
{
    printf("%08X, %08X\r\n", FunValArg, FunTest);
    return 0;
}

内存区域的区分技巧

操作系统为了好管理,内存是分区域的。
对于去掉随机基地址的工程,可以通过内存地址,简单区分它属于哪块内存区域。

  • 全局区域
    一般以0x004x开头,存放:函数的机器码、字符串、全局变量
  • 栈区域
    一般以0x0018、0x0019、0x0012开头,函数中的局部变量、函数调用过程中的形参都会放在栈中
  • 堆区域:暂时不用掌握
    实践以下代码加深理解。
int main(int argc, char* argv[])
{
    printf("%08X\r\n", "hello");
    char chAry[] = "hello";
    printf("%08X\r\n", chAry);
    char *szHello = "hello";
    printf("%08X\r\n", szHello);
    return 0;
}

函数调用过程

int MyAdd(int x, int y);
int main(int argc, char* argv[])
{
    int nResult = 0;
    nResult = MyAdd(10, 20);
    return 0;
}
int MyAdd(int x, int y)
{
    int nLocal1 = 0x1111;
    int nLocal2 = 0x2222;
    return x + y;
}

涉及两方:

  • 调用方(main)
  • 被调用方(MyAdd)

函数的调用及返回的过程,就是在调用方和被调用方切换流程的过程。 又因为函数肩负着接口的作用,所以,除了流程切换之外,还需要保证:(过得去,回得来)

  • 调用方传递的参数,可以被被调用方正确的获取
  • 被调用方要能够传递出返回值,并且被调用方正确的获取

栈帧的概念

内存区域有一块被划分为栈,所有被调用的函数,都会使用这块区域,但是,他们的局部变量、参数等并不会重叠。
每一次函数被调用,都有特定的一块占内存与这次调用对应,这称为“栈帧”。
开始调用某函数,会自动分配栈帧空间。
如果某函数调用结束,那么会回收栈帧空间,这个过程,称为平衡栈。

函数调用过得去,回得来的细节:

大概轮廓:

  • 按照调用约定传参
  • 保存返回地址
  • 流程转移到被调用函数
  • 保存上一层栈帧的地址
  • 开辟局部变量空间
  • 执行被调用函数的相关代码
  • 最终将返回值存放在寄存器eax中
  • 返回到调用方

按照调用约定传参

C语言的默认调用约定,具体的操作是:

  • 从右往左传参
  • 将参数依次push到内存的栈中
  • 注意详细讲解:

按照调用约定传参

  • 从右往左传参
  • 将参数一次push到内存栈中

实际上,C语言调用约定有三种:

  • C约定:从右往左传参,通过内存栈区域传参,调用平衡栈(调用方通过汇编,修改esp及存取)
  • _stdcall:传参方向和传参介质(内存)都与C约定一致。被调用方平衡栈。
  • _fastcall:传参方向从左往右,介质是寄存器+内存,被调用方平衡栈
int _cdecl MyAdd(int x, int y);
int _stdcall MyAdd2(int x, int y);

int main(int argc, char* argv[])
{
    int nResult = 0;
    nResult = MyAdd(0x10, 0x20);
    nResult = MyAdd2(0x10, 0x20);
    return 0;
}

int MyAdd(int x, int y)
{
    int nLocal1 = 0x1111;
    int nLocal2 = 0x2222;
    return x + y;
}

int _stdcall MyAdd2(int x, int y)
{
    int nLocal1 = 0x1111;
    int nLocal2 = 0x2222;
    return x + y;
}

节省空间角度而言,_stdcall更优秀

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值