1.函数的概念
函数就是一个完成某项特定的任务的一个小段代码,代码是有特殊的写法和调用方法的。
两类函数:1.库函数 2.自定义函数
2.库函数
2.1 使用方法
double sqrt(double x);
//sqrt是函数名
//x是函数的参数,表示调用sqrt函数需要传递一个double类型的值
//double是返回值类型-表示函数计算的结果是double类型的值
2.1.1 头文件
对于库函数的使用,必须包含对应的文件。
2.1.2 练习
#include<stdio.h>
#include<math.h>
int main()
{
double d = 16.0;
double r =sqrt(d);
printf("%1f\n",r);
return 0;
}
3.自定义函数
3.1语法形式
与库函数类似,如下。
ret_type fun_name(形式参数)
{
}
//ret_type是函数返回类型
//fun_name是函数名
括号中是形式参数
{}括起来是函数体
3.2举例
一个加法函数,完成2个整型变量的加法操作。
#include<stdio.h>
int Add(int x,int y) //括号中写的x,y称为形参
{
int z=0;
z=x+y;
return z;
}
int main()
{
int a=0;
int b=0;
scanf("%d %d",&a,&b);
int r=Add(a,b); //调用函数,这里的a,b为实参
printf("%d\n",r);
return 0;
}
4.数组做函数参数
如果写一个函数对将一个整型数组的内容,全部置-1,再写一个函数打印数组的内容。
#include<stdio.h>
int main()
{
int arr[]={1,2,3,4,5,6,7,8,9,10};
int sz=sizeof(arr)/sizeof(arr[0]);
set_arr(arr,sz);//设置数组内容为-1
printf_arr(arr,sz);//打印数组内容
return 0;
}
数组作为参数传递给了set_arr 和 print_arr 函数了,那这两个函数应该如何设计呢?
这⾥我们需要知道数组传参的⼏个重点知识:
函数的形式参数要和函数的实参个数匹配
函数的实参是数组,形参也是可以写成数组形式的
形参如果是⼀维数组,数组⼤⼩可以省略不写
形参如果是⼆维数组,⾏可以省略,但是列不能省略
数组传参,形参是不会创建新的数组的
形参操作的数组和实参的数组是同⼀个数组
根据上述的信息,我们就可以实现这两个函数:
void set_arr(int arr[],int sz)
{
int i=0;
for(i=0;i<sz;i++)
{
arr[i]=-1;
}
}
void print_arr(int arr[],int sz)
{
int i=0;
for(i=0;i<sz;i++)
{
printf("%d ",arr[i]);
}
printf("\n");
}
5.嵌套调用和链式访问
5.1 嵌套调用
函数之间互相调用。
比如:计算某年某月有几天?如果要函数实现,可以设计2个函数:
is_leap_year() 根据年份确定是否是闰年
get_days_of_month() 调用is_leap_year确定是否是闰年后,再根据月计算这个月的天数
int is_leap_year(int y)
{
if(((y%4==0)&&(y%100!=0))||(y%400==0))
return 1;
else
return 0;
}
int get_days_of_month(int y,int m)
{
int days[]={0,31,28,31,30,31,30,31,31,30,31,30,31};
int day=days[m];
if(is_leap_year(y) && m ==2)
day+=1;
return day;
}
int main()
{
int y=0;
int m=0;
scanf("%d %d",&y,&m);
int d=get_days_of_month(y,m);
printf("%d\n",d);
return 0;
}
6.链式访问
将一个函数的返回值作为另外一个函数的参数,像链条一样将函数串起来就是函数的链式访问。
比如:
#include<stdio.h>
int main()
{
int len=strlen("abcdef");//strlen求一个字符串的长度
printf("%d\n",len);
return 0;
}
如果把strlen的返回值直接作为printf函数的参数呢?这样就是一个链式访问的例子了。
#include<stdio.h>
int main()
{
printf("%d\n",strlen("abcdef"));//链式访问
return 0;
}
7. static和extern
static是 静态的 的意思,可以用来:
修饰局部变量
修饰全局变量
修饰函数
extern是用来声明外部符号的。
7.1 static 修饰局部变量:
代码1
#include<stdio.h>
void test()
{
int i=0;
i++;
printf("%d ",i);
}
int main()
{
int i=0;
for(i=0;i<5;i++)
{
test();
}
return 0;
}
代码2
#include<stdio.h>
void test()
{
//static修饰局部变量
static int i=0;
i++;
printf("%d ",i);
}
int main()
{
int i=0;
for(i=0;i<5;i++)
{
test();
}
return 0;
}
对⽐代码1和代码2的效果,理解static修饰局部变量的意义。
代码1的test函数中的局部变量i是每次进⼊test函数先创建变量(⽣命周期开始)并赋值为0,然后
++,再打印,出函数的时候变量⽣命周期将要结束(释放内存)。
代码2中,我们从输出结果来看,i的值有累加的效果,其实test函数中的i创建好后,出函数的时候是
不会销毁的,重新进⼊函数也就不会重新创建变量,直接上次累积的数值继续计算。
结论:static修饰局部变量改变了变量的⽣命周期,⽣命周期改变的本质是改变了变量的存储类型,本
来⼀个局部变量是存储在内存的栈区的,但是被static修饰后存储到了静态区。存储在静态区的变量和全局变量是一样的,生命周期就和程序的生命周期一样,只有程序结束,变量才销毁,内存才回收。但是作用域不变。
如果要一个变量出了函数后,还想保留值,等下次进入函数继续使用,就可以使用static修饰。
7.2 extern声明外部符号
代码1:
add.c
int g_val=20;
test.c
#include<stdio.h>
extern int g_val;
int main()
{
printf("%d\n",g_val);
return 0;
}
extern是用来声明外部符号的,如果一个全局的符号在A文件中定义的,在B文件中想用,就可以使用extern进行声明,然后使用。