函数

如果一个函数有声明没实现,那么就会出现链接错误:

以上代码会出现链接错误。

  • 函数实现
int MyTest(int x, int y)
{
return x + y;
}

以上是函数实现,函数实现可以与声明放在同一个文件中,也可以不在同一个文件

中。

  • 函数调用

    在运行过程中,函数名+括号+实参,可以实现函数调用。

  • 实参与形参的概念

    所谓的形参,就是在函数实现过程中,占位的参数,比如shang上方代码中的x,y都是形参

    函数的作者,在实现函数时,是不知道函数调用时,形参的具体值的。在函数调用时,调用函数的作者,是具体知道参数的值,那个值,就是实参。

    以上的5,6就是实参(实际传递的参数)。

  • return与返回值

    return语句用于函数返回,并带出返回值(如果有)。

    对于函数调用那一方,可以将函数调用后的返回值存储下来,也可以直接放弃。

    除了“带出返回值”的作用外,还要加强“函数返回”的理解。所谓的函数返回,具体说就

    是将执行流程跳转到调用当前函数的那一方

C语言中的变参函数

  • printf在MSDN中奇怪的资料

以上的中括号,其实是技术文档的约定,表示可选内容。所以,从C语言的角度看,

printf的函数声明,其实是:

以上的三个英文句号(...),代表参数个数可变。也就是说,函数调用过程中,传递任意

多个实参,都会被认为语法正确。

变参函数,是我们可以发明更方便的函数,比如,计算任意多个正数的和。

另外,要注意,变参语法并没有颠覆之前学习的规则,也就是说,函数声明(又

称为函数原型)与函数的调用必须匹配。

实践以下代码加深对声明与调用必须匹配的理解

int  main(int argc, char* argv[])
{
char *szHello = ("hello,%d");
printf(szHello,10); }

函数的本质是什么

函数本身,其实就是一堆有意义的机器码。

我们可以通过调试过程中,反汇编窗口去确认。

内存区域的区分技巧

操作系统为了好管理,内存是分区域的。

对于去掉随机基地址的工程,我们可以通过内存地址,简单区分它属于哪块内存区

域。

全局区域:一般以0x004x开头,全局区域中一般存放:函数的机器码、字符串、

全局变量

栈区域:一般以0x0018、0x0019、0x0012开头,函数中的局部变量、函数调用

过程中的形参都会放在栈中

堆区域:暂时不用掌握

函数的调用过程

函数的调用过程,涉及两方:

函数的调用方(main)

函数的被调用方(MyAdd)

函数的调用及返回的过程,就是在调用方和被调用方切换流程的过程。

又因为函数肩负着接口的作用,所以,除了流程切换之外,还需要保证:

调用方传递的参数,可以被被调用方正确的获取

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

流程转移方面:过得去,回得来

那么,C程序中,函数调用过程的细节是如何的呢?

栈帧的概念

内存区域有一块被划分为栈,所有被调用的函数,都会使用这块区域,但是,他们的

局部变量、参数等并不会重叠。

每一次函数被调用,都有特定的一块占内存与这次调用对应,这称为“栈帧”。

开始调用某函数,会自动分配栈帧空间。

如果某函数调用结束,那么会回收栈帧空间,这个过程,称为平衡栈。

调用过程细节

大概的轮廓,逐一详细解释:

在函数调用过程中

按照调用约定传参(将实参复制到栈中)

保存返回地址

流程转移到被调用函数

保存上一层栈帧的地址

开辟局部变量空间

执行被调用函数的相关代码

最终将返回值存放在寄存器eax中

返回到调用方

按照约定传参

们为了不将问题复杂,我们现在只讨论C语言的默认调用约定,它具体的操作是:

从右往左传参

将参数依次push到内存的栈中

【实际操作请见视频】

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

C约定:从右往左传参,通过内存栈区域传参,调用方平衡栈(调用方通过汇

编,修改esp及存取)

_stdcall:传参方向和传参介质(内存)都与C约定一致。被调用方平衡栈。

_fastcall:传参方向从左往右,介质是寄存器+内存,被调用方平衡栈

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

面试题:printf是什么约定?为什么?

答:C约定。因为printf是变参函数,变参函数只有调用方才知道具体传递了几个参

数,所以必须需要调用方平衡栈。

在C语言中函数及其调用过程的更多相关文章

  1. C语言中函数的调用方式

    第一眼看到这样一个题目的我,心想除了如下的直接调用还能怎么调用呢? 1 void fun(void) 2 { 3 ...... 4 //你的代码 5 ..... 6 } 7 int main(void ...

  2. C语言中函数参数传递

    C语言中函数参数传递的三种方式 (1)值传递,就是把你的变量的值传递给函数的形式参数,实际就是用变量的值来新生成一个形式参数,因而在函数里对形参的改变不会影响到函数外的变量的值.(2)地址传递,就是把 ...

  3. C语言中函数返回字符串的4中方法

    C语言中函数返回字符串的4中方法 函数的构成部分:返回类型.函数名称.参数.函数主体 参数:函数调用时传入的参数称为实参,函数定义时出现的参数为形参 形参的作用在于接收实参传入的值,形参和函数内部的其 ...

  4. c语言中函数的简单介绍

    c语言中函数的介绍: 函数,简单的说就是代码的打包.存放在一个地方,当需要的时候调用. 函数分类: 1.无参无返回值函数 void func() 2.无参有返回值函数  int func() 3.有参 ...

  5. [Android Pro] 深入理解函数的调用过程——栈帧

    cp :http://blog.csdn.net/x_perseverance/article/details/78897637 每一个函数被调用时,都会为函数开辟一块空间,这块空间就称为栈帧. 首先 ...

  6. C/C++/动态链接库DLL中函数的调用约定与名称修饰

    参见:http://blog.twofei.com/cc/impl/calling-convension.html 调用约定(Calling Convention)是指在程序设计语言中为了实现函数调用 ...

  7. JavaScript中函数的调用

    JavaScript中函数的调用 制作人:全心全意 在JavaScript中,函数定义后并不会自动执行,要执行一个函数需要在特定的位置调用该函数,调用函数需要创建调用语句,调用语句包含函数名称和参数. ...

  8. 代码 or 指令,浅析ARM架构下的函数的调用过程

    摘要:linux程序运行的状态以及如何推导调用栈. 1.背景知识 1.ARM64寄存器介绍: 2.STP指令详解(ARMV8手册): 我们先看一下指令格式(64bit),以及指令对于寄存机执行结果的影 ...

  9. object C—类中函数的调用

    Object C-类中函数的调用 创建,三个类.然后,在代码中调用相同名字的函数.观察他们的调用次序. @interface test : NSObject - (void)print; @end @ ...

随机推荐

  1. WCF概述

    Tips:概念性的东西仅助理解,可以略过 概述 1.SOA概述 1).从三个问题开始 SOA是什么——面向服务架构.一种编程模式.一种架构模式.它将应用程序分成不同功能(服务)单元,再通过服务之间的接 ...

  2. java上传视频文件

    需求:项目要支持大文件上传功能,经过讨论,初步将文件上传大小控制在500M内,因此自己需要在项目中进行文件上传部分的调整和配置,自己将大小都以501M来进行限制. 第一步: 前端修改 由于项目使用的是 ...

  3. HDU 5727 Necklace ( 2016多校、二分图匹配 )

    题目链接 题意 : 给出 2*N 颗珠子.有 N 颗是阴的.有 N 颗是阳的.现在要把阴阳珠子串成一个环状的项链.而且要求珠子的放置方式必须的阴阳相间的.然后给出你 M 个限制关系.格式为 ( A.B ...

  4. Remainder Problem

    F. Remainder Problem 这个其实并不难,只是看看考察有没有分块的思路 思路:用一个ans[i][j]来记录所有k=(1~5e5)中所有a[k]%i==j的和,在查询的时候可以达到复杂 ...

  5. 设置Linux自启服务以及优先级

    一. 启动优先级 今天有一台服务器没有正常启动,原因是有一个服务没有启动起来,因为A服务需要B服务启动之后才能正常启动,所以需要调整A,B服务的启动顺序.在网上查找了一些资料,总结了一下,以备以后需要 ...

  6. IDEA安装配置Thrift

    下载exe:http://archive.apache.org/dist/thrift/0.9.3/ 新建一个thrift目录放进来,并将名字改成thrift

  7. 20165207 Exp6 信息搜集与漏洞扫描

    Exp6 信息搜集与漏洞扫描 一.实验内容 1. 查询基本信息 1.1. 使用whois.nslookup.dig命令查询DNS和ip信息 使用whois查询某个我经常用的学习网站的域名(去掉了前缀的 ...

  8. 如何在Linux上安装Composer

    我们可以通过 Composer 镜像安装 composer ,在这之前,请确认您的机器上已经安装了PHP,并可以通过php来执行命令.如果您的服务器上必须通过全路径来执行(/usr/local/php ...

  9. ping包的checksum校验和

    PING包发送里面有一个比较关键的就是checksum即校验和 checksum本来就是ICMP包内的数据 怎么又从ICMP包开始计算?后来看了一下文档,就是就算ICMP的时候checksum本身 的 ...

  10. vmvare ubuntu虚拟机固定ip设置

    1.vmvare网络设置 2.window网络设置 3.ubuntu网络设置 先ifconfig 查看ip 已经设置为192.168.122.128 sudo vim /etc/network/int ...