C语言---函数概念深入学习底子(3)

[复制链接]
发表于 2026-1-24 17:55:19 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
函数

1.函数是一段可以重复实行的代码

它可以继承差别的参数,
完成对应的使用。
下面的例子就是一个函数
  1. int plus(int n) {
  2.   return n;
  3. }
复制代码
上面的代码声明确一个函数plus()。
2.函数声明的语法有以下几点,必要注意。


  • 返回值范例。
    函数声明时,
    起首必要给出返回值的范例,
    上例是int,
    表现函数plus()返回一个整数。
  • 参数。
    函数名背面的圆括号内里,
    必要声明参数的范例和参数名,
    plus(int n)表现这个函数有一个整数参数n。
  • 函数体。
    函数体要写在大括号内里,
    背面(即大括号外貌)不必要加分号。
    大括号的起始位置,
    可以跟函数名在同一行,
    也可以另起一行。
  • return语句。
    return语句给出函数的返回值,
    步调运行到这一行,
    就会跳出函数体,
    竣事函数的调用。
    如果函数没有返回值,
    可以省略return语句,
    大概写成return;。
3.调用函数时,

只要在函数名背面加上圆括号就可以了,
现实的参数放在圆括号内里,
就像下面如许。
  1. int a = plus(13);
  2. // a 等于 14
复制代码
4.函数调用时,

参数个数必须与界说内里的参数个数同等(逐一对应),
参数过多或过少都会报错。
  1. int plus(int n) {
  2.   return n + 1;
  3. }
  4. plus(2, 2); // 报错
  5. plus();  // 报错
复制代码
上面示例中,函数plus()只能继承一个参数,传入两个参数或不传参数,都会报错。
5.函数必须声明后使用,

否则会报错。
也就是说,
肯定要在使用plus()之前,声明这个函数。
如果像下面如许写,编译时会报错。
  1. int a = plus(13);
  2. int plus(int n) {
  3.   return n + 1;
  4. }
复制代码
上面示例中,在调用plus_one()之后,才声明这个函数,编译就会报错。
6.C 语言尺度规定,

函数只能声明在源码文件的顶层,
不能声明在其他函数内部。
7.没有返回值的函数,

使用void关键字表现返回值的范例。
没有参数的函数,
声明时要用void关键字表现参数范例。
  1. void myFunc(void) {
  2.   // ...
  3. }
复制代码
上面的myFunc()函数,
既没有返回值,
调用时也不必要参数。
8.函数可以调用自身,

这就叫做递归(recursion)
下面是斐波那契数列的例子。
  1. unsigned long Fibonacci(unsigned n) {
  2.   if (n > 2)
  3.     return Fibonacci(n - 1) + Fibonacci(n - 2);
  4.   else
  5.     return 1;
  6. }
复制代码
上面示例中,
函数Fibonacci()调用了自身,
如许做可以简化算法。
9.main()

C 语言规定,
main()是步调的入口函数,
即全部的步调肯定要包罗一个main()函数。
步调总是从这个函数开始实行,
如果没有该函数,
步调就无法启动。
其他函数都是通过它引入步调的。
main()的写法与其他函数一样,
要给出返回值的范例和参数的范例,
就像下面如许。
  1. int main(void) {
  2.   printf("Hello World\n");
  3.   return 0;
  4. }
复制代码
上面示例中,
末了的return 0;表现函数竣事运行,返回0。
11.C 语言约定,

返回值0表现函数运行乐成,
如果返回其他非零整数,
就表现运行失败,
代码出了题目。
体系根据main()的返回值,
作为整个步调的返回值,
确定步调是否运行乐成。
正常环境下,
如果main()内里省略return 0这一行,
编译器会主动加上,
即main()的默认返回值为0。
以是,写成下面如许,
效果完全一样。
  1. int main(void) {
  2.   printf("Hello World\n");
  3. }
复制代码
由于 C 语言只会对main()函数默认添加返回值,
对其他函数不会如许做,
发起总是保存return语句
12.参数的传值引用

如果函数的参数是一个变量,
那么调用时,
传入的是这个变量的值的拷贝,
而不是变量自己。
  1. void increment(int a) {
  2.   a++;
  3. }
  4. int i = 10;
  5. increment(i);
  6. printf("%d\n", i); // 10
复制代码
上面示例中,
调用increment(i)以后,
变量i自己不会发生厘革,
还是即是10。
由于传入函数的是i的拷贝,
而不是i自己,
拷贝的厘革,
影响不到原始变量。
这就叫做“传值引用(单向值转达)”。
另有一种方法是是双向转达(也叫所在转达)下面会解说
以是,
如果参数变量发生厘革,
最好把它作为返回值传出来。
  1. int increment(int a) {
  2.   a++;
  3.   return a;
  4. }
  5. int i = 10;
  6. i = increment(i);
  7. printf("%d\n", i); // 11
复制代码
再看下面的例子,Swap()函数用来交换两个变量的值,由于传值引用,下面的写法不会收效。
  1. void Swap(int x, int y) {
  2.   int temp;
  3.   temp = x;
  4.   x = y;
  5.   y = temp;
  6. }
  7. int a = 1;
  8. int b = 2;
  9. Swap(a, b); // 无效
复制代码
上面的写法不会产生交换变量值的效果,
由于传入的变量是原始变量a和b的拷贝,
不管函数内部怎么使用,
都影响不了原始变量。
13.如果想要传入变量自己,

有一个办法,
就是传入变量的所在(所在转达是双向的)。
  1. void Swap(int* x, int* y) {
  2.   int temp;
  3.   temp = *x;
  4.   *x = *y;
  5.   *y = temp;
  6. }
  7. int a = 1;
  8. int b = 2;
  9. Swap(&a, &b);
复制代码
上面示例中,
通过传入变量x和y的所在,
函数内部就可以直接使用该所在,
从而实现交换两个变量的值。
固然跟传参无关,
这里特别注意下,
函数不要返回内部变量的指针。
  1. int* f(void) {
  2.   int i;
  3.   // ...
  4.   return &i;
  5. }
复制代码
上面示例中,
函数返回内部变量i的指针,
这种写法是错的。
由于当函数竣事运行时,
内部变量就消散了,
这时指向内部变量i的内存所在就是无效的,
再去使用这个所在好坏常伤害的。
14.函数指针

函数自己就是一段内存内里的代码,
C 语言答应通过指针获取函数。
  1. void print(int a) {
  2.   printf("%d\n", a);
  3. }
  4. void (*print_ptr)(int) = &print;
复制代码
上面示例中,
变量print_ptr是一个函数指针,
它指向函数print()的所在。
函数print()的所在可以用&print得到。
注意,
(*print_ptr)肯定要写在圆括号内里,
否则函数参数(int)的优先级高于*,
整个式子就会酿成void* print_ptr(int)。
有了函数指针,
通过它也可以调用函数。
  1. (*print_ptr)(10);
  2. // 等同于
  3. print(10);
复制代码
比力特别的是,
C 语言还规定,
函数名自己就是指向函数代码的指针,
通过函数名就能获取函数所在。
也就是说,
print和&print是一回事。
  1. if (print == &print) // true
复制代码
因此,上面代码的print_ptr等同于print。
  1. void (*print_ptr)(int) = &print;
  2. // 或
  3. void (*print_ptr)(int) = print;
  4. if (print_ptr == print) // true
复制代码
以是,对于恣意函数,
都有五种调用函数的写法。
  1. // 写法一
  2. print(10)
  3. // 写法二
  4. (*print)(10)
  5. // 写法三
  6. (&print)(10)
  7. // 写法四
  8. (*print_ptr)(10)
  9. // 写法五
  10. print_ptr(10)
复制代码
为了轻便易读,
一样平常环境下,
函数名前面都不加*和&。
15.这种特性的一个应用是,

如果一个函数的参数或返回值,
也是一个函数,
那么函数原型可以写成下面如许。
  1. int compute(int (*myfunc)(int), int, int);
复制代码
上面示例可以清楚地表明,
函数compute()的第一个参数也是一个函数。
16.函数原型

前面说过,
函数必须先声明,后使用。
由于步调总是先运行main()函数,
导致全部其他函数都必须在main()函数之前声明。
  1. void func1(void) {
  2. }
  3. void func2(void) {
  4. }
  5. int main(void) {
  6.   func1();
  7.   func2();
  8.   return 0;
  9. }
复制代码
上面代码中,
main()函数必须在末了声明,
否则编译时会产生告诫,
找不到func1()或func2()的声明。
但是,
main()是整个步调的入口,
也是紧张逻辑,
放在最前面比力好。
另一方面,
对于函数较多的步调,
包管每个函数的序次精确,
会变得很贫苦。
C 语言提供的办理方法是,
只要在步调开头处给出函数原型,
函数就可以先使用、后声明。
所谓函数原型,
就是提前告诉编译器,
每个函数的返回范例和参数范例。
其他信息都不必要,
也不消包罗函数体,
具体的函数实现可以背面再补上。
  1. int twice(int);
  2. int main(int num) {
  3.   return twice(num);
  4. }
  5. int twice(int num) {
  6.   return 2 * num;
  7. }
复制代码
上面示例中,
函数twice()的实现是放在main()背面,
但是代船埠部先给出了函数原型,
以是可以精确编译。
只要提前给出函数原型,
函数具体的实现放在那里,
就不紧张了。
17.函数原型包罗参数名也可以,

固然如许对于编译器是多余的,
但是阅读代码的时间,
大概有助于明确函数的意图。
  1. int twice(int);
  2. // 等同于
  3. int twice(int num);
复制代码
上面示例中,
twice函数的参数名num,
无论是否出如今原型内里,
都是可以的。
注意,
函数原型必须以分号末端。
一样平常来说,
每个源码文件的头部,
都会给出当前脚本使用的全部函数的原型。
18.函数阐明符

C 语言提供了一些函数阐明符,
让函数用法更加明确。
(1)extern 阐明符

对于多文件的项目,
源码文件会用到其他文件声明的函数。
这时,当前文件内里,
必要给出外部函数的原型,
并用extern阐明该函数的界说来自其他文件。
  1. extern int foo(int arg1, char arg2);
  2. int main(void) {
  3.   int a = foo(2, 3);
  4.   // ...
  5.   return 0;
  6. }
复制代码
上面示例中,
函数foo()界说在其他文件,
extern告诉编译器当前文件不包罗该函数的界说。
不外,
由于函数原型默认就是extern,
以是这里不加extern,
效果是一样的。
(2)static 阐明符

默认环境下,
每次调用函数时,
函数的内部变量都会重新初始化,
不会保存上一次运行的值。
static阐明符可以改变这种举动。
static用于函数内部声明变量时,
表现该变量只必要初始化一次,
不必要在每次调用时都举行初始化。
也就是说,
它的值在两次调用之间保持稳固。
  1. #include <stdio.h>
  2. void counter(void) {
  3.   static int count = 1;  // 只初始化一次
  4.   printf("%d\n", count);
  5.   count++;
  6. }
  7. int main(void) {
  8.   counter();  // 1
  9.   counter();  // 2
  10.   counter();  // 3
  11.   counter();  // 4
  12. }
复制代码
上面示例中,
函数counter()的内部变量count,
使用static阐明符修饰,
表明这个变量只初始化一次,
以后每次调用时都会使用上一次的值,
造成递增的效果。
注意,
static修饰的变量初始化时,
只能赋值为常量,
不能赋值为变量。
  1. int i = 3;
  2. static int j = i; // 错误
复制代码
上面示例中,
j属于静态变量,
初始化时不能赋值为另一个变量i。
别的,
在块作用域中,
static声明的变量有默认值0。
  1. static int foo;
  2. // 等同于
  3. static int foo = 0;
复制代码
static可以用来修饰函数自己。
  1. static int Twice(int num) {
  2.   int result = num * 2;
  3.   return(result);
  4. }
复制代码
上面示例中,
static关键字表现该函数只能在当前文件里使用,
如果没有这个关键字,
其他文件也可以使用这个函数(通过声明函数原型)。
static也可以用在参数内里,
修饰参数数组。
  1. int sum_array(int a[static 3], int n) {
  2.   // ...
  3. }
复制代码
上面示例中,static对步调举动不会有任何影响,
只是用来告诉编译器
该数组长度至少为3,
某些环境下可以加快步调运行速率。
别的,
必要注意的是,
对于多维数组的参数,
static仅可用于第一维的阐明。
(3)const 阐明符

函数参数内里的const阐明符,
表现函数内部不得修改该参数变量。
  1. void f(int* p) {
  2.   // ...
  3. }
复制代码
上面示例中,
函数f()的参数是一个指针p,
函数内部大概会改掉它所指向的值*p,
从而影响到函数外部。
为了克制这种环境,
可以在声明函数时,
在指针参数前面加上const阐明符,
告诉编译器,
函数内部不能修改该参数所指向的值。
  1. void f(const int* p) {
  2.   *p = 0; // 该行报错
  3. }
复制代码
上面示例中,声明函数时,
const指定不能修改指针p指向的值,
以是*p = 0就会报错。
但是上面这种写法,
只限定修改p所指向的值,
而p自己的所在是可以修改的。
  1. void f(const int* p) {
  2.   int x = 13;
  3.   p = &x; // 允许修改
  4. }
复制代码
上面示例中,
p自己是可以修改,
const只限定*p不能修改。
如果想限定修改p,
可以把const放在p前面。
  1. void f(int* const p) {
  2.   int x = 13;
  3.   p = &x; // 该行报错
  4. }
复制代码
如果想同时限定修改p和*p,
必要使用两个const。
  1. void f(const int* const p) {
  2.   // ...
  3. }
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!qidao123.com:ToB企服之家,中国第一个企服评测及软件市场,开放入驻,技术点评得现金
回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表