C语言-基础入门-学习笔记(6):函数

C语言-基础入门-学习笔记(6):函数

一. 概述

对于非常长的程序,由于分模块很多,所以需要对程序分节、分章甚至分篇处理。

1. 模块化编程

所谓模块化编程,是指将程序划分为一系列功能相互独立的模块,再以模块为单元进行开发,最后合并到主程序的编程方法。

范例1
下面将学习笔记(5)中的例子进行模块化改写:
原版为:

#include <stdio.h>
#define SIZE 4

int main(void){
	int i,j;
	int array[SIZE][SIZE] = {{0,1,2,3},
							 {4,5,6,7},
							 {8,9,10,11},
							 {12,13,14,15}};
	int transpose[SIZE][SIZE] = {0};

	printf("primary array:\n");
	for(i=0;i<SIZE;i++){
		for(j=0;j<SIZE;j++){
			printf("\t%-4d",array[i][j]);
		}
		printf("\n");
	}
	/*进行转置处理*/
	for(i=0;i<SIZE;i++){
		for(j=0;j<SIZE;j++){
			transpose[j][i] = array[i][j];
		}
	}

	printf("transpose array:\n");
	for(i=0;i<SIZE;i++){
		for(j=0;j<SIZE;j++){
			printf("\t%-4d",transpose[i][j]);
		}
		printf("\n");
	}
	return 0;
}

模块化后:

#include <stdio.h>
#define SIZE 4

/*打印数组*/
void print_array(int array[SIZE][SIZE]){
	int i,j;

	for(i=0;i<SIZE;i++){
		for(j=0;j<SIZE;j++){
			printf("\t%-4d",array[i][j]);
		}
		printf("\n");
	}
}

/*矩阵转置*/
void transpose_array(int array[SIZE][SIZE],int transpose[SIZE][SIZE]){
	int i,j;
	for(i=0;i<SIZE;i++){
		for(j=0;j<SIZE;j++){
			transpose[j][i] = array[i][j];
		}
	}
}

int main(void){
	int array[SIZE][SIZE] = {{0,1,2,3},
							 {4,5,6,7},
							 {8,9,10,11},
							 {12,13,14,15}};
	int transpose[SIZE][SIZE] = {0};

	printf("primary array:\n");
	print_array(array);

	transpose_array(array,transpose);
	
	printf("\nFinal array:\n");
	print_array(transpose);

	return 0;
}

在这里插入图片描述

2. 定义函数

函数的定义由函数声明和函数体两部分组成。函数声明又可以分为:由函数返回值类型、函数名、参数列表、函数体,以及函数操作符5个部分组成。标准形式如下:

函数返回值类型 函数名(参数类型1 参数名1,参数类型2 参数名2,……){
	/*函数体*/
	变量定义;
	函数操作;
	return语句;
}

3. 调用函数

函数调用表达式由函数名、函数操作符和逗号表达式三部分组成。表示方式如下:

函数名(参数表达式1,参数表达式2,……)

函数调用语句中的参数表达式,由于具有实际值,也被称为“实际参数”,简称“实参”;与此同时,函数声明和函数定义中的参数,在函数被调用前,不占用内存中的存储空间,有形无实,所以被称为“形式参数”,简称为“形参”。

函数调用方式分为三种:

  1. 单独成句
print_array(array);
transpose_array(array,transpose);
  1. 子表达式
b = square(5);
print_array(array),transpose_array(array,transpose)
  1. 函数实参
c = max(a,min(a,b));
d = max(a,min(a,b));

范例2

#include <stdio.h>

void print_data(const int a,const int b){
	printf("x = %d,y = %d\n",a,b);
}


int sum(const int a,const int b){
	int tmp;
	tmp = a + b;
	return tmp;
}

int dif(const int a,const int b){
	int tmp;
	tmp = a - b;
	return tmp;
}

int pro(const int a,const int b){
	int tmp;
	tmp = a * b;
	return tmp;
}

int main(void){
	const int x = 3;
	const int y = 9;
	int tmp;

	print_data(x,y);

	tmp = sum(x,y);
	printf("x + y = %d\n",tmp);

	tmp = dif(x,y);
	printf("x - y = %d\n",tmp);

	tmp = pro(x,dif(x,y));
	printf("x * (x - y) = %d\n",tmp);

	return 0;
}

在这里插入图片描述
对程序进行优化后:

范例3

#include <stdio.h>

void print_data(const int a,const int b){
	printf("x = %d,y = %d\n",a,b);
}


int sum(const int a,const int b){
	return a + b;
}

int dif(const int a,const int b){
	return a - b;
}

int pro(const int a,const int b){
	return a * b;
}

int main(void){
	const int x = 3;
	const int y = 9;

	print_data(x,y);

	printf("x + y = %d\n",sum(x,y));
	printf("x - y = %d\n",dif(x,y));
	printf("x * (x - y) = %d\n",pro(x,dif(x,y)));

	return 0;
}

二. 函数声明

1. 声明的形式

完整的函数声明定义了一个功能模块的接口,可以作为单独语句使用,形式如下:
函数值类型 函数名(参数类型1 形参1,参数类型2 形参2,……);

void print_data(const int a,const int b)
int sum(const int a,const int b)

函数可以视为一个变量,函数声明即变量声明,函数名即变量名。

2. 声明与定义

函数声明确定了一个函数的接口,告诉编译器该函数的函数名、函数值类型,以及形参列表中形参的个数和顺序,而函数定义则确立了一个函数的功能,不仅仅包含了函数声明所有的信息,还包含了形参的名字和函数体。

函数被调用前必须有函数声明,如果函数定义发生在函数调用之后,那么,必须在函数调用前使用单独函数声明语句。

范例4

#include <stdio.h>

void hello_world(void);

int main(void){
	hello_world();
	return 0;
}

void hello_world(void){
	printf("Hello World!\n");
}

三. 函数值与形参列表

1. 函数值类型

可以是整型、浮点型、字符型,也可以是自定义类型(typedef的结果)。但是函数值类型不能是数组型,也不能是函数型。对于需要返回数组型或函数型的函数值,可以使用指针来声明函数值类型。

当函数值类型定义为void型时,该函数没有返回值。例如下面的例子是错的

void set_a(int a);
b = set_a(2);

2. 形参列表

必须为每个参数的声明类型和参数名。例如:
函数值类型 函数名(参数类型1 形参1,参数类型2 形参2,……);

  • 为了提高程序的可读性,建议使用带参数名的声明方式。void copy_value(int dst,int src)
  • 要声明一个无形参的函数时,建议使用void型作为函数列表内容,以显式地说明不需要参数。 int hello_mark(void)
  • 调用函数后,每个形参都初始化为相对应的实参。参数类型1 形参1 = 实参1;
void print_data(const int a,const int b)l
print_data(x,y);
可以理解为
const int a = x;
const int b = y;

3. 函数返回值

当函数返回值类型不为void型时,函数返回值就可以作为函数调用表达式中的一个操作数来使用。

  • 函数可以使用return语句将函数内某个值带到函数外。

范例5

#include <stdio.h>

int cube(const int x){
	return x * x * x;
}

int factorial(int x){
	int rst = 1;

	for(;x>0;--x){
		rst *=x;
	}
	return rst;
}

int main(void){
	const int v = 7;
	printf("cube(7) = %d\n",cube(v));
	printf("factorial(7) = %d\n",factorial(v));

	return 0;
}

在这里插入图片描述

  • 通过在函数内部使用一个标志变量来记录执行的分支,最后用return语句将该值返回。在函数外部通过检查该标志的值,就可以判断函数执行了哪一个分支。

范例6

#include <stdio.h>
#define MAX_SIZE 8

const int TRUE = 1;
const int FALSE = 0;

/*检查一个数组中是否存在目标数*/
int search_data(const int array[],const int size,const int target){
	int i = 0;

	for(i = 0;i < size ;i++){
		if(target == array[i])
			return TRUE;
	}
	return FALSE;
}

int main(void){
	int q[MAX_SIZE] = {7,5,0,89,12,4,31,54};
	int x = 0;
	int i = 0;

	printf("Elements in the array:\n");
	for(i=0;i<MAX_SIZE;i++){
		printf("%4d",q[i]);
	}

	printf("\nPlease input the target number:");
	scanf("%d",&x);

	if(TRUE == search_data(q,MAX_SIZE,x)){
		printf("%4d exists in this array.\n",x);
	}else{
		printf("Can't find %d in this array.\n",x);
	}

	return 0;
}

在这里插入图片描述
在这里插入图片描述

4. const 形参

很多形参在声明中使用了const限定词,其作用是避免只读变量被修改。
这里要注意,定义const型的形参时,要确保该变量在函数执行过程中中不会被修改,如果出现自增自减等对参数自身值进行改变时,不要使用const进行定义。

范例7

#include <stdio.h>
const int YELLOW = 1;
const int RED = 2;

void set_yellow(const int colour){
	colour = YELLOW;
}

void set_red(int colour){
	colour = RED;
}

int main(void){
	int wall_colour = 0;

	set_yellow(wall_colour);

	set_red(wall_colour);

	return 0;
}

在这里插入图片描述
这里由于语句colour = YELLOW; ,而前面是将形参colour设置为了const,出现了冲突,所以编译不能通过。

四. 函数体

函数体一般由变量定义、函数操作和return语句三部分组成。

1. 变量定义

变量定义就是定义函数中需要用到的变量。一个程序块(放在花括号内的复合函数)中的变量定义必须放在这个程序块的最前面。
下面的变量j和变量i,max属于不同的程序块,但都放在程序块的最前端,因此,本函数的变量定义没有错。

int get_max(int a[SIZE][SIZE]){
	int i;
	int max = a[0][0];
	for(i=0;i<SIZE;i++){
		int j;
		for(j=0;j<SIZE;j++){
			if(max < a[i][j])
				max = a[i][j];
		}
	}
	return max;
}

2. 检查形参

在函数体中对形参进行操作前,有必要先检查形参值的合法性。例如:如果对数组元素进行操作,必须在访问数组元素前,对数组索引进行检查,看其是否在合法的范围内:

int deal_element(int array[SIZE],int index){
	if(index >= SIZE || index < 0){
		printf("Error index when get_ele().\n");
	}

	return 0;
}

3. return 语句

return 语句一般由关键字return 和表达式两部分组成,如下:
return 表达式;
如果省略了return语句,编译器会自动返回一个相应类型的随机值。
返回值可以是一个,可以是空(renturn;),也可以是多个(设置多个return)。

范例8

#include<math.h>
#include <stdio.h>

/*向下取整*/
int floor_new(const double d){
	return d;
}
/*向上取整*/
int ceiling_new(const double d){
	int f = floor(d);
	return d == f?f:f+1;
}

int main(void){
	double data;

	printf("Please input a double number: ");
	scanf("%lf",&data);

	printf("floor(%f) = %d\n",data,floor_new(data));
	printf("ceiling(%f) = %d\n",data,ceiling_new(data));

	return 0;
}

在这里插入图片描述

五. main函数

main函数时C语言中最特殊的函数,它是C程序的入口。
main的返回值必须为int,而形参列表可以为空,也可以带两个形参。 标准声明形式如下:
int main(void)
省略函数值类型是不推荐的,而声明为void型则是错误的。

完整的main声明如下:
int main(int argc,char ** argv)

范例9

#include <stdio.h>

int main(int argc,char *argv[]){
	int i = 0;

	printf("There are %d arguments.\n",argc);

	for(i=0;i<argc;i++){
		printf("%d: [%s]\n",i,argv[i]);
	}

	return 0;
}

在这里插入图片描述

练习1
对该范例进行函数封装。

#include <stdio.h>
#include <string.h>
#define MAX_STRING 200

int main(void){
	int i = 0;
	int j = 0;

	char str[MAX_STRING] = {0};
	int length = 0;
	char tmp = 0;

	int start = 0;
	int end = 0;

	printf("Input riginal string:\n");
	gets(str);
	length = strlen(str);

	start = 0;
	end = length - 1;

	while(start < end){  //将整个字符串进行翻转
		tmp = str[start];
		str[start] = str[end];
		str[end] = tmp;

		++start;  //采取两端向中缩进的原则
		--end;
	}
	printf("Step 1:\n");
	printf("%s\n",str);

	i=0;
	start=0;

	while(i<length){
		//翻转其中一个单词
		if(str[i] != ' '){ //寻找单词的开头
			start = i;

			while(str[i] != ' ' && str[i] != '\0') //寻找单词的结尾
				++i;
			end = i -1;
			while(start < end){  //对于单词进行翻转
				tmp = str[start];
				str[start] = str[end];
				str[end] = tmp;

				++start;
				--end;
			}
		}
		++i;
	}

	printf("Step 2:\n");
	printf("%s\n",str);

	return 0;
}

封装如下:

//将代码进行函数封装
#include <stdio.h>
#include <string.h>
#define MAX_STRING 200

void reverse_string(char str[MAX_STRING],int start,int end){
	char tmp = 0;

	//将整个字符串进行翻转
	while(start < end){  
		tmp = str[start];
		str[start] = str[end];
		str[end] = tmp;

		++start;  //采取两端向中缩进的原则
		--end;
	}
}

int main(void){
	int i = 0;
	int j = 0;

	char str[MAX_STRING] = {0};
	int length = 0;
	char tmp = 0;

	int start = 0;
	int end = 0;
	i = 0;

	printf("Input riginal string:\n");
	gets(str);
	length = strlen(str);


	start = 0;
	end = length - 1;

	reverse_string(str,0,length - 1);

	printf("Step 1:\n");
	printf("%s\n",str);

	i=0;
	start=0;

	while(i<length){
		//翻转其中一个单词
		if(str[i] != ' '){ //寻找单词的开头
			start = i;

			while(str[i] != ' ' && str[i] != '\0') //寻找单词的结尾
				++i;
			reverse_string(str,start,i - 1);
		}
		++i;
	}

	printf("Step 2:\n");
	printf("%s\n",str);

	return 0;
}

在这里插入图片描述
练习2

//判断输入整数是否为素数,请使用函数实现
#include <stdio.h>
#include <math.h>

int input_data(void){
	int data = 0;

	do{
		printf("Please input a positive integer: ");
		scanf("%d",&data);
	}while(data <= 0);//如果输入的数小于或等于0,那么就一直重新输入

	return data;
}

int is_prime(const int data){
	int i = 0;
	int max = sqrt(data);

	for(i=2;i<=max;i++){  
		if(data % i ==0)  //将2到这个数的平方根作为除数,检查这个数是否能整除其中某个数
			return -1;
	}
	return 0;
}

int main(void){
	int data = 0;
	int flag = 0;

	data = input_data();

	flag = is_prime(data);

	if(0 == flag){  //当为合数时返回-1,当为素数时返回0,因此可以作为标志位进行判断
		printf("%d is a prime number.\n",data);
	}else{
		printf("%d is not a prime number.\n",data);
	}

	return 0;
}

在这里插入图片描述
练习3

//证明任何一个而大于6的偶数都可以表示成两个素数之和
#include <stdio.h>
#include <math.h>

int input_data(void){
	int data = 0;

	do{
		printf("Please input a even integer (>=6):");

		scanf("%d",&data);
	}while(data < 6 || data % 2 == 1);//只要不是大于6或者为偶数,就一直循环

	return data;//将输入的数返回出来
}

int is_prime(const int data){
	int i = 0;
	int max = sqrt(data);

	for(i = 2;i<=max;i++){
		if(data % i == 0)//进行素数的判断
			return -1;
	}

	return 0;
}

int goldbach_conjecture(const int n){
	int a = 0;
	int mid = n/2;   //将mid定义为n的一半

	for(a=3;a<=mid;a +=2){  
		if(0 == is_prime(a)){  //a为其中的一个素数
			if(0 == is_prime(n-a)){  //n-a为另外一个素数
				printf("%d can be devided into two prime number:%d and %d\n",n,a,n-a);
				return 0;
			}
		}
	}
	return -1;
}

int main(void){
	int n = 0;
	int flag = 0;

	n = input_data();

	flag = goldbach_conjecture(n);
	if(0 == flag){
		printf("The Goldbach's conjecture is correct!\n");
	}else{
		printf("The Goldbach's conjecture is wrong!\n");
	}

	return 0;
}

在这里插入图片描述

发布了55 篇原创文章 · 获赞 76 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/qq_42826337/article/details/102673134