函数
概述
几乎所有的编程语言都有函数(或方法)的概念,
那么什么是函数呢?
什么是函数?
简单地说,,一台大型机器中的“微型执行单元”:
- 输入:当你启动一个函数时,你通常会传递给它一些数据,这些数据被称为或“输入参数”。
- 处理:函数一旦接收到这些输入,它会根据内部的指令和逻辑进行处理和运算。
- 输出:处理完成后,函数通常会提供一个结果,这就是我们所说的。
如下图所示:
这实际上就是函数的三个基本特征:参数输入、功能执行以及输出返回值。
总之,函数是C语言编程中的基本组成单位。它是一个代码块,用于执行特定的任务,可以接收参数并返回一个值。
函数分类
C 程序是由函数组成的,我们写的代码都是由主函数 main()开始执行的。函数是 C 程序的基本模块,是用于完成特定任务的程序代码单元。
从函数定义的角度看,函数可分为系统函数和用户定义函数两种:
- 系统函数,即库函数:这是由编译系统提供的,用户不必⾃⼰定义这些函数,可以直接使用它们,如我们常用的打印函数printf()。
- 用户定义函数:用以解决用户的专门需要。
不论是来源于标准库还是由用户自定义,所有的函数在C语言中都遵循相同的基本原则和语法结构。
函数的定义
函数定义格式
函数定义的一般形式:
返回类型 函数名(形式参数列表){
数据定义部分;
执行语句部分;
}
// 或者下面这种形式
返回值类型 函数名(形参列表){
// 函数体
}
其中:
- 函数的返回值类型就填一个数据类型,比如我们上面提到的int和float。
- 函数名是一个标识符,遵循命名规则即可。
- "(形参列表)"里的内容用于确定此函数需要什么参数数据输入。比如需要两个整数输入,就写"(int a, int b)"。同时在大括号里面还可以用a和b来使用传入函数的参数数据。
- 如果函数有返回值,那么"return + 返回值"会结束函数,并给出函数的返回值。注意函数返回值要和函数定义时的返回值类型对应。
记住以下几个概念:
- 被"{}"包裹的就是函数体,注意此大括号不可省略。函数体就是函数实现的具体逻辑,函数做了什么。
- 身体上面的就是头,包括"返回值类型 函数名(形参列表)"统称函数头。函数头是函数定义的起始部分
函数名
理论上是可以随意起名字,最好起的名字见名知意,应该让用户看到这个函数名字就知道这个函数的功能。注意,函数名的后⾯有个圆换号(),代表这个为函数,不是普通的变量名。
形参列表
在定义函数时指定的形参,,因此称它们是形式参数或虚拟参数,简称形参,表⽰它们并不是实际存在的数据,所以,形参里的变量不能赋值。
void max(int a = 10, int b = 20) // error, 形参不能赋值
{ }
在定义函数时指定的形参,必须是,类型+变量的形式:
//1: right, 类型+变量
void max(int a, int b){}
//2: error, 只有类型,没有变量
void max(int, int){}
//3: error, 只有变量,没有类型
int a, int b;
void max(a, b){}
在定义函数时指定的形参,可有可⽆,根据函数的需要来设计,
// 没形参, 圆括号内容为空
void max(){}
// 没形参, 圆括号内容为void关键字
void max(void){}
函数体
花括号{ }里的内容即为函数体的内容,这里为,这和以前的写代码没太⼤区别,以前我们把代码写在main()函数里,现在只是把这些写到别的函数里。
返回值
函数的返回值是通过函数中的return语句获得的,return后⾯的值也可以是一个表达式。
尽量保证return语句中表达式的值和函数返回类型是同一类型。
cint max() // 函数的返回值为int类型 { int a = 10; return a;// 返回值a为int类型,函数返回类型也是int,匹配 }
如果函数返回的类型和return语句中表达式的值不一致,则以函数返回类型为准,即。对数值型数据,可以⾃动进行类型转换。
cdouble max() // 函数的返回值为double类型 { int a = 10; return a;// 返回值a为int类型,它会转为double类型再返回 }
⚠️:如果函数返回的类型和return语句中表达式的值不一致,而它又⽆法⾃动进行类型转换,程序则会报错。
return语句的另一个作用为中断return所在的执行函数,类似于break中断循环、switch语句一样。
cint max(){ return 1;// 执行到,函数已经被中断,所以下⾯的return 2⽆法被执行到 return 2;// 没有执行 }
如果函数带返回值,return后⾯必须跟着一个值,如果函数没有返回值,函数名字的前⾯必须写一个void关键字,这时候,我们写代码时也可以通过return中断函数(也可以不用),只是这时,return后⾯不带内容( 分号“;”除外)。
c// 最好要有void关键字 void max(){ return; // 中断函数,这个可有可⽆ }
函数的声明
如果使用用户⾃⼰定义的函数,而该函数与调用它的函数(即主调函数)不在同一文件中,或者,则必须在调用此函数之前对被调用的函数作声明。
所谓函数声明,就是在函数尚在未定义的情况下,事先将该函数的有关信息通知编译系统,相当于告诉编译器,函数在后⾯定义,以便使编译能正常进行。
⚠️ :一个函数只能被定义一次,但可以声明多次。
#include <stdio.h>
#include <stdlib.h>
int maxx(int x, int y);// 函数的声明,分号不能省略
// int maxx(int, int); // 另一种⽅式
int main(void) {
int a = 10, b = 25, num_max = 0;
num_max = maxx(a, b);// 函数的调用
printf("num_maxx = %d\n", num_max);
return 0;
}
// 函数的定义
int maxx(int x, int y) { return x > y ? x : y; }
程序输出:
num_maxx = 25
函数定义和声明的区别
- 定义是指对函数功能的确⽴,包括指定函数名、函数类型、形参及其类型、函数体等,它是一个完整的、独⽴的函数单位。
- 声明的作用则是把函数的名字、函数类型以及形参的个数、类型和顺序(注意,不包括函数体)通知编译系统,以便在对包含函数调用的语句进行编译时,据此对其进行对照检查(例如函数名是否正确,实参与形参的类型和个数是否一致)。
函数的调用
这和main()函数不一样,main()为编译器设定好⾃动调用的主函数,⽆需人为调用,我们都是在main()函数里调用别的函数,当调用函数时,需要关⼼:
- 头文件:包含指定的头文件
- 函数名字:函数名字必须和头文件声明的名字一样
- 功能:需要知道此函数能⼲嘛后才调用
- 参数:参数类型要匹配
- 返回值:根据需要接收返回值
函数执行流程
#include <stdio.h>
#include <stdlib.h>
void print_test() { printf("this is for test\n"); }
int main(void) {
print_test();// print_test函数的调用
return 0;
}
程序输出:
this is for test
进⼊main()函数
调用print_test()函数:
- 它会在main()函数的前寻找有没有一个名字叫“print_test”的函数定义;
- 如果找到,接着检查函数的参数,这里调用函数时没有传参,函数定义也没有形 参,参数类型匹配;
- 开始执行print_test()函数,这时候,main()函数里⾯的执行会阻塞( 停 )在print_test()这 一行代码,等待print_test()函数的执行。
print_test()函数执行完( 这里打印一句话 ),main()才会继续往下执行,执行到return 0, 程 序执行完毕。
函数的形参和实参
- 形参出现在函数定义中,在整个函数体内都可以使用,离开该函数则不能使用。
- 实参出现在主调函数中,进⼊被调函数后,实参也不能使用。
- 实参变量对形参变量的数据传递是“值传递”,即单向传递,
- 在调用函数时,编译系统临时给形参分配存储单元。调用结束后,形参单元被释放。
- 实参单元与形参单元是不同的单元。调用结束后,形参单元被释放,函数调用结束返回主调函数后则不能再使用该形参变量。实参单元仍保留并维持原值。因此,
无参函数调用
如果是调用⽆参函数,则不能加上“实参”,但括号不能省略。
// 函数的定义
void test(){}
int main(){
// 函数的调用
test(); // right, 圆括号()不能省略
test(250); // error, 函数定义时没有参数
return 0;
}
有参函数调用
如果实参表列包含多个实参,则各参数间用逗号隔开。
c// 函数的定义 void test(int a, int b){} int main(){ int p = 10, q = 20; test(p, q); // 函数的调用 return 0; }
实参与形参的个数应相等,类型应匹配(相同或赋值兼容)。实参与形参按顺序对应,一对一地传递数据。
实参可以是常量、变量或表达式, 所以,这里的变量是在圆括号( )外⾯定义好、赋好值的变量。
c// 函数的定义 void test(int a, int b){} int main(){ // 函数的调用 int p = 10, q = 20; test(p, q); // right test(11, 30 - 10); // right test(int a, int b); // error, 不应该在圆括号里定义变量 return 0; }
函数返回值
如果函数定义没有返回值,函数调用时不能写void关键字,调用函数时也不能接收函数的返回值。
c// 函数的定义 void test(){} int main(){ // 函数的调用 test(); // right void test(); // error, void关键字只能出现在定义,不可能出现在调用的地⽅ int a = test(); // error, 函数定义根本就没有返回值 return 0; }
如果函数定义有返回值,这个返回值我们根据用户需要可用可不用,但是,假如我们需要使用这个函数返回值,
c// 函数的定义, 返回值为int类型 int test(){} int main(){ // 函数的调用 int a = test(); // right, a为int类型 int b; b = test(); // right, 和上⾯等级 char *p = test(); // 虽然调用成功没有意义, p为char *, 函数返回值为 int, 类型不匹配 // error, 必须定义一个匹配类型的变量来接收返回值 // int只是类型,没有定义变量 int = test(); // error, 必须定义一个匹配类型的变量来接收返回值 // int只是类型,没有定义变量 int test(); return 0; }
产生随机数
#include <time.h>
time_t time(time_t *t);
功能:获取当前系统时间
参数:常设置为NULL
返回值:当前系统时间, time_t 相当于long类型,单位为
#include <stdlib.h>
void srand(unsigned int seed);
功能:用来设置rand()产生随机数时的随机种⼦
参数:如果每次seed相等,rand()产生随机数相等
返回值:⽆
#include <stdlib.h>
int rand(void);
功能:返回一个随机数值
参数:⽆
返回值:随机数
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main() {
time_t tm = time(NULL); //得到系统时间
srand((unsigned int) tm);//随机种⼦只需要设置一次即可
int r = rand();
printf("r = %d\n", r);
return 0;
}
程序输出:
r = 687136184
函数的作用
函数的使用可以省去重复代码的编写,降低代码重复率
#include <stdio.h>
#include <stdlib.h>
// 求两数的最⼤值
int maxx(int a, int b) {
if (a > b) {
return a;
} else {
return b;
}
}
int main() {
// 操作1 ……
// ……
int a1 = 10, b1 = 20, c1 = 0;
c1 = maxx(a1, b1);// 调用max()
printf("c1 = %d\n", c1);
// 操作2 ……
// ……
int a2 = 11, b2 = 21;
c1 = maxx(a2, b2);// 调用max()
printf("c1 = %d\n", c1);
// ……
return 0;
}
程序输出:
c1 = 20
c1 = 21
函数可以让程序更加模块化,从而有利于程序的阅读,修改和完善
假如我们编写一个实现以下功能的程序:读⼊一行数字;对数字进行排序;找到它们的平均值;打印出一个柱状图。如果我们把这些操作直接写在main()里,这样可能会给用户感觉代码会有点凌乱。但,假如我们使用函数,这样可以让程序更加清晰、模块化:
#include <stdio.h>
#include <stdlib.h>
// 求两数的最⼤值
int maxx(int a, int b) {
if (a > b) {
return a;
} else {
return b;
}
}
int main() {
float list[50];
// 这里只是举例,函数还没有实现
readlist(list, 50);
sort(list, 50);
average(list, 50);
bargraph(list, 50);
return 0;
}
这里我们可以这么理解, 默认情况下,公司就是一个⼤部门( 只有一个部门的情况下 ),相当于C程序的main()函数。如果公司比较⼩( 程序比较⼩ ),因为任务少而简单,一个部门即可( main()函数 )胜任。但是,如果这个公司很⼤( 大型应用程序 ),任务多而杂,如果只是一个部门管理( 相当于没有部门,没有分工 ),我们可想而知,公司管理、运营起来会有多混乱,不是说这样不可以运营,只是这样不完美而已,如果根据公司要求分成一个个部门( 根据功能封装一个一个函数 ),招聘由行政部门负责,研发由技术部门负责等,这样就可以分工明确,结构清晰,⽅便管理,各部门之间还可以相互协调。
main函数与exit函数
在main函数中调用exit和return结果是一样的,但在⼦函数中调用return只是代表⼦函数终⽌了,在⼦函数中调用exit,那么程序终⽌。
#include <stdio.h>
#include <stdlib.h>
void fun() {
printf("fun\n");
//return;
exit(0);
}
int main(void) {
fun();
while (1);
return 0;
}
程序输出:
fun
关于主函数,我们需要知道
- 主函数是程序的入口,任何C程序的运行都是从主函数开始的。C语言的这种设计在编程领域引领潮流,被许多后来的编程语言采用,例如C++、Java、C#以及Go等。
- int 是 main 函数的返回值类型,表示该函数执行完毕后将返回一个整数值给操作系统。
- 我们约定,main函数的返回值是0时表示程序正常终止,非0则表示程序意外终止。
函数类型
通过什么来区分两个不同的函数?
一个函数在编译时被分配一个⼊⼜地址,这个地址就称为函数的指针,函数名代表函数的⼊ ⼜地址。
函数三要素: 名称、参数、返回值。C语⾔中的函数有⾃⼰特定的类型。
C语⾔中通过typedef为函数类型重命名:
typedef int f(int, int); // f 为函数类型
typedef void p(int);// p 为函数类型
这一点和数组一样,因此我们可以用一个指针变量来存放这个⼊⼜地址,然后通过该指针变 量调用函数。
注意:通过函数类型定义的变量是不能够直接执行,因为没有函数体。只能通过类型定义一 个函数指针指向某一个具体函数,才能调用。
typedef int(p)(int, int);
void my_func(int a,int b){ printf("%d %d\n",a,b); }
void test(){
p p1;
//p1(10,20); //错误,不能直接调用,只描述了函数类型,但是并没有定义函数体,没 有函数体⽆法调用
p* p2 = my_func; p2(10,20); //正确,指向有函数体的函数⼊⼜地址
}
函数指针
函数指针(指向函数的指针)
- 函数指针定义⽅式(先定义函数类型,根据类型定义指针变量);
- 先定义函数指针类型,根据类型定义指针变量;
- 直接定义函数指针变量;
int my_func(int a, int b) {
printf("ret:%d\n", a + b);
return 0;
}
//1. 先定义函数类型,通过类型定义指针
void test01() {
typedef int(FUNC_TYPE)(int, int);
FUNC_TYPE *f = my_func;
//如何调用?
(*f)(10, 20);
f(10, 20);
}
//2. 定义函数指针类型
void test02() {
typedef int (*FUNC_POINTER)(int, int);
FUNC_POINTER f = my_func;//如何调用?
(*f)(10, 20);
f(10, 20);
}
//3. 直接定义函数指针变量
void test03() {
int (*f)(int, int) = my_func;//如何调用?
(*f)(10, 20);
f(10, 20);
}
函数指针数组
函数指针数组,每个元素都是函数指针。
void func01(int a) { printf("func01:%d\n", a); }
void func02(int a) { printf("func02:%d\n", a); }
void func03(int a) { printf("func03:%d\n", a); }
void test() {
#if 0
//定义函数指针
void(*func_array[])(int) = { func01, func02, func03 };
#else
void (*func_array[3])(int);
func_array[0] = func01;
func_array[1] = func02;
func_array[2] = func03;
#endif
for (int i = 0; i < 3; i++) {
func_array[i](10 + i);
(*func_array[i])(10 + i);
}
}
回调函数
函数指针做函数参数(回调函数),函数参数除了是普通变量,还可以是函数指针变量。在 C 语言中把函数指针作为参数传递是十分普遍的,这样的函数我们称之为回调(callback)函数。
//形参为普通变量
void fun( int x ){}
//形参为函数指针变量
void fun( int(*p)(int a) ){}
函数指针变量常见的用途之一是把指针作为参数传递到其他函数,指向函数的指针也可以作为参数,以实现函数地址的传递。
-
void (*func)(int, int)
-
&func1
或者func1
-
(*func)(实参列表)
或者func(实参列表)
代码示例01
#include <stdio.h>
int plus(int a, int b) { return a + b; }
int sub(int a, int b) { return a - b; }
int main() {
// 函数指针变量
int (*func)(int, int);
// 把函数地址赋值给函数指针变量,函数地址表示方式为 &plus,通过函数指针调用函数
func = +
printf("%d\n", (*func)(10, 20));
// 把函数地址赋值给函数指针变量,函数地址表示方式为 sub,通过函数指针调用函数
func = sub;
printf("%d\n", func(10, 20));
return 0;
}
程序输出:
30
-10
代码示例02
#include <stdio.h>
int plus(int a, int b) { return a + b; }
int sub(int a, int b) { return a - b; }
//函数指针 做函数的参数 --- 回调函数
void Calculator(int (*myCalculate)(int, int), int a, int b) {
int ret = myCalculate(a, b);
printf("ret = %d\n", ret);
}
int main(int argc, char const *argv[])
{
Calculator(plus, 10, 20);
Calculator(sub, 10, 20);
return 0;
}
程序输出:
ret = 30
ret = -10
注意:函数指针和指针函数的区别
- 函数指针是指向函数的指针;
- 指针函数是返回类型为指针的函数;
C 函数库中一些功能强大的函数都是以函数指针作为参数,比如 qsort
函数。qsort
声明在 <stdlib.h>
头文件中,它是可以给任意数组排序的通用函数。数组的元素也可以是任意类型,甚至可以是结构体或者指针,因此,我们必须告诉 qsort
函数如何比较数组元素的大小。传入一个比较函数可以提供这些信息。
qsort
函数的原型如下:
void qsort(void* base, size_t nmemb, size_t size, int (*compar)(const void *, const void *));
base
指向数组中要排序的第一个元素 (一般是数组的第一个元素)。nmemb
是要排序元素的数量 (可以小于或等于数组中元素的个数)。size
表示数组元素的大小。compar
是比较函数,如果第一个参数比第二个参数小则返回负数,相等则返回零,第一个参数大于第二个参数则返回正数。
qsort 函数可以对任意类型的数组进行排序。默认情况下,qsort 函数使用
strcmp
函数来比较字符串,并且是从小到大排序。
compar
是比较函数,一般也会被称为钩子函数。qsort
通过参数伸出一个钩子,通过钩子函数来比较数组元素。
qsort 示例
从键盘录入 5 个学生的信息,然后对学生进行排序。排序规则如下:先按总分从高到低进行排序,如果总分一样,依次按语文、数学、英语的分数从高到低进行排序;如果各科成绩都一样,则按名字的字典顺序从小到大排序。
学生结构体定义如下:
typedef struct {
int id;
char name[25];
int chinese;
int math;
int english;
} Student;
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 学生结构体定义
typedef struct {
int id;
char name[25];
int chinese;
int math;
int english;
} Student;
int compare(const void *p1, const void *p2);
int main(void) {
Student s[5];
for (int i = 0; i < 5; ++i)
{
scanf("%d%s%d%d%d", &s[i].id, s[i].name, &s[i].chinese, &s[i].math, &s[i].english);
}
qsort(s, 5, sizeof(Student), compare);
puts("----------------------------");
for (int i = 0; i < 5; ++i)
{
printf("%d %s %d %d %d\n", s[i].id, s[i].name, s[i].chinese, s[i].math, s[i].english);
}
return 0;
}
int compare(const void *p1, const void *p2) {
const Student *s1 = p1;
const Student *s2 = p2;
int total1 = s1->chinese + s1->math + s1->english;
int total2 = s2->chinese + s2->math + s2->english;
if(total1 != total2) {
// qosrt默认从小到大排序,且返回<0 则p1小 越靠前
// 但是排名顺序应该为分数越高名次越靠前,所以分数大的应该靠前
return total2 - total1;
}
if(s1->chinese != s2->chinese) {
return s2->chinese - s1->chinese;
}
if(s1->math != s2->math) {
return s2->math - s1->math;
}
if(s1->english != s2->english) {
return s2->english - s1->english;
}
return strcmp(s1->name, s2->name);
}
程序运行情况:
1 liuyifei 98 95 95
2 tangyutong 95 95 98
3 xixi 100 100 100
4 wangyuyan 100 100 100
5 wuqing 95 98 95
----------------------------
4 wangyuyan 100 100 100
3 xixi 100 100 100
1 liuyifei 98 95 95
5 wuqing 95 98 95
2 tangyutong 95 95 98
递归函数
基本概念
C通过运行时堆栈来支持递归函数的实现。递归函数就是直接或间接调用⾃⾝的函数。
普通函数调用
void funB(int b) {
printf("b = %d\n", b);
}
void funA(int a) {
funB(a - 1);
printf("a = %d\n", a);
}
int main(void) {
funA(2);
printf("main\n");
return 0;
}
函数的调用流程如下:
递归函数调用
void fun(int a) {
if (a == 1) {
printf("a = %d\n", a);
return;//中断函数很重要
}
fun(a - 1);
printf("a = %d\n", a);
}
int main(void) {
fun(2);
printf("main\n");
return 0;
}
函数的调用流程如下:
作业:递归实现给出一个数8793,依次打印千位数字8、百位数字7、⼗位数字9、个位数字3。
void recursion(int val){
if (val == 0){
return;
}
int ret = val / 10;
recursion(ret);
printf("%d ",val % 10);
}
可以参考下⾯一个普通函数实现改为递归函数实现的案例
#include <stdio.h>
double power(double n, int p);// ANSI函数原型
int main(void) {
double x, xpow;
int exp;
printf("Enter a number and the positive integer power");
printf(" to which\nthe number will be raised. Enter q");
printf(" to quit.\n");
while (scanf("%lf%d", &x, &exp) == 2) {
xpow = power(x, exp);// 函数调用
printf("%.3g to the power %d is %.5g\n", x, exp, xpow);
printf("Enter next pair of numbers or q to quit.\n");
}
printf("Hope you enjoyed this power trip -- bye!\n");
return 0;
}
double power(double n, int p) {
// 函数定义
if (p == 0) {
if (n > -0.000001 && n < 0.000001) { printf("0的0次幂未定义,因此把该值处理为1。\n"); }
return 1;
} else if (n > -0.000001 && n < 0.000001) {
return 0;
} else if (p < 0) {
double pow = 1;
int i;
for (i = p; i < 0; i++)
pow /= n;
return pow;
}
double pow = 1;
int i;
for (i = 1; i <= p; i++)
pow *= n;
return pow;// 返回pow的值
}
#include <stdio.h>
double power(double n, int p);// ANSI函数原型
double power1(double n, int p, double pow);
int main(void) {
double x, xpow;
int exp;
printf("Enter a number and the positive integer power");
printf(" to which\nthe number will be raised. Enter q");
printf(" to quit.\n");
while (scanf("%lf%d", &x, &exp) == 2) {
xpow = power(x, exp);// 函数调用
printf("%.3g to the power %d is %.5g\n", x, exp, xpow);
printf("Enter next pair of numbers or q to quit.\n");
}
printf("Hope you enjoyed this power trip -- bye!\n");
return 0;
}
double power(double n, int p) {
// 函数定义
double pow = 1;
if (p == 0) {
if (n > -0.000001 && n < 0.000001) { printf("0的0次幂未定义,因此把该值处理为1。\n"); }
return 1;
} else if (n > -0.000001 && n < 0.000001) {
return 0;
}
pow = power1(n, p, pow);
return pow;
}
double power1(double n, int p, double pow) {
if (p == 0) { return pow; }
if (p > 0) {
pow *= n;
p--;
return power1(n, p, pow);
} else {
pow /= n;
p++;
return power1(n, p, pow);
}
}
在上⾯改写的例⼦当中,如果power1函数中调用递归的时间没有使用return程序就⽆法得到预期的结果;。
又如下⾯一个实际使用递归函数然后改写成另一个需求的例⼦(在该程序中,如果r的值是0,to_binary()函数就显⽰字符'0'; 如果r的值是1,to_binary()函数则显⽰字符'1'。条件表达式r == 0 ? '0' : '1'
用于把数值转换成字符。);第二部分为了让程序中的to_binary()函数更通用,编写了一个to_base_n()函数接受两个参数,且第二个参数在2~10范围内,然后以第2个参数中指定的进制打印第1个参数的数值。例如,to_base_n(129,8)显⽰的结果为201,也就是129的⼋进制数。在一个完整的程序中测试该函数。
#include <stdio.h>
void to_binary(unsigned long n);
int main(void) {
unsigned long number;
printf("Enter an integer (q to quit):\n");
while (scanf("%lu", &number) == 1) {
printf("Binary equivalent: ");
to_binary(number);
putchar('\n');
printf("Enter an integer (q to quit):\n");
}
printf("Done.\n");
return 0;
}
/* 递归函数 */
void to_binary(unsigned long n) {
int r;
r = n % 2;
if (n >= 2) to_binary(n / 2);
putchar(r == 0 ? '0' : '1');
return;
}
#include <stdio.h>
void to_binary(unsigned long n);
void to_base_n(unsigned long n, int x);
int main(void) {
unsigned long number;
printf("Enter an integer (q to quit):\n");
while (scanf("%lu", &number) == 1) {
printf("Binary equivalent: ");
to_binary(number);
putchar('\n');
to_base_n(number, 8);
putchar('\n');
printf("Enter an integer (q to quit):\n");
}
printf("Done.\n");
return 0;
}
/* 递归函数 */
void to_binary(unsigned long n) {
int r;
r = n % 2;
if (n >= 2) to_binary(n / 2);
putchar(r == 0 ? '0' : '1');
return;
}
void to_base_n(unsigned long n, int x) {
if (x < 2 || x > 10) { return; }
int r;
r = n % x;
if (n >= x) to_base_n(n / x, x);
putchar('0' + r);
return;
}
递归实现字符串反转
#include <stdio.h>
#include <string.h>
int reverse1(char *str) {
if (str == NULL) { return -1; }
// 函数递归调用结束条件
if (*str == '\0') { return 0; }
reverse1(str + 1);
printf("%c", *str);
return 0;
}
char buf[1024] = {0};//全局变量
int reverse2(char *str) {
if (str == NULL) { return -1; }
// 函数递归调用结束条件
if (*str == '\0') { return 0; }
reverse2(str + 1);
strncat(buf, str, 1);
return 0;
}
int reverse3(char *str, char *dst) {
if (str == NULL || dst == NULL) { return -1; }
// 函数递归调用结束条件
if (*str == '\0') { return 0; }
reverse3(str + 1);
strncat(dst, str, 1);
return 0;
}
多文件(分文件)编程
分文件编程
- 把函数声明放在头文件
xxx.h
中,在主函数中包含相应头文件 - 在头文件对应的
xxx.c
中实现xxx.h
声明的函数
防止头文件重复包含
当一个项目比较⼤时,往往都是分文件,这时候有可能不⼩⼼把同一个头文件 include 多次,或者头文件嵌套包含。
a.h` 中包含 `b.h` : `#include "b.h"
b.h` 中包含 `a.h`: `#include "a.h"
main.c
中使用其中头文件:
#include "a.h"
int main(){
return 0;
}
编译上⾯的例⼦,会出现如下错误:
为了避免同一个文件被include多次,C/C++中有两种⽅式,一种是 #ifndef ⽅式,一种是 #pragma once ⽅式。
#ifndef ⽅式
c#ifndef __SOMEFILE_H__ #define __SOMEFILE_H__ // 声明语句 #endif
#pragma once ⽅式
c#pragma once // 声明语句