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 ...
随机推荐
- ipcs、ipcrm命令
进程间通信概述进程间通信有如下的目的:1.数据传输,一个进程需要将它的数据发送给另一个进程,发送的数据量在一个字节到几M之间:2.共享数据,多个进程想要操作共享数据,一个进程对数据的修改,其他进程应该 ...
- 菜鸟级的Git与GitHub使用总结(转)
菜鸟级的Git与GitHub使用总结 原创 2016年12月01日 14:58:30 1792 前言 这几天一直在折腾学习Git和GitHub的使用.几天下来,在网上查阅了大量的资料,总算有一些成果. ...
- 使用showOptionDialog显示多项选择框
-----------------siwuxie095 工程名:TestJOptionPane 包名:com.siwuxie095.showdi ...
- Firefox 网页 光标 闪烁
最近 Firefox 出现怪异情况:鼠标点击网页,在点击的位置显示光标,并一直闪烁,导致 Home End 等按键都无效. 原来这是 Firefox 的 “特色功能”:Caret Browsing,激 ...
- SQL中的union,except,intersect用法
限制:所有查询中的列数和列的数序必须相同 union all:完全整合两个结果集查出所有数据 union:查出两个表的数据并且去除重复的数据 except:去重之后只会保留第一个表中的数据,查询a表在 ...
- unity 查找游戏中隐藏的物体
在Hierarchy 有时会隐藏一些游戏物体,我们需要在游戏的时候将其激活状态变为true 我们发现通过 GameObject.Find("隐藏物体名字") 是查找不到隐藏对象的 ...
- C# 将数组转换为以逗号分隔的字符串
例子: string[] array = { "A", "B", "C", "D" }; string str = st ...
- DOMContentLoaded时间触发与js,css,img的关联
DOMContentLoaded触发原理: 1.规范总是那么的晦涩,但至少有一点是可以明确了的,就是在JS(不包括动态插入的JS)执行完之后,才会触发DOMContentLoaded事件. 2.DOM ...
- Boost Python学习笔记(一)
开发环境搭建 下载源码 boost_1_66_0.tar.gz 生成编译工具 # tar axf boost_1_66_0.tar.gz # cd boost_1_66_0 # yum install ...
- AT2045 Salvage Robots
传送门 这个题只要想到移动机器人和移动出口是等价的就好做了 考虑设\(f[i][j][k][t]\)为最远向左移动\(i\),向右移动\(j\),向上移动\(k\),向下移动\(t\),这个矩形内最多 ...