函数
一、函数检述
- 函数是完成特定任务的独立程序代码单元。语法规则定义了函数的结构和使用方式。
- 函数原型告诉编译器函数的类型;函数调用表明在此处执行函数;函数定义明确指定了函数要做什么。
- 函数和变量一样,有多种类型。任何程序在使用函数之前都要声明该函数的类型。
- 一般而言,函数原型指明了函数的返回值类型和函数接受的参数类型。这些信息称为该函数的签名。例如 int sum(int n1,int n2);
- 和定义在函数中的变量一样,形参也是局部变量,属于该函数私有。
- ANSI C要求在每个变量前都声明其类型。例如void (int x,y,z)//错误写法
- 用于测试函数的程序有时被称为驱动程序
- 使用return时,返回的值不一定是变量的值,也可以是任意表达式的值
return (n < m) ? n : m;
- 可以在函数中使用多个return,但是一个return更容易理解函数。
- 还可以使用return :或return;这样的语句会终止函数,并把控制返回给主调函数。
- 函数类型是指函数的返回值类型。
- 函数原型都应声明在使用函数之前。
二、递归
- C允许函数调用它自己,这种调用过程称为递归。
- 可以使用循环的地方都可以使用递归。递归方案更简洁,但是效率没有循环高。
- 每一级递归的变量都属于本级递归私有
1.递归的基本原理
示例:
/* recur.c -- recursion illustration */
#include <stdio.h>
void up_and_down(int);
int main(void)
{
up_and_down(1);
return 0;
}
void up_and_down(int n)
{
printf("Level %d: n location %p\n", n, &n); // #1
if (n < 4)
up_and_down(n+1);
printf("LEVEL %d: n location %p\n", n, &n); // #2
}
- 每级函数调用都有自己的变量
- 每次函数调用都会返回一次。当函数执行完毕后(即“递”这一部分完成时,该“归”了),控制权将被传回上一级递归。程序必须按顺序逐级返回递归。
- 递归函数中位于递归调用之前的语句,均按被调函数的顺序执行。#1的顺序 :第1级、第2级、第3级、第4级。总共执行4次。
- 递归函数中位于递归调用之后的语句,均按被调函数相反的顺序进行。#2的顺序:第4级、第3级、第2级、第1级。总共执行4次。
- 递归函数必须包含能让递归调用停止的语句。
2、尾递归
- 最简单的递归形式是把递归调用置于函数的末尾,即正好在return 语句之前。
- 递归会花费更多的内存。因为递归调用在底层其实是对线程栈的压栈和出栈操作,每调用一次都会压栈一次,并记录相关的局部变量信息,线程栈的内存是非常有限的,而递归调用如果是无限的,那么很快就会消耗完所有的内存资源,最终导致内存溢出,这一点与空的while死循环是不一样的,单纯的死循环会大量的消耗cpu资源,但不会占用内存资源,所以不会导致程序异常。
- 示例
long rfact(int n) // 使用递归的函数
{
long ans;
if (n > 0)
ans= n * rfact(n-1);
else
ans = 1;
return ans;
}
3、递归和倒序计算
- 递归非常适合处理倒序问题,比循环简单
- 示例
/* binary.c -- 以二进制形式打印整数 */
#include <stdio.h>
void to_binary(unsigned long n);
int main(void)
{
unsigned long number;
printf("Enter an integer (q to quit):\n");
while (scanf("%lu", &number) == 1)
{
printf("Binary equivalent: ");
to_binary(number);
putchar('\n');
printf("Enter an integer (q to quit):\n");
}
printf("Done.\n");
return 0;
}
void to_binary(unsigned long n) /* 递归函数 */
{
int r;
r = n % 2;
if (n >= 2)
to_binary(n / 2);
putchar(r == 0 ? '0' : '1');
return;
}
三、查找地址:&运算符
- 指针是C语言最重要的概念之一,用于存储变量的地址。
- 主调函数不使用return 返回的值,则必须通过地址才能修改主调函数中的值。
- 主调函数与背调函数是成对出现的,是主动和被动的关系,现在有A、B两个函数,A函数调用了B函数,那么A函数就是主调函数,B函数就是被调函数。
- 一元&运算符给出变量的存储位置。如果pooh是变量名,那么&pooh是变量的位置。可以把地址看作是变量在内存中的位置。
- %p格式控制符(pointer指针)用来以十六进制整数方式输出指针的值,附加前缀0x。
- %p是打印地址的, %x是以十六进制形式打印, 完全不同!
四、更改主调函数中的变量
temp = x;//交换x与y的值
x = y;
y = temp;
五、指针简介
- 从根本上看,指针是一个值为内存地址的变量(或数据对象)
ptr = &pooh;
//把pooh的地址赋给ptr,因此ptr“指向”pooh。ptr 和 &pooh的区别是ptr是变量,而&pooh是常量。- 要创建指针变量,先要声明指针变量的类型。
1.间接运算符:*
ptr = &pooh;
然后可以使用间接运算符* 找出存储在pooh中的值,因此该运算符有时也被称为解引用运算符。例如:val = *ptr;
//找出ptr指向的值。- 语句
ptr = &pooh;
和val = *ptr;
等同于val = pooh - 地址运算符:&,后面跟一个变量名时,&给出该变量的地址。
- 间接运算符:*,后更一个指针名或地址时,*给出存储在指针指向地址上的值。
2.声明指针
int * pi;//pi是指向int类型变量的指针
char * pc;//pc是指向char类型变量的指针,pc的值是一个地址
float * pf,* pg;//pf、pg都是指向float类型变量的指针
- *表明声明的变量是一个指针。int * pi;表明pi是一个指针,*pi是int类型。
- *和指针名之间的空格可有可无,一般声明时用空格,在解引用变量时省略空格。
- 指针实际上是一个新类型,不是整数类型。
- void interchange (&x,&y);//交换的是x和y的地址。
- 实参用于把值从主调函数传递给被调函数。
- 如果返回值的类型与声明的返回类型不匹配,返回值将会被转换成函数声明的返回类型。
- 函数签名指定了传入函数的值的类型和函数返回值的类型。
五、一些补充
-
错误导致无法编译,警告仍然可以编译。
-
程序中每个C函数与其他函数都是平等的。
-
&&和||有短路求值的特点(最小化求值)。这是因为C语言是从左往右对逻辑表达式求值
&&是逻辑与运算符,同真则真。倘若左边计算为假,那么不用计算右边,这个逻辑表达式就为假。 ||是逻辑或运算符,有真则真,倘若左边计算为真,那么也不用计算右边,这个逻辑表达式就为真。 一般而言,与 != 这个运算符联系密切。
-
PC地址通常用十六进制形式表示。
-
通过指针,可以在被调函数中改变多个主调函数的值,而不必通过返回值的形式,示例:
void interchange( int * u,int * v)//交换u,v所指向的值
{
int temp;
temp = *u;
*u = *v;
*v =tem;
}
- 普通变量把值作为基本量,把地址作为通过&运算符获得的派生量,而指针变量把地址作为基本量,把值作为通过*运算符获得的派生量。
- 编写程序时,可以认为变量有两个属性:名词和值
- 编译和加载程序时,计算机认为 变量有两个属性:地址 和 值。地址是变量在计算机内部的名称。对程序员隐藏。