数学中我们常见的函数的概念,在维基百科当中对函数的定义就是:子程序
- 在计算机科学中,子程序,是大型程序中的某部分代码,由一个或多个语句块组成,它负责完成某项特定的任务,而且相对于其他的代码开说,具有相对的独立性
- 一般会输入参数并有返回值,提供对过程的封装和细节的隐藏,这些代码通常叫做软件库
为什么会有库函数?
像上面的这些描述的基础的功能,是我们在开发能够用的到的,能够支持可移植性和提高程序的效率,所以就会在C语言的基础库当中提供一些类似的函数,进而来方便程序员的开发
我们还能够看看
www.cplusplus.com
http://en.cppreference.com
MSDN 一个小的工具
常见的库函数:
IO函数
字符串操作函数:strcpy
字符操作函数:大小写字母的判断和转换
内存操作函数
时间/日期的函数
数字的函数
其他的库函数
例子:
strcpy这个函数:
#include
#include
int main()
{
char arr1[]="bite!";
char arr2[20]="######";
strcpy(arr2,arr1);
printf("%s",arr2);
return 0;
}
疑问:
字符串的拷贝为什么不会保留原来的数组的字符串,我们怎么样才能够修改这个函数的逻辑,然后可以将数组的字符串可以拼接,然后就是这个函数对\0 结束符的处理方式是什么?
原字符串要比目标字符串长会不会溢出,然后溢出的话,因该怎么解决?
strcpy 函数是用来拷贝的,不可以进行字符串的拼接,接下来的学习当中应该会遇到可以实现字符串的拼接,并且还需要记得对结束符 \0 的处理方式,strcpy这个函数拷贝的时候是连着结束符 \0 一起拷贝过来的
一定会溢出,目前没有解决方案,只可以原字符串的长度小于目标的字符串,严么目标字符串的空间比原字符串的空间大,不然自己就写了一个Bug
memset函数:
#include
#include
#include
int main()
{
char arr[] = "hello world";
memset(arr,'*',5);
printf("%s",arr);
//***** world
return 0;
}
更加重要的就是自己定义的函数,自定义的函数和库函数是一样的,有函数名,返回值,函数的参数,但是不一样的是这些都是我们自己来设计,给我们自己很大的发展空间
ret_type fun_name(para1,*)
{
statement; //语句项
}
ret_type //返回的类型
fun_name //函数名
para1 //函数的参数
// 书写一个较大值的函数
#include
int getMAx(int x,int y)
{
if(x>y)
return x;
else
return y;
}
int main()
{
int x = 10;
int y = 30;
int z = getMax(x,y);
printf("max = %d\n",z);
return 0;
}
一个函数不需要返回类型,void的意思是这个函数没有返回值,void是空,无的意思,他代表没有返回值
书写一个交换数字的函数不使用指针不可以的,和JavaScript当中一样,如果没有使用指针书写的话,就会存在主函数里面的变量开辟了两个空间,函数里面开辟了两个空间,他们所指的变量的地址是不同的,函数对里面空间的两个变量进行了交换,但是不能对外面的变量进行交换,因为他们的地址不同,但是如果使用指针就可以将变量的地址传进去,修改变量本身的值,这个时候操作的空间是同一个。但是高级语言是不可以访问内存地址的
void swap1(int x,int y)
{
int temp = 0;
x = y;
y = temp;
}
void swap2(int *px,int *py)
{
int temp = 0;
temp = *px;
*px = *py;
*py = temp;
}
int main()
{
int num1 = 10;
int num2 = 20;
swap1(num1,num2);
printf("Swap1: num1 = %d,num2 = %d\n",num1,num2);
swap2(&num1,&num2);
printf("Swap2: num1 = %d,num2 = %d\n",num1,num2);
return 0;
}
实际参数( 实参 )
真实传给函数的参数,叫做实参,实参可以是:常量,变量,表达式,函数等,无论实参是何种类型的量,在进行函数的调用的时候他们必须有确定的值,以便把这些值传给形参
形式参数 ( 形参 )
形式参数是指函数名后括号中的变量,因为形式参数只有在函数被调用的过程中才实例化( 分配内存单元 ),所以叫做形式参数,形式参数当函数调用完成后就自动销毁了,因此形式参数只在函数中有效。
在上面的Swap1和Swap2函数中的参数x,y,px,py都是形式参数,在main函数里面中Swap1的num1,num2和传给Swap2函数的&num1,&num2是实际参数。
我们可以简单的理解为:形参实例化之后就相当于实参的一份临时拷贝
函数的形参和实参分别占有不同额内存块,对形参的修改不会影响实参
- 传址调用是把函数外部创建变量的内存地址传递给函数参数的一种函数的调用的方式
- 这种传参的方式可以让函数和函数外边的变量真正建立起联系,也就是说函数的内部可以直接操作函数外部的变量
练习:
- 写一个函数可以判断一个数是不是素数
- 写一个函数判断一年是不是闰年
- 写一个函数,实现一个整型有序的数组的二分查找
- 写一个函数,每调用一次这个函数,就会将num的值加1
//1. 写一个函数可以判断一个数是不是素数
#include
int Judge(int n)
{
for(int i = 2;i < n;i++)
if(n%i == 0)
printf("这个数是一个素数\n");
else
printf("这个数不是一个素数\n");
}
int main()
{
int n = 0;
printf("please input a number\n");
scanf("%d",&n);
jadge(n);
return 0;
}
//输出1~100 之间的素数
#include
int Jadgement(int n)
{
for(int i = 2; i < n; i++)
{
if( n%i ==0 )
return 0;
}
return 1;
}
int main()
{
int i = 0;
for(Jadgement(i) == 1)
{
printf("%d",i);
}
return 0;
}
如果需要是设计一个函数在某个区间内求一些闰年,就可以参照上面的函数,然后就可以设计出来是一样的思路,但是现在自己大的代码是太不行了,被说了,哈哈哈,不能直接打印 是不是素数或者闰年,返回值1或者0就可以了,所以要转换一个思路,人家需要什么代码就书写什么代码给人家,不然人家就不会用你的代码和你写的函数
//2. 写一个函数判断一年是不是闰年
#include
int Leapyear(int n)
{
if( (n%4 == 0 && n%100 != 0) || (n%400 == 0) )
printf("这一年是闰年\n");
else
printf("这一年不是闰年\n");
}
int main()
{
int year = 0;
printf("please input a year\n");
scanf("%d",&year);
Leapyear(year);
return 0;
}
#include
int is_leap_year(int y)
{
if((y%4 == 0 && y%100 != 0) || (y%400 ==0))
return 1;
}
}
int main()
{
int year = 1000;
for(year = 1000; year < 2000;year++)
{
if(is_leap_year == 1)
{
printf("%d",year);
}
}
return 0;
}
//3. 写一个函数,实现一个整型有序的数组的二分查找
#include
#include
int Binarysearch(int arr[])
{
int size = strlen(arr) - 1;
int left = 0;
int right = size - 1;
int x = 0;
printf("请输入你想要查找的数字\n");
scanf("%d",&x);
int mid = 0;
while( left <= right )
{
mid = (left + right)/2;
if(arr[mid] < x)
left = mid + 1;
else if(arr[mid] > x )
right = mid -1;
else
printf("这个数找到了\n");
}
if(left > right )
printf("找不到这个数\n");
}
int main()
{
int arr[]={1,2,3,4,5,6,7,8,9,10,11,12,13,14};
Binarysearch(arr[20]);
return 0;
}
疑问:怎么实现代码的循环,并且怎么书写一个代码可以实现计算调用函数的次数,我本身想的就是可以在函数当中写一个num,然后计数,但是函数现在不能循环起来,就不知道应该怎么做到程序循环并且能够完成调用的计数
//4. 写一个函数,每调用一次这个函数,就会将num的值加1
//这个题其实是考相关的传址的使用!!
#include
int Max(int x,int y)
{
if(x > y)
return x;
else if(x < y)
return y ;
else
return 0;
}
int main()
{
int x = 0;
int y = 0;
printf("please input two number\n");
scanf("%d,%d",&x,&y);
Max(x,y);
return 0;
}
函数和函数之间是可以有机的结合在一起的
#include
void new_line()
{
printf("hehe\n");
}
void three_line()
{
int i = 0;
for( i = 0; i<3; i ++ )
{
new_line();
}
}
int main()
{
three_line();
return 0;
}
把一个函数的返回值作为另一个函数的参数
#include
#include
int main()
{
char arr[20] = "hello!";
int ret = strlen(strcat(arr,"bit")); //这里介绍一下strlen函数
printf("%d\n",ret);
return 0;
}
#include
int main()
{
printf("%d",printf("%d",printf("%d",43)));
return 0;
}
//这个函数打印出来的是4321,因为printf这个函数返回的打印的长度,先打印43,然后就是长度。
函数的声明:
- 告诉编译器有一个函数叫做什么,参数是什么,返回类型是什么
- 函数的声明一般出现在函数的使用之前,要满足先声明后使用的原则
- 函数的声明一般放在头文件中
函数的定义:
函数的定义是指函数的具体实现,交代函数的功能实现
//函数的声明
int Add(int ,int);
int main()
{
int a = 10;
int b = 20;
int sum = 0;
sum = Add(a,b);
printf("%d\n",sum);
return 0;
}
//函数的定义
int Add(int x,int y)
{
int z = x + y;
return z;
}
https://stackoverflow.com
程序员的知乎
程序调用自身的编程技巧叫做递归,递归作为一种算法在程序设计语言中广泛应用,一个过程或一个函数在其定义或声说明中有直接的或间接调用自身的一种方法,他通常把一个大型复杂的问题层层转换为一个与原问题相似规模较小的问题求解,递归策略只需少量的程序就可描述出解题过程所需要的多次重复计算,大大减少了程序的代码量,递归的主要思考方式在于:把事化小
练习1:接受一个整型值(无符号),按照顺序打印他的每一位,例如:输入1234,输出1 2 3 4
//求一个区间的素数
//问题:怎么解决重复出现的问题,出现了重复出现得问题
#include
int main()
{
int number = 0;
printf(""please input a number\n);
scanf("%d",&number);
for( ; i<=number ;i++)
{
for( ; j < i ;j++)
if(i%j != 0)
printf("%d\t",i);
}
}
#include
void resolve(int);
int main()
{
int number = 0;
printf("please input a number\n");
scanf("%d",&number);
resolve(number);
return 0;
}
void resolve(int x)
{
if(x>9)
{
resolve(x/10);
}
printf("%d\t",x%10);
}
练习2:编写函数不允许创建临时变量,求字符串的长度
#include
#include
int my_strlen(char* str)
{
int count = 0;
while( *str != '\0')
{
count++;
str++;
}
return count;
}
int main()
{
char arr[] = "bit";
//int len = strlen(arr); //求字符串的长度
//printf("%d\n",len);
int len = my_strlen(arr);
printf("%d\n",len);
//arr是数组,数组传参,传过去的不是整个数组,而是第一个元素的地址。
}
//不使用临时变量,就需要使用递归函数来解决这和个问题
现在进行大事化小
my_strlen(“bit”);
1+my_strlen(“it”);
1+1+my_strlen(“t”);
#include
#include
int my_strlen(char* str)
{
if( *str != '\0' )
return 1+my_strlen(str+1);
else
return 0;
}
int main()
{
char arr[] = "bit";
int len = my_strlen(arr);
printf("%d\n",len);
return 0;
}
递归与迭代
练习3:求n的阶乘(不考虑溢出)
#include
int factorial(int n)
{
if(n <= 1)
return 1
else
return n*factorial(n-1);
}
int main()
{
int number = 0;
printf("please input a number\n");
scanf("%d",&number);
int num = factorial(number);
printf("%d\n",num);
return 0;
}
阶乘和循环其实可以互换!
练习4:求第n个斐波那契数(不考虑溢出)
#include
int Fit(int n)
{
if(n>0)
{
if( 0 < n <= 2)
return 1;
else
return Fit(n-1) + Fit(n-2);
}
}
int main()
{
int num = 0;
printf("please input a number\n");
scanf("%d",&num);
int sum = Fit(num);
return 0;
}
但是我们发现了一些问题:
递归和循环都可以解决问题的时候,就需要对比那一个更加好多方面考虑,考虑他的计算时间还有就是考虑他是否溢出
我们如何解决问题呢?
在斐波那契数列的计算当中,可以写一个程序关于使用三个新的变量不断的循环计算,可以大大的加快程序的运行速度
#include
int Fac(int n)
{
int a = 1;
int b = 1;
int c = 1;
while(n>2)
{
c = a+b;
a = b;
b = c;
n--;
}
return c;
}
int main()
{
int num = 0;
printf("please input a number\n");
scanf("%d",&num);
int sum = Fac(num);
printf("%d",sum);
return 0;
}
提示:
加强训练: