想知道什么是函数,首先给出函数的定义

函数是C语言的基础模块,是以完成某种目的为目标的语句的集合
函数有较强的独立性,可以相互调用。当我们需要使用函数内相关的功能时;只需要调用函数就可以完成功能,能大大减少冗余重复语句的使用,增强代码的可读性。

举个栗子:
假如你是公司的老板,有一项任务很麻烦,但你每天都要做;在公司资金充足的情况下,我相信大部分人会选择雇一个员工专门做这件事,这样的话,每天你的任务就从做这件事变成了呼叫这个员工来做这件事;那么,这个员工的名字我们就可以理解为函数
↑栗子结束↑
函数一般分为两种:库函数自定义函数,库函数是c语言自带的函数,比如对于字符串的strcmp()等,而自定义函数是我们自己定义的函数,也就是我们下面主要要说的内容;

那么,理解了函数是什么,现在我们可以学着来写函数了;
大家学c语言第一次helloworld的时候,我相信大家都见到过下面

int main() {
	printf("helloworld");
	return 0;
}

这样的语句;我们就来拿它举一个栗子
(来,把目光聚过来)
这个函数除了printf之外,我们大体可以把它分为几个部分,分别是

  1. int
  2. main
  3. ()
  4. return 0;(这一点在第一部分讲解)
  5. {}
    我们接下来就简单来讲讲每个部分的意义都是什么,都是做什么用的

1.int(函数类型)

int的含义是整形,在这里作为函数的类型被使用,常用的类型还有 short、int、long、char、float、double(c++中还有bool类型),它们也都可以作为函数的类型使用。除了这几种之外还有一种类型是void类型;我们按照函数是否是void类型(是否有返回值)可以把函数分为两种:即
1.void — 无返回值类型
2.不是void的情况 — 有返回值类型
函数的return部分和函数的类型是一对需要注意的地方,如果函数的类型是void,通常不需要返回值(后面会详细讲返回值的问题),也就是说如果把main函数改成这个样子也是可以的

void main() {
	printf("helloworld");
}

没有return 是合法的~

但是在有返回值的情况下,要注意一点,返回值类型要与函数类型一致(敲黑板)
但是在有返回值的情况下,要注意一点,返回值类型要与函数类型一致(敲黑板)
但是在有返回值的情况下,要注意一点,返回值类型要与函数类型一致(敲黑板)
(重要的事情说三遍)

就像int main函数里面的return 0;正因为main函数的类型是int,0是int值;所以return的值可以是0;当然,别的值应该也可以(比如1),但是我们一般用0表示正常退出,因此main函数一般都return 0;
别的类型的函数也是一样的,比如float需要return一个小数,char需要return一个字符;bool需要return一个true或者false;只要保持一致就可以进行return;
在其他函数中,return的值代表函数的值;比如说我写了一个int add()函数,在函数里面写了return 5;那么如果在主函数里面写这样的代码

printf("%d",add())

的时候,console中就会打印一个5出来,即会打印出add函数的返回值

2.main(函数名)

可以字母,数字,下划线混合使用,要求如下:

  1. 只能以字母或下划线开始;
  2. 不能以数字开始;
  3. 一般小写;
  4. 关键字不允许用(比如说int float 之类的为保留字不允许用);
    但我们一般推荐函数名的命名规则为:
    1.见名知意;
    2.自定义函数函数名首字母尽量大写(库函数里的函数名都是以小写字母定义,为了区分库函数和自定义函数,避免冲突)。

3.()

在()内,按是否有参数分为两种函数;一般可以分为有参类型和无参类型
有参类型函数 指的是在该函数被调用的时候,主调函数通过参数向里面传递了数据。
无参类型函数 很好理解,就是不向函数内部传递数据,因为没有参数,无法获得返回值。

主要介绍下有参类型;用简单的代码来举个例子

函数数据类型 函数名字(参数1, 参数2, 参数3... , 参数n)
{
   调用语句块;
}

对于每一个参数而言,根据自己的需求,如果想传递数据类型中的整数类型,那么就需要把参数的类型定义成整数型(int,short,long)中的一个;其他数据类型也是同理。
在我们自定义的函数里面,括号里面的参数叫做形式参数(形参),而在调用函数里面,括号后面的参数叫做实际参数(实参),它可以是常量,变量,表达式
举例而言:

add_1 = add(12, 43);        //12,43都是常量
add_2 = add(n1 + n2, 4 * 5);  //n1 n2都是变量,进行加法操作是一个表达式

在调用函数的时候,我们需要记住以下几点:
1:在调用的时候,调用函数向参数传递的是值,在调用结束后,该值不会改变
2:函数只有被调用,系统才会给它分配内存。
3:调用结束后,系统给他分配的内存单元会立即被释放,但调用函数中的值不会改变
4:在主调函数把值传递给被调用函数后,被调用的函数里面可以直接使用这个值,但是要记住,值的数据类型要和参数的数据类型一一对应

下面详细解释下:
在调用函数的时候,从形参到实参发生的是值传递;也就是把实参的值拷贝一份副本给形参,在形参里面改变值并不会反向影响实参的值,也就是说这里的值是单向传递的:
实参 -> 形参
举例:

int Redefine(int n) {
	n = 100;
}

int main() {
	int n = 50;
	Redefine(n);
	printf("%d",n);
	return 0;
}

在上面这个程序中,我在Redefine中改变了n的值,但是在printf的地方输出的仍然是n = 50;
是因为Redefine函数仅仅生成了一份n的副本,尽管在Redefine的内部产生了100这个n的值,但这个100仅在Redefine函数内部生效

而关于传入值的数据类型要和参数的数据类型一一对应这一问题,比如对于上面的函数Redefine,如果我向里面传入一个浮点类型的数double n = 10.5,在函数内部这个数由于形参是int型会变成10,也就是产生了数据截断(损失)的问题。

当然,我们也可以选择在参数的地方传入地址而不是值,这里引入地址传递这一概念
简单来讲:
传值就是直接用一个变量去存储那个值
传地址就是用一个变量(指针变量)去记住那个地址,再通过计算机查找地址来获得这个地址对应的值。
这里我们再引入一个小知识点:
1.数组名的含义,是一个指针变量,也就是第一个元素的地址,我们一般称之为首地址;它的类型取决于数组内部元素的类型;如果元素都是int类型,那么数组名的类型就是
指向int的常量指针”;

为什么说它是常量,是因为它在被定义的时候系统已经给它分配好了内存地址,也就是说,已经是无法更改的常量了。
2.如果你要在函数中,要改变调用函数中某个变量的值,那么就要把想传的变量对应的地址传递给形参,
如果不需要改变,直接传递值的拷贝即可。
对于传值和传址,就要提到一个经典的易错问题:交换两个参数的值的问题
大家来看下下面这段代码

void SwapValue(int a, int b)
{
	int t;
	t = a;
	a = b;
	b = t;
}
int main() {
	int a=5,b=9;
	SwapValue(a,b);
	printf("a=%d,b=%d",a,b);
	return 0;
}

大家觉得输出结果会是什么呢?

我们一般可能会觉得是在swap中a和b的值进行交换了,也就是a=9,b=5这样的输出
但其实不然,输出结果仍然是a = 5,b = 9(大家可以自己敲一下代码试一下)
之所以这样,是因为我仅仅只是把 a = 5, b = 9 这个值给拷贝进了SwapValue的形参中,并不影响调用函数中的变量的值。
但如果我们想做到真正的交换两个变量的值,我们就需要使用指针变量来传入地址
代码如下:

void swap(int *a,int *b) {
	int t = *a;
	*a = *b;
	*b = t;
}

int main() {
	int a = 5,b = 9;
	cout <<a<<b<<endl;//这句话和printf(“%d,%d”,a,b)等价,是c++里面的语法,作者懒得写了
	swap(a,b);
	cout <<a<<b<<endl;
	return 0;
}

执行了这样的代码之后,我们就可以清晰的看到两个变量的值发生了改变(大家可以把代码复制进编译器试一下),这就是地址和值传递的区别;

5.{}

这个很好理解,“{”就是函数开始的地方,“}”是函数结束的地方,每一对{}都代表了一个函数,在中间部分对于要执行的语句不要忘记加缩进~

<时间比较匆忙,如有错误或缺少的知识点的地方请指出>

更多推荐

C语言入门基础知识-函数(新手入门详细讲解)