C 碎片六 函数
一、程序编译执行过程
程序的编译执行过程分为4个阶段:预处理阶段、编译阶段、汇编阶段、连接阶段
1. 预处理阶段:预处理器(cpp)处理以头文件、宏、条件编译(字符#开头)等内容的替换。此阶段不进行语法检查,只进行简单的替换工作,修改原始的C程序,得到另一个C程序,通常以.i作为文件扩展名,产生的.i文件会变大(PS:增加了替换后的内容)。
gcc -o hello.i -E hello.c
2. 编译阶段:编译器(ccl)进行词法分析和语法分析之后,将文件hello.i翻译成文件hello.s。它包含一个汇编语言程序。汇编语言程序中的每条语句都以一种标准的文本格式确切地描述了一条低级机器语言指令。汇编语言为不同编译器提供了通用的输出语言。
gcc -o hello.s -S hello.i
3. 汇编阶段:汇编器(as)将hello.s翻译成机器语言指令,并将结果保存在目标文件hello.o中。hello.o是一种二进制文件,它的字节编码是机器语言指令而不是字符。对于被翻译系统处理的每一个C语言源程序,都将最终经过这一处理而得到相应的目标文件,目标文件中所存放的也就是与源程序等效的目标的机器语言代码。目标文件由段组成,通常一个目标文件中至少有两个段:
3.1 代码段:顾名思义就是存放程序代码的段,主要存放一系列的指令。
3.2 数据段:主要存放程序中要用到的各种全局变量或静态的数据。一般数据段都是可读,可写,可执行的。
gcc -o hello.o -c hello.s
4. 连接阶段:连接器(ld)将有关的目标文件彼此连接起来,因为程序源文件中的函数可能引用了另一个源文件中定义的某个符号(如变量或者函数调用等),也可能调用了某个库文件中的函数,都需要经链接程序的处理方能得以解决。也即将在一个文件中引用的符号同该符号在另外一个文件中的定义连接起来,使得所有的这些目标文件成为一个能够诶操作系统装入执行的统一整体。用下面的gcc命令后会生成一个test的可执行文件,执行的时候直接 ./test 即可。
gcc -o test hello.o
总结:
-E Preprocess only; do not compile, assemble or link
-S Compile only; do not assemble or link
-c Compile and assemble, but do not link
-o Place the output into
二、内存布局
在内存中,布局从低地址到高地址,依次是只读区(代码区),读写区(数据区),堆区,栈区,如图左上。地址增长方式如图右上。其中,堆区,栈区也叫动态区域;只读区,读写区也叫静态区域。
只读区:存放程序编译后的代码,和只读变量。特点:只读
读写区:存放全局变量,静态变量和字符串常量。特点:可读写
堆区:调用malloc函数会在堆区开辟空间。特点:主动去释放free
栈区:普通函数调用,压栈/出栈会在栈区开辟/释放空间。特点:先进后出FILO
三、变量的生命周期及作用域
参考上图:
1. C语言中的每个变量有两个属性:数据类型(整形、浮点型、字符型),还有数据存储类别,分别为自动的(auto),静态的(static),寄存器的(register)和外部的(extern),下面逐一说明:
auto类型:函数中的局部变量不加特殊声明都是auto变量,但是关键字"auto"可以被省略。这些变量在函数被调用时分配存储方式,函数调用结束后这些存储空间就被释放了
static类型:被static声明的变量为静态(局部/全局)变量,静态局部变量函数调用结束后,这些变量不消失,而保留当前数据,下一次调用时变量的值为上一次调用完成后的值
register类型:Register修饰符暗示编译程序相应的变量将将被频繁使用,如果可能的话,应将其保存在CPU的寄存器中,以指加快其存取速度。但是,使用register修饰符有几点限制:
(1)只有局部自动变量和形式参数可以作为寄存器变量,其他(如全局变量)不行。
(2)一个计算机系统中的寄存器数目是有限的,不能定义任意多个寄存器变量。
(3)局部静态变量不能定义为寄存器变量。
其实这个变量已经过时,因为现在的计算机处理速度够快,所以很少使用
extern类型:它不是一个定义,而是一个声明,他表示这个变量或者函数的定义在别的文件中。extern使用时,告诉编译器去其他文件找对应变量。
在C语言中,只有extern,static可以修饰函数。函数被默认定义为extern;static函数只能被本文件中的函数调用,而不能被同一程序其它文件中的函数调用。
2. 下面是 全局变量,静态全局变量,静态局部变量,局部变量 的比较,作用域全局变量 > 静态全局变量 > 静态局部变量 > 局部变量
局部变量:只要在{}内,包括代码块、函数体内,声明的变量就是一个局部变量,如果不给初始化默认是随机的数
(1)作用域(在代码中的使用范围):从包含变量声明的{}内开始到这个{}结束
(2)内存位置:在所在函数的函数栈空间中
(3)生命周期:从声明开始到当前函数栈释放
全局变量:如果不给初始化那么默认是0
(1)作用域:在整个程序整个工程,所有函数所有文件都可以使用,共享这个变量空间
(2)内存位置:数据段(跟栈段没有关系,内存中的堆段,栈段,代码段,数据段,都是相互独立的,数据段不会伴随函数栈的释放而释放)
(3)生明周期:编译代码的时候大小就确定了,程序一旦开始全局变量的空间就会创建,程序结束那么数据段全局变量空间才会释放
静态变量:static修饰的变量(在编译的时候就已经执行了,在函数运行时就不会执行了)
静态局部变量:在{}中用static修饰的变量
(1)作用域:在包含声明静态变量的大括号内
(2)内存:数据段
(3)生明周期:编译的时候就确定大小了,程序运行开始创建空间,程序结束空间释放
静态全局变量:在函数外用static修饰的变量
(1)作用域:在当前声明静态全局的文件内用
(2)内存:数据段
(3)生明周期:编译的时候就确定大小了,程序运行开始创建空间,程序结束空间释放
四、函数初步
#include <stdio.h> //自定义函数add
int add(int a, int b) { return a+b;
} //自定义函数printStar
void printStar(void) {
printf("****\n");
return;
} //main函数
int main(int argc, const char * argv[]) { //函数只有调用了才会执行里面的代码
//调用函数了才会进行压栈push操作
int ret = add(3, 5); printf("ret:%d\n",ret); printStar();
//函数执行完会进行pop出栈操作
return 0;
}
分析:上述程序代码的执行过程:
1. 先编写代码,编写完之后进行编译,编译会产生一个可执行文件(可执行文件:就是生成一个二进制文件),这时代码源码文件(.c)和可执行文件会放在硬盘上
2. 执行/运行可执行文件(二进制文件),cpu首先会把这个二进制文件的内容拷贝到内存中的代码段;然后cpu开始执行代码段中这个二进制文件的内容
(1)cpu 会从二进制文件中的main函数标号开始,调用main函数(这时会在栈段压一个main栈)执行main函数中的代码(从上至下)
(2)先执行第一句代码,遇到了int ret = add(3,5);先调用add(3,5) (这时会压/push一个add函数栈)执行add里面的代码,执行中遇到return函数返回到调用的地方(add函数栈就会出栈/pop,栈会释放),会返回值给ret空间
(3)接着执行printf函数(压printf栈),执行完之后返回调用的地方(printf 出栈)
(4)接着执行下面的printStar()(压一个printStar栈),printStar执行中遇到了return 这时printStar函数返回到调用的地方(printStar栈出栈 栈释放)
(5)最后main中遇到了return 那么main函数返回 (main栈出栈 释放)整个程序结束退出
1. 什么是函数
(1)函数是一个可以实现一个具体功能的代码块
(1)有名的代码块
2. 函数的分类
(1)库函数、printf scanf pow abs
(1)自定义函数 自己实现的
3. 函数定义
函数声明格式:返回值类型 函数名(参数);
函数调用格式:函数名(参数)
函数三要素:返回值 函数名 参数
五、函数作用
1. 函数的作用
(1)函数使我们的程序清晰明白
(2)为开发人员提供解决问题的方法:细化
(3)一次定义,处处使用,利用以有的代码
(4)抽象出公共的部分,隔离开易变部分
2. 函数用法
(1)使用之前必须先定义
(2)通过函数调用来使用,类似上下级管理形式
(3)调用时指定函数名字和所需要的信息(参数)
(4)调用完成后向老板报告工作,递交报告(返回值)
六、自定义函数
1. 什么情况下自定义函数
(1)需要一个功能相对独立的子模块
(2)一段代码多次使用
2. 如何自定义函数
(1)明确函数功能,起一个有意义的函数名(标识符)
(2)参数和返回值类型:考虑清楚,需要几个参数;是否需要返回值,什么类型?返回值最多一个
(3)声明函数原型,建议放在头文件中
(4)定义函数体内容
七、递归函数
自己调用自己的函数就是递归函数,递归函数一般解决数学推理问题
递归函数调用过程如图
汉诺塔问题:有三根柱子 A B C . A柱子上有N个盘子,要求把这N个盘子从A可以借助柱子B移动到柱子C上
1. 这个N个盘子大小不同,必须是小盘子在大盘子上面
2. 每次只能移动一个盘子
C 碎片六 函数的更多相关文章
- 深入理解PHP内核(六)函数的定义、传参及返回值
一.函数的定义 用户函数的定义从function 关键字开始,如下 function foo($var) { echo $var; } 1.词法分析 在Zend/zend_language_scann ...
- JavaScript基础学习(六)—函数
一.函数的定义 1.function语句形式 //1.function语句式 function test1(){ alert("I am test1"); } test1(); 2 ...
- C++学习基础十六-- 函数学习笔记
C++ Primer 第七章-函数学习笔记 一步一个脚印.循序渐进的学习. 一.参数传递 每次调用函数时,都会重新创建函数所有的形参,此时所传递的实参将会初始化对应的形参. 「如果形参是非引用类型,则 ...
- python成长之路六-函数的初识
定义函数 我们现学已知的python函数有<内置函数> 而我们现在要学的是<自定义函数> 1,def 定义一个函数 def name(): # 后接函数名 冒号 pass 2 ...
- STL学习笔记(六) 函数对象
条款38:遵循按值传递的原则来设计仿函数 仿函数都是 pass-by-value Function for_each(InputIterator first, InputIterator last, ...
- 【Swift】学习笔记(六)——函数
函数 懂编程语言的来说这个是最主要的了,不论什么语言都有函数这个概念.函数就是完毕特定任务的独立代码块. 函数怎么创建: 1.创建一个无參无返回值的函数(实际上全部的函数都有返回值,这个函数返回vo ...
- Python基础(六) 函数
.函数 函数是对动作的封装 2.1函数的基本结构 #函数的定义 def 函数名(): #函数提 pass #函数的执行 函数名() 2.2参数初识 #形参 def hanshu(aaa): #参数相当 ...
- Python开发的入门教程(六)-函数
介绍 本文主要介绍Python中函数的基本知识和使用 Python之什么是函数 我们知道圆的面积计算公式为: S = πr² 当我们知道半径r的值时,就可以根据公式计算出面积.假设我们需要计算3个不同 ...
- 1、C语言中的函数指针
一 通常的函数调用 void MyFun(int x); //此处的申明也可写成:void MyFun( int ); int main(int argc, char* argv[]) { MyFun ...
随机推荐
- 异常:Project configuration is not up-to-date with pom.xml解决方案
转自:https://www.cnblogs.com/zhujiabin/p/6343423.html 1. Description Resource Path Location ...
- Linux包管理
1.yum(Yellow dog Updater, Modified) yum是一个在Fedora(基于Linux的操作系统)和RedHat(基于Linux的操作系统)以及SUSE(基于Linux的操 ...
- 1.3 xss原理分析与剖析(4)
0×01 URL编码 URL只允许用US-ASCII字符集中可打印的字符(0×20—0x7x),其中某些字符在HTTP协议里有特殊的意义,所以有些也不能使用.这里有个需要注意的,+加号代表URL编码的 ...
- 【mybatis 的foreach的用法】
foreach一共有三种类型,分别为List,[](array),Map三种. foreach属性 属性 描述 item 循环体中的具体对象.支持属性的点路径访问,如item.age,item.inf ...
- C++开源库(一) ----log4cpp详解
我们在写程序的时候通常会希望将一些信息记录下来,方便我们进行日后的一些信息跟踪,错误排查等等.比如:我们在进行数据库操作的时候,我们通常希望知道现在是程序的哪一部分进行了数据库的操作,所以我们会记录下 ...
- 【leetcode 105. 从前序与中序遍历序列构造二叉树】解题报告
前往 中序,后序遍历构造二叉树, 中序,前序遍历构造二叉树 TreeNode* build(vector<int>& preorder, int l1, int r1, vecto ...
- go语言实战教程:项目文件配置和项目初始化运行
在上节内容中,我们已经搭建了实战项目框架,并将实战项目开发所需要的静态资源文件进行了导入.在本节内容中,我们将讲解如何通过相关的配置,并初始化运行项目. conf配置文件读取配置信息 我们前面说过,使 ...
- java 调用SAP RFC函数错误信息
RFC接口调用SAP如果有异常会通过com.sap.mw.jco.JCO$Exception: 抛出异常 在开发中遇到的异常有如下 用户名密码可能是错误或者用户无权限,确认用户,必要时联系SAP负责人 ...
- myclipse运行web的一些问题
一.修改项目访问路径 项目右键>properties(属性)>输入web搜索>双击web>修改Web-Content root内容即可 二. myeclipse中web项目不自 ...
- sass(scss)10大常用重要特性
用sass用了好久,期初看中的是他的嵌套功能,因为刚开始的时候是用jquery,电脑安装Ruby,全局安装sass,将scss编译为css,不得不说真的很方面,节点套节点,和html的很类似.但是后来 ...