C语言再学习 -- 函数

1084人阅读 评论(0) 收藏 举报
分类:

一、函数概述

1、首先什么是函数?

函数是用于完成特定任务的程序代码的自包含单元。

2、为什么使用函数?

第一、函数的使用可以身故重复代码的编写。第二、函数使得程序更加模块化,有利于程序的阅读修改和完善。

3、main函数原型

int main (int argc, char * argv[], char * envp[]) {....}

第一个参数:命令行参数的个数

第二个参数:命令行参数的地址信息

第三个参数:环境表的首地址

参数argc表示输入参数的个数(含命令名),如果有命令行参数,argc应不小于1;argv表示传入的参数的字符串,是一个字符串数组,argv[0]表示命令名,argv[1]指向第一个命令行参数;至于第三个参数env,它与全局变量environ相比也没有带来更多益处,所以POSIX.1也规定应使用environ而不使用第三个参数。通常用getenv和putenv函数来存取特定的环境变量,而不是直接使用environ变量。例如:

//main函数原型的使用
#include <stdio.h>
int main(int argc,char* argv[],char* envp[])
{
	printf("argc=%d\n",argc);
	int i=0;
	for(i=0;i<argc;i++)
	{
		printf("感谢%s\n",argv[i]);
	}
	//声明全局变量
	extern char** environ;
	printf("environ=%p,envp=%p\n",environ,envp);//直接访问环境表
	return 0;
}
输出结果为:
argc=1
感谢./a.out
environ=0xbfab74cc,envp=0xbfab74cc
再如:

命令行为:myprogram c:\a.txt c:\b.txt
则:argc==3
argv[0]=="myprogram"
argv[1]=="c:\\a.txt"
argv[2]=="c:\\b.txt"

再再如:

#include <stdio.h>
int main (int argc, char *argv[])
{
	int count;
	printf ("The command line has %d arguments: \n", argc - 1);
	for (count = 1; count < argc; count++)
		printf ("%d: %s\n", count, argv[count]);
	printf ("\n");
	return 0;
}
输出结果:
tarena@ubuntu:~/project/C$ ./a.out I LOVE YOU
The command line has 3 arguments: 
1: I
2: LOVE
3: YOU


每个C程序都必须有一个 main 函数,因为它是程序执行的起点。关键字 int 表示函数返回一个整型值,关键字 void 表示函数不接受任何参数。main 函数的函数体包括左花括号和与之匹配的右花括号之间的任何内容。

注意:如果没有写明返回值,一般默认为 int 类型。

main (void)
{
...
    return 0;
}

二、函数声明和定义

1、函数声明

在任何程序在使用函数之前都需要声明该函数的类型。

例如:int imax (int a, int b);

第一个int指的是函数类型;位于圆括号内的两个int表明该函数的形式参数为int类型。分号的作用表示该语句是进行函数声明而不是函数定义。

在函数原型中可以根据自己的喜好省略变量名。

int imax (int , int);

需要注意的是这些变量名只是虚设的名字,他们不必和函数定义中使用的变量名想匹配。使用这种函数原型信息,编译器就可以检查函数调用语句是否和其原型声明一致。比如检查参数个数是否正确,参数类型是否匹配。将声明语句放置在main ()之前和之内两种方式都是正确的。需要重点注意的是函数声明要在使用函数之前进行。当在main之前函数定义的话,就不需要声明语句了。

#include <stdio.h>
int imin (int, int);
int main (void)
{
    int inim (int, int);
}

2、隐式声明

如果被调用函数写在调用函数后面则编译器会猜测被调用函数的格式,这叫做函数的隐式声明。隐式声明可以和实际情况不一致这时就会出错,可以把被调用函数的声明单独写在文件开头,这叫做函数的显示声明。除了主函数以外的所有函数都应该进行显示声明,因为主函数永远不是被调用函数。如果main()之后又函数定义,main()之前却没有函数声明的话,编译的时候会出现隐式声明报错。

3、头文件的使用

如果把main ()函数放在第一个文件中吧自定义哈拿书放在第二个文件中实现,那么第一个文件仍需要使用函数原型。如果把函数原型放在一个头文件中,就不必每次使用这些函数时输入其原型声明了。

#ifndef	__02READ_H__
#define __02READ_H__
extern int num;		//主函数全局变量写在了头文件里且要加extern关键字
int read(char, int);	//函数声明
#define PI 3.14
#endif 

4、函数定义
函数声明只是将函数类型告诉编译器,而函数定义部分则是函数的实际实现代码。之所以使用函数原型,是为了在编译器编译第一个调用函数的语句之前想其表明该函数的使用方法。因此,可以在首次调用某函数之前对该函授进行完整的定义。这样函数定义部分就和函数原型有着相同的作用。通常对较小的函数会这样做:

//下面既是一个函数的定义,也是它的原型
#include <stdio.h>
int imax (int a, int b)
{
	return a > b ? a : b;
}

int main (void)
{
	int x, z;
	scanf ("%d", &x);
	z = imax (x, 50);
	printf ("%d\n", z);
	return 0;
}
三、函数形参与实参

1、形式参数

函数定义为下面的函数:

void show (char ch, int num)

这行代码通知编译器show()使用名为ch和num的两个参数,并且这两个参数的类型分别为char和int。变量ch和num被称为形式参数。

注意:ANSI C形式要求在每个变量前声明其类型。也就是说,不能像通常变量声明那样使用变量列表声明同一类型的变量。如下所示:

void dibs (int x, y, z)  /*不正确的函数头*/

void dibs (int x, int y, int z)  /*正确的函数头*/

2、实际参数

函数调用中,通常使用实际参数对ch和num赋值,如:

show (SPACE, 12);

形式参数是被调函数中的变量,而实际参数是调用函数分配给被调用函数变量的特定数值。实际参数可以是常量、变量或一个复杂的表达式。但是无论何种形式的实际参数,执行时首先要计算其值,然后将该值复制给被调用函数中相应的形式参数。

3、无参数

void dibs (void);

为了表示一个函数确实不使用参数,需要在圆括号内加入void关键字。

四、递归函数

1、递归介绍

C允许一个函数条用其本身,这种调用过程被称为递归。

递归与循环比较:

采用递归函数解决问题的思路叫递归
采用循环解决同样问题的思路叫递推

递归一般可以代替循环语句使用。有些情况下使用循环语句比较好,而有些时候使用递归更有效。递归方法虽然使用程序结构优美,但其执行效率却没有循环语句高。一般来说,选择循环更好一些。首先,因为每次递归调用都拥有自己的变量集合,所以就需要占用较多的内存;每次递归调用需要把心的变量集合存储在堆栈中。其次,由于进行每次函数调用需要花费一定的时间,所以递归的执行速度较慢。

//示例一
#include <stdio.h>
void show (int);

int main (void)
{
	show (1);
	return 0;
}

void show (int n)
{
	printf ("Level %d: n location %p\n", n, &n);
	if (n < 4)
		show (n + 1);
	printf ("Level %d: n location %p\n", n, &n);
}

执行结果如下:
Level 1: n location 0xbf8bf680
Level 2: n location 0xbf8bf660
Level 3: n location 0xbf8bf640
Level 4: n location 0xbf8bf620
Level 4: n location 0xbf8bf620
Level 3: n location 0xbf8bf640
Level 2: n location 0xbf8bf660
Level 1: n location 0xbf8bf680

//示例二
#include <stdio.h>

void print (int num)
{
	if (num == 1)
	{
		printf ("1\n");
		return ;
	}
	printf ("%d\n", num);
	print (num -1);
}

int main (void)
{
	print (10);
	return 0;
}
输出结果:
10
9
8
7
6
5
4
3
2
1


2、递归的基本原理

第一:每一级的函数调用都有自己的变量。

第二:每一次函数调用都会有一次返回。

第三:递归函数中,位于递归调用前的语句和各级被调用函数具有相同的执行顺序。

第四:递归函数中,位于递归调用后的语句的执行顺序和各个被调用函数的顺序相反。

第五:虽然每一级递归都有自己的变量,但是函数代码并不会得到复制。

最后:递归函数中必须包含可以终止递归调用的语句。

3、递归的优缺点

其优点在于为某些变成问题提供了最简单的解决方法,而缺点是一些递归算法会很快耗尽计算机的内存资源。同时,使用递归的程序难于阅读和维护。


五、指针函数和函数指针

指针函数:返回指向char的指针的函数,例如:char * fump ( );
函数指针:指向返回类型char的函数指针,例如:char (* frump) ( );
这里需要明白一个符号之间优先级的问题,"( )"的优先级比"*"要高。

再考虑一个,char (* flump[3]) ( );

由3个指针组成的数组,每个指针指向返回类型为 char 的函数。

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    友情链接

       

           


        不安分的小宝带你暴走全世界
        脚步从未停止,旅途永无止境

        喜欢户外旅行的,等你来!!


    个人资料
    • 访问:518348次
    • 积分:7261
    • 等级:
    • 排名:第3134名
    • 原创:253篇
    • 转载:142篇
    • 译文:0篇
    • 评论:127条
    博客专栏