自定义函数:
有两情况适合把代码封装成自定义函数: 1、代码量过多,一般代码量超过50行就要考虑封装成函数,方便管理代码,提高代码的安全性(程序员平均每50行会出现一个BUG)。 2、如果一个代码需要在不同位置多次执行,为了防止出现代码冗余,就要把它封装成函数,方便重复使用,也能降低可执行文件的大小。
函数声明:
返回值类型 函数名(参数列表);
1、根据函数的功能为函数取名字,在Linux系统下函数名全部小写,多个单词用下划线分隔。 2、参数列表,指的是函数执行时,调用者需要传递它的数据,此时重点关注的是参数的类型,在函数声明时可以忽略参数的名,如果函数执行时不需要调用者传递数据则写void。
返回值类型 函数名(类型名 形参名1,类型名 形参名2,...); 返回值类型 函数名(int n1,int n2,...); 返回值类型 函数名(void);
3、返回值类型,指的是函数的执行结果是什么类型的数据,如果函数没有返回值,则写void。 4、函数声明就是告诉编译器该函数的格式,方便编译器检查调用者的使用是否正确。
5、一般函数声明放在main函数之前
函数定义:
返回值类型 函数名(类型 参数名)
{
// 函数体
}
// 如果函数的定义出现在调用之前,函数声明可以省略
函数调用:
函数名(实参);
1、调用者会把实参赋值给形参变量。 2、函数的返回值会放置在调用位置,可立即使用,也可用变量保存。
练习3:实现一个判断是否是素数的函数,调用它打印出100-1000之间的所有素数
#include <stdio.h>
#include <stdbool.h>
bool is_prime(int num);
int main(int argc,const char* argv[])
{
for(int i=100; i<1000; i++)
{
if(is_prime(i))
printf("%d ",i);
}
bool flag = is_prime(9999);
}
bool is_prime(int num)
{
for(int i=2; i<=num/2; i++)
{
if(0 == num%i) return false;
}
return true;
}
作业2:输入两个日期yyyy-mm-dd,计算相差多少天,考虑封装函数
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
// 判断闰平年
bool is_leap(uint16_t year);
// 计算日期天数函数
uint32_t date_to_days(uint16_t year,uint8_t month,uint8_t day);
int main(int argc,const char* argv[])
{
uint16_t y = 0;
uint8_t m = 0,d = 0;
printf("请输入一个日期(yyyy-mm-dd)");
scanf("%hu-%hhu-%hhu",&y,&m,&d);
uint32_t day = date_to_days(y,m,d);
printf("请再输入一个日期(yyyy-mm-dd)");
scanf("%hu-%hhu-%hhu",&y,&m,&d);
printf("间隔了%d天\n",
abs((int)date_to_days(y,m,d)-(int)day));
}
// 计算日期天数函数
uint32_t date_to_days(uint16_t year,uint8_t month,uint8_t day)
{
uint32_t sum = day;
for(int y=1; y<year; y++)
{
sum += 365 + is_leap(y);
}
char arr[12] = {31,28,31,30,31,30,31,31,30,31,30,31};
arr[1] += is_leap(year);
for(int m=0; m<month-1; m++)
{
sum += arr[m];
}
return sum;
}
// 判断闰平年
bool is_leap(uint16_t year)
{
return 0==year%4 && 0!=year%100 || 0==year%400;
}
自定义函数要注意的问题:
1、函数的命名空间互相独立,函数之间传参是单向值传递(实参给形参赋值),所以两个函数之间不能通过传参共享变量。
#include <stdio.h>
int func(int num)
{
printf("func:%d\n",num);
num = 100;
printf("func:%d\n",num);
printf("func:%p\n",&num);
return 0;
}
// 实现一个交换两个变量的值的函数
void swap(int n1,int n2)
{
printf("swap:n1:%d n2:%d\n",n1,n2);
int temp = n1;
n1 = n2;
n2 = temp;
printf("swap:n1:%d n2:%d\n",n1,n2);
}
int main(int argc,const char* argv[])
{
int num = 10;
printf("main:%d\n",num);
printf("main:%p\n",&num);
func(num);
printf("main:%d\n",num);
int num1 = 10,num2 = 20;
printf("num1:%d num2:%d\n",num1,num2);
swap(num1,num2);
printf("num1:%d num2:%d\n",num1,num2);
}
2、C语言中如果函数的参数列表是空的,则意味着该函数提供任意类型、多个参数都可以调用,容易给调用者造成误会,影响代码的可读性,如果函数执行时不需要调用者传递数据则参数列表要写void,不要空着。
#include <stdio.h>
int func(void)
{
printf("func\n");
}
int main(int argc,const char* argv[])
{
func();
//func(10);
//func(3.14,10);
//func(3.14,"hehe",10);
}
3、如果函数有返回值但没有写return语句,调用该函数时依然有返回值。 当调用一个有返回值的函数时,系统会为调用者和被调用者约定一个空间用于存储返回值,而return语句的作用就是把一个数据存储到这个空间,如果没有写return语句,调用者依然会从共用空间读取返回值,只是读取到的数据是随机的。
#include <stdio.h>
int func(void)
{
//return 10;
}
int main(int argc,const char* argv[])
{
int num = func();
printf("%d\n",num);
}
gcc -Wall -Werror xxx.c 可以防止漏写return语句。
xxx.c:x:x: error: control reaches end of non-void function [-Werror=return-type]
4、当使用数组作为函数的参数传递时,它的长度信息就丢失了,(数组会蜕变成指针),无法使用sizeof计算数组的长度,需要调用者额外提供一个参数作为数组的长度。
void show_arr(int arr[],size_t len)
{
for(int i=0; i<len; i++)
{
printf("%d ",arr[i]);
}
}
int main(int argc,const char* argv[])
{
int arr[] = {1,2,3,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5};
show_arr(arr,sizeof(arr)/sizeof(arr[0]));
}
练习4:定义一个函数,功能是对一个数组进行升序排序
5、当函数中使用数组作为参数传递是,是"址传递" ,是可以被函数所共享数组
6、当使用二维数组作为函数的参数时,C语言规则定义二维数组时必须有列数,所以要行、列数在前,数组在后,并且把列数设置给数组。
void show_arr(int row,int col,int arr[][col])
{
for(int i=0; i<row; i++)
{
for(int j=0; j<col; j++)
{
printf("%d ",arr[i][j]);
}
printf("\n");
}
}
int main(int argc,const char* argv[])
{
int arr[2][5] = {
{1,2,3,4,5},
{2,3,4,5,6}
};
show_arr(2,5,arr);
return 0;
}
1850

被折叠的 条评论
为什么被折叠?



