【Linux一站式编程】3.简单函数


1.Side Effect

原来printf也是一个函数,上例中的printf(“sin(pi/2)=%f\nln1=%f\n”, sin(pi/2), log(1.0))是带三个参数的函数调用,而函数调用也是一种表达式,因此printf语句也是表达式语句的一种。
但是printf感觉不像一个数学函数,为什么呢?因为像log这种函数,我们传进去一个参数会得到一个返回值,我们调用log函数就是为了得到它的返回值,至于printf,我们并不关心它的返回值(事实上它也有返回值,表示实际打印的字符数)。

我们调用printf不是为了得到它的返回值,而是为了利用它所产生的副作用(Side Effect)--打印。C语言的函数可以有Side Effect,这一点是它和数学函数在概念上的根本区别。

2.头文件参考

现在简单理解 头文件就是一组我可能用到的函数的声明,每个文件引入相当于复制粘贴了这些声明,就可以使用了,等到链接的时候把所有文件.c聚在一起,编译器会自动找到所有函数的定义。

  1. function.c
#include "function.h"

int add(int a,int b){
    return a+b;
}
  1. function.h
#include<stdio.h>

int add(int a,int b);
  1. main.c
#include "function.h"
#include<stdio.h>

int main(){
    int a=1,b=2;
    int c=add(a,b);
    printf("c=%d\n",c);
    return 0;
}

这样编译的时候将两个.c文件一起编译。头文件只是函数声明所需要的,因为在一个.c文件中函数必须先声明再使用。

gcc main.c function.c -o main
./main

#include语句有两种方式包含头文件,分别是使用双引号" "与左右尖括号< >。其区别是(对于不是使用完全文件路径名的)头文件的搜索顺序不同

使用双引号" "的头文件的搜索顺序:

  • 包含该#include语句的源文件所在目录;
  • 包含该#include语句的已经打开的头文件的逆序(因为头文件可以#include另一个头文件构成一个序列);
  • 编译选项-I所指定的目录
  • 环境变量INCLUDE所定义的目录

使用左右尖括号< >的头文件的搜索顺序:

  • 编译选项-I所指定的目录
  • 环境变量INCLUDE所定义的目录

3.main函数

main函数的特殊之处在于执行程序时它自动被操作系统调用,操作系统就认准了main这个名字,除了名字特殊之外,main函数和别的函数没有区别。
main函数的返回值是int型的,return 0;这个语句表示返回值是0,main函数的返回值是返回给操作系统看的,因为main函数是被操作系统调用的,通常程序执行成功就返回0,在执行过程中出错就返回一个非零值。比如我们将main函数中的return语句改为return 4;再执行它,执行结束后可以在Shell中看到它的退出状态(Exit Status):

$ ./a.out 
11 and 0 hours
$ echo $?  // $?是Shell中的一个特殊变量,表示上一条命令的退出状态
4

操作系统在调用main函数时是传参数的,操作系统传入参数
main函数最标准的形式应该是int main(int argc, char *argv[]),C标准也允许int main(void)这种写法,
如果不使用系统传进来的两个参数也可以写成这种形式。但除了这两种形式之外,定义main函数的其它写法都是错误的或不可移植的。


4.函数定义顺序

声明了一个函数的名字、参数类型和个数、返回值类型,这称为函数原型。在代码中可以单独写一个函数原型,后面加;号结束,而不写函数体,例如:

void threeline(void);

这种写法只能叫函数声明而不能叫函数定义,只有带函数体的声明才叫定义。上一章讲过,只有分配存储空间的变量声明才叫变量定义,其实函数也是一样,编译器只有见到函数定义才会生成指令,而指令在程序运行时当然也要占存储空间。那么没有函数体的函数声明有什么用呢?它为编译器提供了有用的信息,编译器在翻译代码的过程中,只有见到函数原型(不管带不带函数体)之后才知道这个函数的名字、参数类型和返回值,这样碰到函数调用时才知道怎么生成相应的指令,所以函数原型必须出现在函数调用之前,这也是遵循“先声明后使用”的原则。

若使用了函数声明,函数就可在任意地方定义了,只要函数声明在使用它之前。


5.全局变量、局部变量和作用域

函数中定义的变量称为局部变量(Local Variable),由于形参相当于函数中定义的变量,所以形参也是一种局部变量。

  1. 一个函数中定义的变量不能被另一个函数使用。
  2. 每次调用函数时局部变量都表示不同的存储空间。

全局变量定义在所有的函数体之外,它们在程序开始运行时分配存储空间,在程序结束时释放存储空间,在任何函数中都可以访问全局变量.

局部变量可以用类型相符的任意表达式来初始化,而全局变量只能用常量表达式(Constant Expression)初始化。

如果全局变量在定义时不初始化则初始值是0,如果局部变量在定义时不初始化则初始值是不确定的。所以,局部变量在使用之前一定要先赋值,如果基于一个不确定的值做后续计算肯定会引入Bug。
虽然在一个函数体中可以声明另一个函数,但不能定义另一个函数,C语言不允许嵌套定义函数.


6.局部变量的问题

由于c/c++只分全局变量与局部变量,因此全局变量所有函数均能访问,而函数内的局部变量由于不支持嵌套函数,因此只能自身函数内部访问。若想访问,可以使用函数返回值或将其赋值给全局变量。
我们也可以在函数中使用{}语句块进行进一步划分局部变量。

void foo(void)
{
	int i = 3;
	{
		int j = 2;
		printf("i=%d, j=%d\n", i, j);
	}
	printf("i=%d\n", i); /* cannot access j here */
}

python由于可以定义嵌套函数,因此函数既可以访问自身的局部变量,也可以访问包含其函数的其他函数内的局部变量。但无法直接访问同一等级的局部变量。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值