1. 函数是什么
在C语言中的函数是怎样的?
维基百科中对函数的定义:子程序
- 一般会有输入参数并有返回值,提供对过程的封装和细节的隐藏。这些代码通常被集成为软件库。
- 函数在面向过程的语言中已经出现。是结构(Struct)和类(Class)的前身。本身就是对具有相关性语句的归类和对某过程的抽象。
基本释义:
【释义】:能被其他程序调用,在实现某种功能后能自动返回到调用程序去的程序,其最后一条指令一定是返回指令,故能保证重新返回到调用它的程序中去,也可调用其他子程序,甚至可自身调用(如递归)。
例如:这个打印素数的程序
#include <stdio.h>
#include <math.h>
//素数是不能被2到本身开方之间任一整数整除的整数
int main()
{
int i, j, k;
printf("100--200间的素数输出:\n");
for (i = 100; i <= 200; i++)
{
k = sqrt(i);
for (j = 2; j <= k; j++)
{
if (i % j == 0)
break;
}
if (j > k)
printf("%d\ ", i);
}
printf(" \n ");
system("pause");
return 0;
}
像这样的功能如果被⼀个⼈频繁的使用,或者被多个人频繁的使⽤,那如果每次我们都自己重复的实现,那将大的降低我们开发效率,所以我们想着:可不可以整⼀个函数(子程序)来独⽴完成这部分功能,我们在合适的场景下去使⽤(函数调用)这个函数就可以了,C语言是允许的,C语言提供了函数的功能就可以解决这样的问题。
2.函数的分类
库函数(完成有限功能)
我们在描述的一些基础的功能,它们不是业务性的代码,我们在开发的过程中每个程序员都可能用的到,为了⽀持可移植性和提高程序的效率,所以C语⾔的基础库中提供了⼀系列类似的库函数。IO函数
字符操作函数
内存操作函数
时间/日期函数
数学函数
其他库函
注:使用库函数,必须包含 #include 对应的头⽂文件。自定义函数(可以实现自己想要的逻辑)
如何实现:
ret_type fun_name(para1, * )
{
statement;// 语句句项
}
ret_type 返回类型
fun_name 函数名
para1 函数参
例如:写一个函数返回参数二进制中 1 的个数
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
//下面这块就是一个自定义函数,返回二进制参数1的个数
int count_one_bits(unsigned int value)
{
int count = 0;
while (value)
{
count++;
value = value & (value - 1);
}
return count;
}
int main()
{
int num = 0;
int ret = 0;
printf("input a number:\n");
scanf("%d", &num);
ret = count_one_bits(num);
printf("二进制中1的个数 = %d\n", ret);
system("pause");
return 0;
}
3.函数的参数
实际参数(实参)
真实传给函数的参数,叫实参。实参可以是:常量、变量、表达式、函数等。无论实参是何种类型的量,在进行函数调用时,它们都必须有确定的值,以便把这些值传送给形参。
上面程序中:num 就是一个实参形式参数(形参)
形式参数是指函数名后括号中的变量,因为形式参数只有在函数被调用的过程中才实例化(分配内存单元),所以叫形式参数。形式参数当函数调用完成之后就自动销毁了。因此形 式参数只在函数中有效。
上面程序中:value 就是一个形参
4.函数的调用
传值调用
数的形参和实参分别占有不同内存块,对形参的修改不会影响实参。
例:下面这个实现乘法口诀表的函数就是传值调用
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int Multiplication_table(int n)
{
int i, j, num;
for (i = 1; i < n+1; i++)
{
for (j = 1; j <= i; j++)
{
num = i*j;
if (i != j)
printf(" %d x %d = %d \ ", i, j, num);
else
printf(" %d x %d = %d \n", i, j, num);
}
}
}
int main()
{
int ret;
int n;
printf("input a number:");
scanf("%d", &n);
printf("\n 乘法口诀表\n\n");
ret = Multiplication_table(n);
printf(" \n");
system(" pause ");
return 0;
}
传址调用
传值调⽤是把函数外部创建变量的内存地址传递给函数参数的⼀种调⽤函数的方式。这种传参方式可以让函数和函数外边的变量建⽴起真正的联系,也就是函数内部可以直接操作函数外部的变量.
例:下面实现两个数的交换就是传址调用
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int exchange(int *m, int *n)
{
int temp;
temp = *m;
*m = *n;
*n = temp;
}
int main()
{
int a, b;
printf("input two numbers:");
scanf("%d %d", &a, &b);
printf(" a = %d b = %d\n", a, b);
int ret = exchange(&a, &b);
printf(" a = %d b = %d\n", a, b);
system("pause");
return 0;
}
嵌套调用
函数与函数之间可以有机组合的C语言中不允许作嵌套的函数定义。因此各函数之间是平行的,不存在上一级函数和下一级函数的问题,但是C语言允许在一个函数的定义中出现对另一个函数的调用,这样就出现了函数的嵌套调用,即在被调函数中又调用其它函数,这与其它语言的子程序嵌套的情形是类似的.
例:
#include <stdio.h>
void new_line()
{
printf("hehe\n");
}
void three_line()
{
new_line();
new_line();
new_line();
}
int main()
{
three_line();
return 0;
}
- 链式访问
例:
#include <stdio.h>
#include <string.h>
int main()
{
int ret = strlen(strcat("hello","bit"));
printf("%d\n", ret);
return 0;
}
5.函数的声明和定义
- 函数声明
- 告诉编译器有一个函数叫什么,参数是什么,返回类型是什么,但是具体是不存在,无关紧要。
- 函数的声明⼀般出现在函数的使用之前,要满足先声明后使用。
- 函数的声明⼀般要放在头文件中的。
test.h放置函数的声明
#ifndef __TEST_H__ #define __TEST_H__
// 函数的声明 int Add(int x, int y);
#endif //__TEST_H__
- 函数定义
函数的定义是指函数的具体实现,交待函数的功能实现。
test.c放置函数的实现
#include "test.h"
// 函数 Add 的实现
int Add(int x, int y)
{
return x+y;
}
**6.链接属性** 函数在使⽤的时候可能在不同的源文件中,每个源⽂件单独编译完成之后生成对应的目标文件,所有的目标文件以及那些从一个或者多个函数库中引用的函数,链接在一起形成了可执行程序,那相同的几个标识符出现在几个不同的源文件中,如何对待他们?标识符的链接属性决定了如何处理。
- 外部链接属性
全局标识符具有外部链接属性.
外部链接属性就意味着一个标识符,不仅可以在当前源文件使用,使用extern 的声明可以在其他源文件使⽤。
add.c源文件
int Add(int x, int y)//具有外部链接属性
{
return x+y;
}
int g_val = 2018; //具有外部链接属性
test.c源文件
#include <stdio.h>
extern int Add(int, int);
extern int g_val;
int main()
{
int a = 66;
int b = 99;
int ret = Add(a, b);
printf("g_val = %d\n", g_val);
return 0;
}
- 内部链接属性
具有外部链接属性的的标识符如果前面加上一个 static 关键字修饰就会变成内部链接属性
上面的代码做如下修改:
add.c源文件
static int Add(int x, int y)//具有内部链接属性
{
return x+y;
}
int g_val = 2018; //具有内部链接属性
test.c源文件
#include <stdio.h>
extern int Add(int, int);
extern int g_val;
int main()
{
int a = 66;
int b = 99;
int ret = Add(a, b);
printf("g_val = %d\n", g_val);
return 0;
}
此时这个代码编译就会报错:
无法解析的外部符号:_g_val;
无法解析的外部符号:_Add;
- 无属性
局部变量量是⽆无属性的。