编程基础:过程调用

一、函数调用

1、面向问题

        函数调用符合我们人类的一种思维方式,即解决一个问题需要几个步骤呢?首先,其次,再次,最后…以及每一步骤是不是正确工作了?

        与之相对的编程语言形式就是:

#include <iostream>
using namespace std;

/*返回0 表示函数工作没有差错*/
int first(void)
{
	return 0;
}

int second(void)
{
	return 0;
}

int last(void)
{
	return 0;
}
/*main函数 用来解决一个问题*/
int main(void)
{	
	/*res 用来检查每一步是否无差错的完成*/
    int res = 0;
	/*第一步*/
	res = first();
	/*第一步出现差错,不要继续啦*/
	if(res)
	return -1;
	
	/*第二步*/
	res = second();
	/*第二步出现差错,不要继续啦*/
	if(res)
	return -1;
	
	/*第三步*/
	res = last();
	/*第三步出现差错,不要继续啦*/
	if(res)
	return -1;
	
	/*表示main解决了问题*/
	return 0;
}

2、调用嵌套

        实际,一个函数调用可能比上面例子更复杂:函数可以调用子函数,被调用子函数内部还能继续调用子函数呢!
        这就形成了调用嵌套,相伴的问题就是函数栈帧…

假设:函数main调用了do_line函数,do_line 函数又调用了cmd_add函数

  • 对于C/C++语言,进程栈上的数据应当如下图;
    通常来讲,栈帧中存储的数据可能有:局部变量部分寄存器函数参数返回值程序计数器等…

说明!!!关于函数栈的布局完全可以作为一个单独的专题,读者可以参考《程序员的自我修养—链接、装载与库》一书

在这里插入图片描述

二、Goto语句(短跳转)

1、面向问题

        小编这里突然想到一个笑话,如果有一个函数调用不能解决的问题,一定可以用多个函数调用解决
        设想,当把一个问题的解决方案分成第一步,第二步,第三步的时候,万一执行到第二步的时候出错并且需要重头从第一步做起呢? 要不我们在第二步失败的地方再调用第一步函数?(想想都觉得不靠谱)
答案就是goto语句。当然出去异常处理外,goto语句也可以用作它用,比如优化代码可读性

/*main函数 用来解决一个问题*/
int main(void)
{	
	label:
	/*res 用来检查每一步是否无差错的完成*/
    int res = 0;
	/*第一步*/
	res = first();
	/*第一步出现差错,怎么从新从第一步开始呢?*/
	goto label;
	/*第二步*/
	res = second();
	/*第二步出现差错,怎样从第一步开始呢?*/
	goto label;
	
	/*第三步*/
	res = last();
	/*第三步出现差错,怎么样重头从第一步开始呢?*/
	goto label;	
	/*表示main解决了问题*/
	return 0;
}

2、基本原理

goto 语句不能跨函数调用:
goto语句简单的改变了程序计数器的指针(指针在函数体表示的范围内),从而实现程序的跳转

3、代码示例

代码示例读者可以参考另一篇文章C/C++ goto 语句

三、longjmp/setjmp(长跳转)

1、面向问题

        函数错误并不是出现在一个函数体内部,而是出现在多个函数调用层次之间,这个时候goto语句是不能完成异常处理的…
        比如本节的代码示例,level_two函数调用时出错,这个错误信息是没有办法通过函数返回传递给main函数的,这个时候不得不使用longjmp与setjump

2、基本原理

#include <setjmp.h>
int setjmp(jmp_buf env);
void longjmp(jmp_buf env, int val);

  • setjmp 通过全局结构体记录这一刻栈帧状态:程序计数器,栈指针,部分自动变量等
  • longjmp 通过全局结构体,强行恢复程序计数器,栈指针(部分自动变量)等从而实现程序跳转

3、代码演示

#include <iostream>
using namespace std;

#include <csetjmp>
jmp_buf jmp_buff_01;

void level_two(void)
{
	cout << "level_two function" << endl;
	longjmp(jmp_buff_01,1);
}

void level_one(void)
{
	level_two();
}

int main(void)
{	
	if(!setjmp(jmp_buff_01))
	{
		
	}
	else
	{
		cout << "error" << endl;
		return -1;
	}

	level_one();

	return 0;
}

4、引入问题

首先:
longjump 跳过的函数栈可能忘记释放动态内存!!!

其次:
通过longjmp函数,程序重新开始执行(位于进程栈中的程序计数器值回复到setjmp的状态)
但是那些位于进程栈中的自动变量是否也会自动恢复呢?答案是未定义…读者肯定会大骂 What!?

读者可以参考《UNIX环境高级编程_第二版中文》一书有关setjmp 回滚问题一书:为了避免回滚带来的局部变量不可控风险,程序作者可以尽量谨慎的设计回滚后的处理代码

四、参考书籍

《程序员的自我修养—链接、装载与库》
《UNIX环境高级编程_第二版中文》

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值