c语言中函数分类
- 库函数
- 自定义函数
库函数
将被频繁使用的功能定义出来,让C语言更加标准
所有函数都寄存在头文件里,所以使用库函数时要引用头文件
常见库函数有:
- IO函数 (putchar scanf getchar…)
- 字符串操作函数 (strcmp strlen…)
- 字符操作函数 (toupper…))
- 内存操作函数 (memcpy memset…)
- 时间/日期操作函数 (time…)
- 数学函数(sqrt…)
- 其他库函数
学习几个库函数(学会查文档非常重要):
- strcpy----string copy----字符串拷贝
- strlen----string length----字符串长度有关(包含\0)
- memset----内存 set 设置
memset
int main()
{
char arr[] = "hello world";
memset(arr,'*',5);
printf("%s\n",arr); //***** world
return 0;
}
结果为:
切记:使用库函数时,必须包含#include对应的头文件
自定义函数
函数的组成
与库函数一样,有函数名,返回值和函数参数,不同的是,我们可以自己定义,也有了更多的可能。
比较两个值
int get_max(int x, int y)
{
int z = 0;
if (x > y)
z = x;
else
z = y;
return z;
}
int main()
{
int a = 10;
int b = 20;
int max = get_max(a, b);
printf("max=%d\n", max);
return 0;
}
交换两个值
大家觉得下面这种写法是正确的吗?
函数返回类型的地方写:
void----表示这个函数不返回任何值,也不需要返回
如果什么都不写,则会默认返回 int类型
//第一种方法(传值调用)
void Swap1(int x, int y)
{
int z = 0;
z = x;
x = y;
y = z;
}
int main()
{
int a = 10;
int b = 20;
printf("交换前:a= %d b = %d\n", a, b);
Swap1(a, b);
printf("交换后:a= %d b = %d", a, b);
return 0;
}
很多初学者可能第一反应会觉得这种写法是正确的,然鹅。事实却是残酷的=-=这是为什么呢?从下面的图片我们可以知道原因
接下来我们就来了解一下指针
int main()
{
int a = 10;
int* pa = &a; //pa就是一个指针变量
*pa = 20; //*pa即对pa解引用-通过pa找到 a
printf("%d", a);
return 0;
}
我们可以利用指针对第一种方法改造一下
//第二种方法(传址调用)
void Swap1(int* pa,int* pb)
{
int tmp = 0;
tmp = *pa;
*pa = tmp;
//return;
//void不需要返回,没有必要
}
int main()
{
int a = 10;
int b = 20;
printf("交换前:a= %d b = %d\n", a, b);
Swap1(&a, &b);
printf("交换后:a= %d b = %d", a, b);
return 0;
}
通过指针,我们就能让函数之间建立联系
函数的参数
- 实参
实参可以是:常量,变量,表达式,函数等。在进行函数调用时,必须有确定的值。
- 形参
完成函数调用后自动销毁。(类似局部变量)----形参只在函数中有效
swap1在被调用的时候,实参传给形参,其实形参实例化后就等同与被临时拷贝。
改变形参,不能改变实参,但是为什么swap1可以改变呢?,其实就是因为形参和实参之间建立了联系
函数的调用
传值调用
函数的形参和实参占不同内存块,对形参修改不会影响实参
传址调用
即上例子中所用
这种传参方式可以让函数和函数外边的变量建立起真正的联系,让函数内部可以直接操作函数外部的变量
函数的嵌套调用和链式访问
函数不能嵌套定义
嵌套调用
void test3()
{
printf("hehe\n");
}
int test2()
{
test3();
return 0;
}
int main()
{
test2();
return 0;
}
链式访问
返回值作参数
#include<string.h>
int main()
{
//int len = strlen("abc");
//printf("%d\n", len);
printf("%d\n", strlen("abc"));
}
考考你
曾经有这样一道题
printf("%d", printf("%d", printf("%d", 43)));
你们觉得这道题结果是多少呢?
你们有没有做对呢?下面来解释一下原因
考考你
我们不能光说不做,下面一起来做一些练习吧
写一个函数判断一个数是不是素数
#include<math.h>
int is_prine(int n)
{
int j = 0;
for (j = 2; j <= sqrt(n); j++)
{
if (n % j == 0)
return 0;
}
return 1;
}
int main()
{
int i = 0;
int count = 0;
for (i = 100; i <= 200; i++)
{
if (is_prine(i) == 1)
{
printf("%d是素数 \n", i);
count++;
}
}
printf("%d ", count);
return 0;
}
注意:在写函数时我们要明确实际需求,不要写多余的语句
写一个函数判断是不是闰年
int is_leap_year(int n)
{
//if (n % 4 == 0 && n % 100 != 0 || n % 400 == 0)
//{
// return 1;
//}
//else
// return 0;
//我们也可以写成下面这样
return (n % 4 == 0 && n % 100 != 0 || n % 400 == 0);
}
int main()
{
int y = 0;
for (y = 1000; y <= 2000; y++)
{
if (is_leap_year(y) == 1)
{
printf("%d ",y);
}
}
}
写一个函数实现一个整形有序数组的二分查找
int binary_search(int a[], int k, int s)
{
int left;
int right = s - 1;
while (left <= right)
{
int mid = (left + right) / 2;
if (a[mid] > k)
{
right = mid - 1;
}
else if (a[mid] < k)
{
left = mid + 1;
}
else
{
return mid;
}
}
return -1;
}
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
int key = 7;
int sz = sizeof(arr) / sizeof(arr[0]);
//找不到返回-1(不与数组下标冲突)
int ret = binary_search(arr,key,sz);
if (-1 == ret)
{
printf("找不到\n");
}
else
{
printf("找到了,下标是:%d\n", ret);
}
return 0;
}
有的同学可能会想能不能把这里的sz给删掉,在自定义函数内部求sz
求得的sz怎么会是1呢?
数组arr传参,实际传递的不是数组的本身
仅仅传过去了数组首元素的地址(实际上也是一种传址)
因此,我们不能在自定义函数内部求sz大小
写一个函数,每调用一次函数,会将num的值加1
void Add(int* p)
{
(*p)++;
}
int main()
{
int num = 0;
Add(&num);
printf("%d\n", num);
Add(&num);
printf("%d\n", num);
Add(&num);
printf("%d\n", num);
return 0;
}
通过这道题,相信大家对什么时候传值,什么时候传址有了自己的体会
函数的声明和定义
函数声明:
1.告诉编译器有一个函数叫什么,参数是什么,返回类型是什么
2.一般出现在函数使用前面。切记先声明后使用
3.一般放在头文件中
函数定义:
1函数定义是指函数的具体实现,交代函数的功能实现