C语言 函数简介及综合练习

函数:

函数是一段具有某项功能的代码的集合,是C语言中管理代码的最小单位
函数把代码分成一个个函数,可以方便管理和调用代码

函数分类:
    标准库函数:
        C语言标准委员会为C语言以函数形式提供的一套基础功能,被封装在一个libc.so
        库里,使用时需要包含头文件,函数名(参数)即可调用标准库函数

注:在一部分编译器中,没有加头文件的情况下一部分函数依旧能够正常运行,但编译器会发出warning警告,这是因为这部分编译器自动包含了这一部分函数的功能,如gcc就是不需要添加的。
例:
在没有加入头文件stdio.h和ctype.h的情况下直接运行如下代码

int main(int argc,const char* argv[])
{
    printf("%d",toupper('a'));
}

结果得:
C语言 函数简介及综合练习_第1张图片

如图所示,编译器对printf和toupper两个函数发出了警告,但是在警告的下方,依旧通过printf打印出了toupper返回的’a’转为’A’后所对应的ACSII码值65
此处仅做特殊情况展示,平常的编程过程中头文件应做到引用齐全

常用或特殊函数例举:

        int atoi(const char *nptr);
        long atol(const char *nptr);
        long long atoll(const char *nptr);
        功能:把字符串转换为整数

        #include 
        int isalnum(int c);
        功能:判断c是不是数字、字母字符
        int isalpha(int c);
        功能:判断c是不是字母字符
        int isdigit(int c);
        功能:判断c是不是数字字符
        int islower(int c);
        功能:当c是小写字母字符时返回真
        int isupper(int c);
        功能:当c是大写字母字符时返回真
        int toupper(int c);
        功能:把字符转换成大写字符
        int tolower(int c);
        功能:把字符转换成小写字符
        

        #include 
        int abs(int j);
        功能:返回j的绝对值

        以下函数被封装在libm.so库中  编译时需要加上-lm才可正常运行
        #include
        double pow(double x, double y);
        功能:求x的y次幂
        double fabs(double x);
        功能:求浮点型数据的绝对值
        double sqrt(double x);
        功能:返回x的平方根
        double floor(double x);
        功能:返回小于等于x的最大整数
        double ceil(double x);
        功能:返回大于等于x的最小整数

        #include 
        time_t time(time_t *t);
        功能:返回自1970-1-1 0:0:0 到当前时间过了多少秒
            time(NULL)

        #include 
        int system(const char *command);
        功能:调用系统命令
        int rand(void);
        功能:返回一个随机整数
        void srand(unsigned int seed);
        功能:种随机种子

练习1: 获取10个范围[100,1001)的随机数
公式:
[a,b)
rand()%(b-a)+a
由题目得
rand()%(1001-100)+100

#include 
#include 	//调用rand函数
#include 	//调用time函数

int main(int argc,const char* argv[])
{
        srand(time(NULL));	//将随机数种子设为time返回值(为1970-1-1 0:0:0至今的秒数),这样能更大程度上接近真随机,但是执行过于频繁打印出的数据还是同一组
        for(int i = 0; i<10; i++)
        {
            printf("%d\n",rand()%901+100);
        }
}

练习2: 随机出一注双色球彩票号码,不能重复 红球六个:1-33 蓝球一个:1-16

#include 
#include 
#include 

int main(int argc,const char* argv[])
{
        srand(time(NULL));
        int arr[6] = {},n = 0;	//arr数组存储红球,n用于存储已经生成的红球的个数
        for(int i = 0; i<6; i++)
        {
            int flag = 1;
            arr[i] = rand()%6+1;
            for(int j = 0;j<n; j++)
            {
                if(arr[j] == arr[i])	//判断是否重复
                {
                    i--;
                    flag = 0;
                    break;
                }
            }
            if(flag)
            {
                n++;
                printf("红球%d号:%d\n",i+1,arr[i]);
            }
        }
        printf("蓝球:%d\n",rand()%16+1);
}

    系统函数:
        是操作系统以函数接口形式提供的一套功能,这些功能:
            内存管理、信号处理、文件io、文件管理、进程管理、进程通信、线程管理、线程同步、网络通信(最重要的是网络通信)
    第三方库函数:
        由第三方提供的,一些开源或者收费的代码
        MD5 验证密码的第三方库
        JSON 序列化和反序列化
        glog 日志记录

    自定义函数:
        为了更好地管理代码,减少冗余把代码封装成函数
        注意:一个函数尽量控制在50行以内,要求一个函数一个功能

        函数声明:函数声明的目的是为了告诉其他代码该函数的调用格式
            返回值类型 函数名(类型1 形参名,类型2 形参名2,...);
            1、C语言中,函数名全部小写,用下划线分隔
            2、如果不需要参数时,建议在括号内写void,不要空着
            3、如果不需要返回值,也要在函数前写void,但是函数体中的return不能
               返回数据

            隐式声明:
                当调用函数之前没有声明和定义,编译器会猜测函数的格式,参数列表会
                根据调用时提供的实参(数据)来猜测,返回值会猜测成int类型。
                但是隐式声明应该是需要尽量避免的

            注:函数定义如果在函数调用之前,可以省略函数声明

        函数定义:
            返回值类型 函数名(类型1 形参1,类型2 形参2,...)
            {
                函数体
                return val;
            }

        函数调用:
            函数名(实参1,实参2,...);
            注意:返回值会放在调用的位置,可以立即打印显示,或者可以用变量记录下来

综合练习:
1、实现一个函数,判断整数是否是素数,调用该函数显示出100~10000之间所有的素数

#include 
#include 	//引入stdbool.h以使用bool类型

bool is_prime(int p)	//判断是否是素数的函数
{
    for(int i = 2; i<=p/2; i++)
    {   
        if(0 == p%i) return false;
    }   
    return true;
}

int main(int argc,const char* argv[])
{
    for(int i = 100; i<10001; i++)
    {   
        if(is_prime(i)) printf("%d\n",i);
    }   
}

2、输入两个日期,计算两个日期之间间隔了多少天

//代码未经优化
#include 
#include 
#include 

bool is_leap(int year)	//判断是否是闰年
{
    return 0 == year%4 && 0 != year%100 || 0 == year%400;
}

int daysum(int year,int month,int day)	//返回天数总和
{
    int md[] = {31,28,31,30,31,30,31,31,30,31,30,31};	//用于存放每个月的天数
    int yearsum,monthsum = 0;
    yearsum = (year-1)*365-1;
    for(int i = 0; i<year; i++)
    {
        yearsum += is_leap(year);	//判断有多少个闰年加多少天
    }
    if(1 < month)
    {
        for(int i = 0; i<month-1; i++)
        {
            monthsum += md[i];
        }
    }
    if(3 <= month && is_leap(year)) monthsum++;
    return yearsum+monthsum+day-1;
}

int getday(int year,int month)	//获取当月天数
{
    switch(month)
    {
        case 2: return 28+is_leap(year);
        case 4: case 6: case 9: case 11:
            return 30;
        default: return 31;
    }
}

bool lgt(int year,int month,int day)	//判断年月日数据是否合法
{
    return year>0 && month>0 && day>0 && month<13 && day<=getday(year,month);
}

int main(int argc,const char* argv[])
{
    int year1 = 0,year2 = 0,month1 = 0,month2 = 0,day1 = 0,day2 = 0;
    printf("输入两个日期:\n");
    scanf("%d-%d-%d %d-%d-%d",&year1,&month1,&day1,&year2,&month2,&day2);
    if( !lgt(year1,month1,day1) || !lgt(year2,month2,day2))
    {
        printf("Invalid!\n");
        return 0;
    }
    printf("日期差为:%d",abs(daysum(year1,month1,day1)-daysum(year2,month2,day2)));
}

3、实现一个函数,判断整数是否是回文数,调用该函数显示出1e-10e之间所有的回文数

#include 
#include 

bool is_pld(int n)
{
    int nn = n,sum = 0;
    while(0 != nn) 	//倒置这个数
    {   
        sum = sum*10+(nn % 10);
        nn /= 10; 
    }   
    if(sum == n)
        return 1;
    else
        return 0;
}

int main(int argc,const char* argv[])
{
    int cnt = 0;
    for(int i = 100000000; i<1000000001; i++)
    {   
        if(is_pld(i)) 
        {
            cnt++;
            printf("%d ",i);
        }
    }   
    printf("共有%d个回文数",cnt);
}

4、计算出100的阶乘

#include 

int main(int argc,const char* argv[])
{
    int arr[300] = {1};
    int cnt = 1;
    for(int i = 1; i<=100;i++)
    {   
        int jin = 0;
        for(int j = 0; j<cnt; j++)
        {
            int sum = arr[j]*i+jin;
            arr[j] = sum%10;
            jin = sum/10;
        }
        while(0 != jin)
        {
            arr[cnt++] = jin%10;
            jin /= 10; 
        }
    }   
    printf("%d\n",cnt);
    for(int i = cnt-1; i>=0; i--)
    {   
        printf("%d",arr[i]);
    }   
}

5、输入一个整数,显示该整数的补码

#include 
#include 

int main(int argc,const char* argv[])
{
    int nn = 0,n = 0,arr[100] = {},cnt = 0;
    printf("输入一个整数:");
    scanf("%d",&n);
    nn = n;
    while(0 != n)
    {   
        arr[cnt] = n%2;
        n /= 2;
        cnt++;
    }   
    if(0 > nn) 
    {   
        for(int i = 0; i<cnt; i++)
        {
            arr[i] = abs(~(arr[i]));
        }
        arr[0]++;
        for(int i = 0; i<cnt; i++)
        {
            if(2 == arr[i])
            {
                arr[i+1]++;
                arr[i] = 0;
            }
        }
        arr[cnt] = 1;
        cnt++;
    }
    else
    {
        arr[cnt] = 0;
        cnt++;
    }
    int tmp = 0;
    for(int i = 0; i<cnt/2; i++)
    {
        tmp = arr[i];
        arr[i] = arr[cnt-1-i];
        arr[cnt-1-i] = tmp;
    }
    for(int i = 0; i<cnt; i++)
    {
        printf("%d",arr[i]);
    }
}

函数传参:

1、形参变量只属于它所在的函数,出了该函数就不能用了
2、普通实参与形参之间是以赋值的形式来传递数据的(单项值传递)
3、return 其实是把返回值数据放到一个公共区域(函数和函数调用者),如果不写return,该区域中
	就是一个随机的垃圾数据
4、数组作为函数的参数时,长度会丢失,所以需要额外增加一个变量把数组的长度也传递过去,才能
	正确使用
5、数组的传递"址传递",所以函数与函数调用者之间是可以共享数组的

练习1:实现函数,找出数组的最大值
练习2:实现函数,对数组进行排序
练习3:实现函数,查找数组中是否存在某个值,如果存在,返回该值在数组中的下标

#include 

int is_max(int arr[],int cnt)
{
	int max = arr[0];
	for(int i = 1; i<cnt; i++)
	{
		if(arr[i]>max) max = arr[i];
	}
	return max;
}

void sort(int arr[],int cnt)
{
	int tmp = 0,min;
	for(int i = 0; i<cnt; i++)
	{
		min = i;
		for(int j = i; j<cnt; j++)
		{
			if(arr[j]<arr[min]) min = j;
		}
		tmp = arr[i];
		arr[i] = arr[min];
		arr[min] = tmp;
	}
}

int find(int arr[],int cnt,int target)
{
//	遍历查找
	/*
	for(int i = 0; i
//  二分查找前提是数组有序才能查找
	int l = 0, r = cnt-1;
	while(l <= r)
	{
		int p = (l+r)/2;
		if(arr[p] == target)
		{
			return p;
		}
		else if(arr[p] > target)
		{
			r = p-1;
		}
		else
		{
			l = p+1;
		}
	}
	return -1;
}

int main(int argc,const char* argv[])
{
	int arr[10] = {7,1,4,5,2,3,6,4,1,4};
	int cnt = 10;
	printf("%d\n",is_max(arr,cnt));
	sort(arr,cnt);
	for(int i = 0; i<cnt; i++)
	{
		printf("%d ",arr[i]);
	}
	printf("\n");
	printf("输入想要查找的值:");
	int target = 0;
	scanf("%d",&target);
	printf("要寻找的值的下标是:%d",find(arr,cnt,target));
}
设计函数的准则:
    1、一个函数最好只解决一个问题,这样可以降低出错率、提高可读性
    2、尽量不要依赖其它函数(降低耦合度)
    3、数据由调用者提供,结果返回给调用者(通用性)
    4、尽量考虑调用者提供的非法数据,可以通过提示信息、返回值告诉调用者错误原因、或者在
    	注释中把可能出现的情况说明(健壮性)
    5、最好一个函数不超过50行

函数递归:

函数自己调用自己的行为叫做递归,可能导致出现死循环的效果
递归可以实现一种叫做分治的算法思想,把一个复杂的大问题,分解成若干个相同的小问题,知道问题全部解决

1、先设置出口
2、解决一个小问题
3、调用自己

练习:尝试使用递归计算第N项斐波那契数列
1 1 2 3 5 8 13

#include 

int fb(int n)
{
	if(2 >= n)
	{
		return 1;
	}
	else
	{
		return fb(n-1) + fb(n-2);
	}
}

int main(int argc,const char* argv[])
{
	int n = 0;
	printf("输入n:");
	scanf("%d",&n);
	printf("第%d项是%d",n,fb(n));
}
递归函数每次调用自己都会在栈内存产生一份自己的拷贝,直到到达出口,才一层层释放,因此递归非常耗费内存,与循环相比速度非常慢,能用循环解决的问题就不要使用递归
    递归的优缺点:
        1、耗费内存、速度慢
        2、好理解、思路清晰
        3、可以解决非线性问题的执行过程

作业1:使用递归模拟汉诺塔的移动过程

#include 

void hanoi(int num,char now ,char mid ,char end );
void moves(int num,char now ,char end );

int main(int argc,const char* argv[])
{
    int num = 0;
    printf("请输入汉诺塔层数:");
    scanf("%d",&num);
    hanoi(num,'A','B','C');
}

void moves(int num,char now,char end )
{
	printf("%d from %c to %c\n",num,now,end);
}

void hanoi(int num,char now ,char mid ,char end )
{
    if(0 == num)
		return;
    hanoi(num-1,now,end,mid);
    moves(num,now,end);
    hanoi(num-1,mid,now,end);
}

作业2:实现打印 0-9 的全排列

#include 

void whole(int arr[],int n,int m)
//	n是全排列的起点下标 m是终点下标
{
	if(n == m)
	{
		for(int i=0; i<=m; i++)
		{
			printf("%d ",arr[i]);	
		}
		printf("\n");
		return;
	}
	for(int i=n; i<m; i++)
	{
		//交换所有的值到第n位
		int tmp = arr[i]; 
		arr[i] = arr[n];
		arr[n] = tmp;
		
		// 对后面n+1开始全排列
		whole(arr,n+1,m);
		
		//把值交换回第n位
		tmp = arr[i]; 
		arr[i] = arr[n];
		arr[n] = tmp;
		
	}
}

int main(int argc,const char* argv[])
{
	int arr[] = {0,1,2,3,4,5,6,7,8,9};
	whole(arr,0,9);
}

你可能感兴趣的:(c语言)