一.前言
C 语⾔强调模块化编程,这⾥所说的模块就是函数,即把每⼀个独⽴的功能均抽象为⼀个函数来实现。从⼀定意义上讲,C 语⾔就是由⼀系列函数串组成的。 我们之前把所有代码都写在 main 函数中,这样虽然程序的功能正常实现,但显得杂乱⽆章,代码可读性、可维护性较差。学完本节之后,应把每个具体的独⽴功能单位均抽象为⼀个函数,在 main 函数中 调⽤各个函数。
1.概念:
函数是⼀系列 C 语⾔语句的集合, 为了完成某个可能会重复使⽤的功能, ⽽封装起来的代码。
设计的好处: 实现模块化的编程思想,提⾼程序的可读性。
函数设计要求: ⻅名知意。
示例代码:
2.使用方法:
方法1:先声明在定义
返回值类型 函数名(类型 参数1,类型 参数2….); // 函数的声明
int main()
{
函数名(参数1,参数2,参数3); // 函数的调用
return 0;
}
返回值类型 函数名(类型 参数1,类型 参数2….) // 函数的定义
{
C语⾔代码;
return 数据;
}
示例代码:
#include<stdio.h>
void printf_star();
/// *
/// **
/// ***
int main() // 入口
{
for(int k = 1; k <= 3; k++)
{
printf_star(); // 函数的调用
}
return 0;
}
/// void表示返回空
void printf_star() // 函数的定义
{
for(int i = 1; i <= 3; i++) // i代表行数
{
for(int j = 1;j <= i; j++) // 每行输出的星星个数
{
printf("*");
}
printf("\n");
}
return 0;
}
练习:
1. 设计⼀个 void do_sum() 函数,要求函数内部⽆参数和返回值,内部定义⼀个变量 int m, 要求⽤户从键盘输⼊⼀个数据赋值给 m 。
判断 m 的值 , 若是 m 为奇数,输出 1 + 3 + 5 + 7 + 9 +...+m 的和 . 若是 m 为偶数,输出 0 + 2 + 4 + 6 + 8 +...+m 的和 .
#include <stdio.h>
void do_sum() {
int m;
printf("请输入一个整数:");
scanf("%d", &m);
int sum = 0;
if (m % 2 != 0) {
// 计算奇数和 1+3+5+...+m
for (int i = 1; i <= m; i += 2) {
sum += i;
}
} else {
// 计算偶数和 0+2+4+...+m
for (int i = 0; i <= m; i += 2) {
sum += i;
}
}
printf("计算结果为:%d\n", sum);
}
int main() {
do_sum();
return 0;
}
2. 设计⼀个 void do_Result() 函数,要求函数内部定义 int a,b 两个变量,然后从键盘输⼊ 2 个整数赋值给 a,b ,并输出 (a + b) / 2 的值。
#include <stdio.h>
void do_Result() {
int a, b;
printf("请输入两个整数(用空格分隔):");
scanf("%d %d", &a, &b);
int result = (a + b) / 2;
printf("计算结果为:%d\n", result);
}
int main() {
do_Result();
return 0;
}
二.全局 静态 局部变量
1.全局变量:
全局变量:
在函数外部定义的变量,我们叫做全局变量.
特点:
(1) 在本⽂件中的任何函数中都可以使⽤。 ----作⽤域
(2) 在定义变量的时候,系统会申请内存空间,main()函数结束的时候,
系统会回收全局变量对应的内存空间. ----⽣命周期
(3) 全局变量未初始化,值默认为0
2.局部变量:
局部变量:
在函数内部定义的变量,我们叫做局部变量。
特点:
(1) 只能在定义它的那个函数内部使⽤,其他位置不能使⽤。---作⽤域
(2) 调⽤函数时,运⾏在定义变量代码的时候,系统会申请内存空间,
当定义该变量的函数调⽤结束后,系统会回收局部变量对应的内存空间. ----⽣命周期
(3) 局部变量未初始化,值默认为随机值。
示例代码:
#include<stdio.h>
int global = 20;
void do_global()
{
global ++;
printf("global = %d\n",global);
}
void do_local()
{
int t = 10;
t++;
printf("t = %d\n", t);
return ;
}
int main()
{
do_global();
do_global();
do_global();
printf("======================\n");
global++;
printf("g = %d\n",global);
do_local();
do_local();
do_local();
return 0;
}
3.静态局部变量
静态局部变量
格式: static 数据类型 变量名;
static int c;
特点:
(1)静态局部变量,可以改变原来变量的⽣命周期,当定义该变量的函数调⽤结束后,系统不会回收对应的内存空间,当main()函数结束后, 内存空间才会被回收.
(2)只能在定义它的那个函数内部使⽤,其他位置不能使⽤。---作⽤域
(3)静态局部变量未初始化,值默认为0
示例代码:
#include <stdio.h>
void do_local()
{
static int t = 10;
t++;
printf("t = %d\n",t);
}
int main()
{
do_local();
do_local();
do_local();
return 0;
}
练习:
定义全局变量 global_data = 0; 设计⽆参数的函数 global_calc() ,在这个函数内部 要求⽤户从键盘输⼊⼀个数据赋值给 global_data, 然后要求 该函数输出,从 1~global_data 之间所有能被 7 整除的数据。 main 函数来调⽤以下 global_calc() 这个函数。
#include <stdio.h>
int global_data = 0; // 定义全局变量
void global_calc() {
printf("请输入一个正整数:");
scanf("%d", &global_data);
if(global_data < 7) {
printf("没有符合条件的数字\n");
return;
}
printf("能被7整除的数字:\n");
for(int i = 7; i <= global_data; i++) {
if(i % 7 == 0) {
printf("%d ", i);
}
}
printf("\n");
}
int main() {
global_calc();
return 0;
}
三.函数传参之值传递
本质: 函数传参的本质, 变量之间的赋值操作。
功能 :在传递变量中保存的数据。核⼼思想是传递什么样⼦类型的变量。
在接收的位置,就定义什么样类型的变量来接受。
示例代码:
#include<stdio.h>
int do_calc(const int x,const int y)
{
int sum = 0;
printf("x = %d y = %d\n", x,y);
sum = x + y;
return sum;
}
int main()
{
int a = 0,b = 0;
int t = 0;
printf("please input two data :");
scanf("%d%d",&a,&b);
t = do_calc(a,b);
printf("a + b =%d\n", t);
t = t*100-8;
printf("expression result = %d\n",t);
return 0;
}
运行结果:
四.函数传参之地址传递
功能:我们在使⽤函数传参的时候,除了传递普通的变量外,在某些时刻不得不传递变量的地址。
传递变量的地址,我们在使⽤的时候,就只能通过指针来接收了。
好处:传递变量的地址,通过指针接收,可⽤通过指针修改原变量的值。
示例代码:
#include<stdio.h>
void change_value(int *x, int*y)
{
int t;
t = *x;
*x = *y;
*y = t;
return ;
}
void do_calc(int x, int y,int *m,int *n)
{
int sum1 = 0,sum2 = 0;
sum1 = x+y;
sum2 = x-y;
*m = sum1;
*n = sum2;
return ;
}
int main()
{
int a = 0,b = 0;
int ret1 = 0,ret2 = 0;
int t = 0;
printf("please input two data :");
scanf("%d%d",&a,&b);
do_calc(a,b,&ret1,&ret2);
printf("a+b=%d\n",ret1);
printf("a-b=%d\n",ret2);
return 0;
}
练习:
1.main() 函数定义 4 个变量, int a,b,max_value,sum. 要求⽤户从键盘输⼊2 个数据赋值给 a和b
2. 设计⼀个叫做 calc_data() 函数,⾃定义返回值和参数,要求该函数中 求出 a 和 b 中的最⼤值,函数调⽤结束后,要求给 main 函数 max_value 存储的 是两个数中较⼤的值。在求两个数的和,要求给 main 函数 sunm 存储的两个 数据的和。
3. 在 main 函数中打印 max_value 和 sum 的值。
#include <stdio.h>
void calc_data(int x, int y, int *max_ptr, int *sum_ptr) {
*max_ptr = (x > y) ? x : y;
*sum_ptr = x + y;
}
int main() {
int a, b, max_value, sum;
printf("请输入两个整数:");
scanf("%d %d", &a, &b);
calc_data(a, b, &max_value, &sum);
printf("最大值:%d\n", max_value);
printf("两数之和:%d\n", sum);
return 0;
}
五.. 函数传参之数组
本质 :函数传参的本质是变量间的赋值操作,数组名本质是⾸元素的地址,传递数组的⾸地址,我 们定义指针来接收接⼝。
示例代码:
#include<stdio.h>
void input_array(int *p,const int plen)
{
int i = 0;
printf("please input %d data:",plen);
for(i = 0; i < plen; i++)
{
scanf("%d",&p[i]);
}
}
void output_array(int *p, const int plen)
{
int i = 0;
for(i = 0; i < plen; i++)
{
printf("%d\n",&p[i]);
}
}
int main()
{
int a[5] = {0};
int len = sizeof(a) / sizeof(a[0]);
input_array(a,len);
output_array(a,len);
return 0;
}
六.字符串相关操作函数1
1.strcpy ---- 字符串拷贝函数
头文件: #include <string.h>
strcpy (char dest[], char src[]) ---------string copy
功能:把src数组中'\0'之前的所有字符,连同'\0'一起拷贝到dest中。
要求在定义dest时要足够大
参数:
dest--------目标数组
src --------源数组---------[数组首地址或字符串]
例如:
char buf [20] = {0};
strcpy(buf,"hello");
printf("buf = %s\n", buf);
代码示例:
#include<stdio.h>
#include<string.h>
int main()
{
char buf[20] = {'h','e','l','l','o','\0'};
int i = 0;
strcpy(buf,"QQ");
for(i = 0; i < 20; i++)
{
printf("%c: %d\n",buf[i],buf[i]);
}
printf("buf = %s\n",buf);
return 0;
}
2.strcat
头⽂件: #include <string.h>
strcat(char dest[], char src[]);
功能:把src数组'\0'之前的字符串追加到dest字符串后,若是dest中有'\0',会把dest中的'\0'给覆盖掉,然后组成的dest字符串会重新添加'\0'
参数:
例如:
@ dest ⽬标数组
@ src 源数组 [字符数组或字符串]
char buf[] = "hello";
strcat(buf," world");
代码示例:
#include<stdio.h>
#include<string.h>
int main()
{
char word[20];
char explain[20];
char sql[50];
printf("please input you select word:");
gets(word);
printf("please input word explain:");
gets(explain);
strcpy(sql,word);
strcat(sql,explain);
printf("%s\n",sql);
return 0;
}
七.字符串相关操作函数 2
1.strlen字符串长度计算函数--------不算\0
头文件: #include <stdio.h>
int strlen(const char s[]);
功能:计算s数组中第一个'\0'前字符的个数,并返回
参数:
@ s 目标数组,存放字符串
返回值:返回计算的字符个数
例如:
char buf[] = "hello";
int len = strlen(buf);
printf("len = %d\n",len);
代码示例:
2.strcmp
头文件:#include <string.h>
int strcmp(char s1[], char s2[]);
功能: 对s1和s2字符串中的每个字符逐个⽐较
若是s1中某个字符>s2中的某个字符,则返回⼤于0的数。
若是s1中某个字符<s2中的某个字符,则返回⼩于0的数。
若是当前s1和s2的字符相等,则⽐较后⼀个字符。若是完全相等,返回0
参数:
@ 待⽐较的数组s1 [字符串或字符数组]
@ 待⽐较的数组s2 [字符串或字符数组]
返回值:
在gcc的32bit编译器下,
返回如下值:若是s1 > s2 ,返回1
若是s1 == s2,返回0
若是s1 < s2 ,返回-1
char buf1[] = "hello";
char buf2[] = "heltt";
ret = strcmp(buf1,buf2);
示例代码:
#include <stdio.h>
#include <string.h>
int main()
{
char buf1[20];
char buf2[20];
int ret = 0;
printf("please input string buf1 : ");
gets(buf1);
printf("please input string buf2 : ");
gets(buf2);
ret = strcmp(buf1,buf2);
printf("ret = %d\n",ret);
if(ret > 0)
{
printf("buf1 > buf2");
}else if(ret == 0)
{
printf("buf1 == buf2");
}else if(ret < 0)
{
printf("buf1 < buf2");
}
putchar('\n');
return 0;
}
运行结果:
练习:
char buf[] = "I Love China";
(1) 设计⼀个 count_uppercase_character() 函数,⾃⼰定义参数和返回值,要求 统计上述数组中⼤写字符的个数。
(2) 调⽤⾃⼰设计的函数,并输出。
代码示例:
#include <stdio.h>
#include <ctype.h>
// 统计大写字母数量的函数
int count_uppercase_character(const char *str) {
int count = 0;
while (*str) {
if (isupper(*str)) {
count++;
}
str++;
}
return count;
}
int main() {
char buf[] = "I Love China";
int result = count_uppercase_character(buf);
printf("大写字母数量:%d\n", result); // 输出:3
return 0;
}
八.指针函数
本质:是⼀个函数,只不过返回值是⼀个地址。
思考:如何定义⼀个指针函数?
返回值类型 * 函数名称 (类型 1 参数 1, 类型 2 参数 2 ,…);
例如:
int * fun (int a, int b);
(也就是说,指针函数其实和普通的函数⼀样的,只不过返回值是⼀个指针类型。
并且它必须⽤同类型的指针变量来接受)
练习:
(1) 要求⼤家是设计⼀个 design_arary() 函数 , ⾃⼰定义返回值和参数。 要求 design_array() 函数中定义⼀个 ,static char a[100] = {0};, 然后 把数组⾸地址和⻓度返回。
(2) 设计⼀个 input_array() 函数,⾃定义返回和参数,要求⽤户从键盘 输⼊任意的字符串,存放到 a 数组中
(3) 设计⼀个 output_array() 函数,要求输出 a 数组中的每个⼀字符, 以空格作为区分 . w u h a n s h a n g h a i
(4) 设计⼀个 cout_space() 函数,⾃定义返回和参数,要求⽤户统计数组 a 中⽤户 输⼊的空格个数,并返回值给 main 函数。
(5)main ()函数调⽤以上函数,并输出空格个数。
代码示例:
#include <stdio.h>
#include <string.h>
// (1) 定义静态数组并返回信息
char* design_array(int *length) {
static char a[100] = {0};
*length = 100;
return a;
}
// (2) 输入字符串到数组
void input_array(char *arr) {
printf("请输入字符串:");
fgets(arr, 100, stdin);
arr[strcspn(arr, "\n")] = '\0'; // 去除换行符
}
// (3) 输出字符用空格分隔
void output_array(const char *arr) {
for(int i = 0; arr[i] != '\0'; i++) {
printf("%c ", arr[i]);
}
printf("\n");
}
// (4) 统计空格数量
int cout_space(const char *arr) {
int count = 0;
for(int i = 0; arr[i] != '\0'; i++) {
if(arr[i] == ' ') count++;
}
return count;
}
int main() {
int len;
char *arr = design_array(&len);
input_array(arr);
printf("\n字符输出:");
output_array(arr);
int spaces = cout_space(arr);
printf("空格数量:%d\n", spaces);
return 0;
}
九.函数指针
1.方法1
函数指针:本质是⼀个指针,只不过使⽤来函数的地址的。 通过函数指针来调⽤,我们需要的函数。
数据类型(*指针变量名)(类型1 参数1,类型2 参数2...);
注:(1)函数名代表函数的⾸地址
(2)上述中的参数1,参数2.。。等可省略
例如:
int (*funp)(int ,int) = add;
add(10,20);
funp(10,20);
#include<stdio.h>
int add(int a,int b)
{
return a + b;
}
/// 函数名就代表函数的地址
int sub(int a,int b)
{
return a - b;
}
int main(int argc,const char*argv[])
{
int ret = 0;
ret = add(10,20);
printf("a+b=%d\n",ret);
printf("add=%p\n",add);
printf("=============\n");
int(*funp)(int , int) = add;
printf("funp = %p\n",funp);
ret = funp(10,20);
printf("a+b=%d\n",ret);
funp = sub;
printf("a-b=%d\n",ret);
return 0;
}
2.方法2
概念:
回调函数就是⼀个通过函数指针调⽤的函数。 如果你把函数的地址作为参数传递给另⼀个函数,在另⼀个函数中通过指针来接收 , 通过指针来调⽤其函数,我们就说这是回调函数。
#include <stdio.h>
int add(int a,int b)
{
return a + b;
}
int sub(int a,int b)
{
return a - b;
}
int calc(int a,int b,int (*pfun)(int,int))
{
int result;
result = pfun(a,b);
return result;
}
int main()
{
int result = 0;
result = calc(20,10,add)
printf("a + b = %d\n",result);
result = calc(20,10,sub)
printf("a - b = %d\n",result);
return 0;
}