从零开始学编程---第一步-C语言(十九)

时间:2014-03-07 20:57    点击:

现在我们学个新内容,函数
函数这个名词大家应该在数学里接触过,觉得很头疼吧
但C语言里的函数和数学里的函数不同,函数只是个方法
在英文里函数的单词是function 也是功能,方法的意思
说白了函数就是一个具体的处理过程及方法
数学里的函数是数与数之间的一一对应关系即映射,需要人工去计算才能得到结果,而编程里的函数则是把数学中的某一问题的处理过程及方法进行具体化,也就是如何实现的问题。
函数是为了实现某个功能而做的具体过程
如果觉得晕,就这样理解,函数就是实现一个过程的方法 函数=方法

其实我们已接触过 main() 就是主函数
程序的一切全部在这个函数里运行,它是唯一更不能缺少
从函数定义的角度,可分为库函数和自定义函数
库函数就是c语言自带的,比如printf() scanf()等
自定义函数才是我们要学的

函数的定义
void hello()
{
     printf("hello world\n");
    
}

这是定义了一个无返回值的名为hello的函数

关于返回值
每个函数要不有返回值要不没返回值,反正是二者之一
main()主函数可以有返回值也可以没有

#include <stdio.h>

int main()
{
   printf("hello world\n");
return 0;
   
}

这个main()主函数就是有返回值的,返回什么呢,返回0
一般返回0,0就可以省略 
int main()
{
   printf("hello world\n");

   return ;
   
}
甚至也可以省略return
int main()
{
   printf("hello world\n");   
}

其实int也是可以省略的,这个后面慢慢说
main()
{
   printf("hello world\n");   
}

这个是我们常用的mian

当然没返回值也是可以的
#include <stdio.h>

void main()
{
   printf("hello world\n");

}

当然自定义函数就不像主函数这样,该返回就返回,不该返回就不返回


注意,如果main 主函数前不加viod ,后面又不给return, 程序会给警告加个返回值,不过事情不大的,随你便拉
main()
{
printf("hello world");
}

这样是偷懒人的做法


正规的写法是
int main()
{
.......
return 0;
}

void main()
{
.....
}

学了定义函数后,现在该去调用使用它了

函数于函数之间可以互相调用,但main()主函数不能被任何函数调用

main()主函数就相当于控制中心,控制和使用所有的函数



这是我们之前定义的函数

void hello()
{
      printf("hello world\n");
    
}

现在我们在主函数调用它

#include <stdio.h>

void hello()
{
     printf("hello world\n");
}


main()
{
   hello();

}



只要在主函数写上函数名就可以拉

这样就可以实现打印hello world的功能了

但是如果把函数hello放在main主函数后面,那就会报错
这就需要main主函数先声明hello函数在调用了
声明函数需要把它的函数类型也声明出来,hello函数是无返回值类型的函数
所以声明就是 void hello()
程序如下
#include <stdio.h>

void main()
{
     void hello();
     hello();

}

void hello()
{
     printf("hello world\n");
}


函数可以放到程序里任何地方,放到main()之前 main主函数就可以直接调用
放到main之后就需要先声明在调用
反正main主函数不是放到程序开头,就是放在程序结尾,看你习惯了
放到中间是笨蛋的做法


下面我们定义一个有参数的函数,啥叫参数,参数,也叫参变量,是一个变量。
接下来会慢慢理解
函数类型其实和变量类型一样,也有int float char等,既然有变量类型,那函数自己本身就有值
只有是有参数,有类型的函数,那它就会有返回值,返回给函数值

int max(int a,int b)
{
   
     if(a>b)
         return a;
     else
         return b;
}
这是我们定义的一个int类型的函数,现在知道小括号的作用了吧,就是放参数用的
int max(int a,int b)可不能这样写int max(int a,b)
有时候,参数的类型不一定和函数的类型一样
这个函数的作用你应该看的出来,是比较两个数的大小,下面我们来调用

#include <stdio.h>

void main()
{
     int max(int a,int b);
     int a;
     a=max(7,9);
     printf("%d",a);

}

int max(int a,int b)
{
   
     if(a>b)
         return a;
     else
         return b;
}


你会发现这个程序定义过两次变量a,一个是参数,一个是变量,参数是接收传递的一个变量
如果一个函数定义了参数变量,你就不能在这个函数里面在定义一个和参数名字相同的变量
反之,函数有自己的独立性,我这个函数里面定义一个变量x,另外一个函数里面也可以定义一定变量x,类型相同也没问题,这就是局部变量,以后会说
函数声明要把参数也写出来,等于就是复制一下定义的函数,然后多个分号

下面额外说说定义和声明
定义可以理解为建立,建立一个变量或函数
声明的作用就是通知编译系统,告诉它我将要用这个某个变量或函数
如果被调函数在主调函数之前,编译系统已经知道了这个函数了,所以在调用的时候可以不声明(当然你声明也不会有错)


现在说说函数之前的数据传递
void main()
{
     int max(int a,int b);
     int a;
     a=max(7,9);
     printf("%d",a);

}

max(7,9); 7和9是我自己写进去的
此时的7传递到了max函数参数int a ,9传递到了参数int b
然后max函数把接收到的值给它里面的内容
让它里面的代码执行做数据的操作
比大小,结果出来了,得到9大,在值返回给max,然后max得到结果,主调函数从中获取结果

主调函数就像老板,被调函数就是一个部门来实现某个任务,老板下达命令给份文件给这个部分的负责人(被调函数)处理,这个负责人给他下面的人(代码)处理,下面的人得到结果反馈给负责人,老板就可以从这个负责人获取到结果
函数本身是有值的,所以它可以进行赋值操作
a=max(7,9);

这样也可以哦printf("%d",max(7,9));

另外,也可以定义变量来传递数据给函数

#include <stdio.h>

int max(int a,int b)
{
   
     if(a>b)
         return a;
     else
         return b;
}



void main()
{
     int max(int a,int b);

     int a,b,c;
     puts("请输入两个数");
     scanf("%d,%d",&a,&b);
     c=max(a,b);
     printf("最大的数为%d\n",c);

}


这里又有形参和实参的概念
形参就是函数小括号里的参数
int max(int a,int b);
变量a,b就是形参,它们只是个int类型的模型,等着接收int类型的数据
只有在接收到数据时(被调用)才分配内存单元,调用结束则释放内存
因此,形参只在函数内部里使用
而int a,b,c;
这3个变量就是实参,实实在在的数据,要真正处理的变量数据
当然,实参的变量名不需要和形参的变量名一样,这里定义int x,y,z一样
实参和形参的数量,顺序,数据类型必须一致

形参的作用主要是用来接收实参的数据的
你可以不写形参,而把本来的形参作为普通变量直接定义到函数里
但这样做没什么意义,这个函数基本就没什么使用价值了

#include <stdio.h>

int max()
{
    int a=7,int b=9;
     if(a>b)
         return a;
     else
         return b;
}



main()
{
   printf("%d",max());
}


这样max()函数基本就废了

另外,就算形参的值发生变化,实参的值不会有任何变化
下面看程序
#include <stdio.h>

void x(int a)
{
  
    printf("这是实参的值:%d\n",a);
    a=1;
    printf("这是形参的值:%d\n",a);
}

main()
{

     int a;
     puts("输入一个数");
     scanf("%d",&a);
     x(a);
    puts("------");
     printf("主函数的实参值依然为:%d\n",a);

}

输入一个值存入变量a(实参)
然后调用函数x,把a的值传递给它
x函数获取实参值,给里面的代码执行
首先打印a实参的值,然后修改a=1,此时实参的值并没变化,只是形参的值发生了变化
可以这样理解
实参传递的值相当于复制传递,自己本身的值不变,只是复制的那一分给函数

尽量不要让函数名和形参名一样,还有主函数里定义的变量不要和被调函数名一样
比如这个程序
#include <stdio.h>

int a(int a)
{
  
    return a;
}

main()
{

     int a;
     a(a);

}


会报错,把主函数变量名改了或者被调函数改了就可以了
因为程序会将变量a看作是函数,这样一来在参数中填入一个函数名,自然报错了

下面我们用函数编写一个很普通的例题,计算园的周长和面积
我们需要写两个函数,一个计算周长一个计算面积
程序如下:
#include <stdio.h>

main()
{
    float size(int r);
    float area(int r);
    int r;       //半径
    puts("请输入园的半径");
    scanf("%d",&r);
    printf("园的周长为:%.2f\n",size(r));
    printf("园的面积为:%.2f\n",area(r));

}
float size(int r)    //计算园的周长
{
   
     float l=r*2*3.14;
     return l;
}
float area(int r)    //计算园的面积
{
     float m=r*r*3.14;
     return m;

}

我们这里设半径为int型,所以函数名和形参的数据类型可以不一致
但是,函数名是什么数据类型,返回值的变量的数据类型就必须跟函数名的数据类型一致

这个程序还可以这样写
#include <stdio.h>

main()
{
    float size(int r);
    float area(int r);
    int r;       //半径
    puts("请输入园的半径");
    scanf("%d",&r);
    printf("园的周长为:%.2f\n",size(r));
    printf("园的面积为:%.2f\n",area(r));

}
float size(int r)    //计算园的周长
{
   
     return r*2*3.14;

}
float area(int r)    //计算园的面积
{
     return r*r*3.14;
    

}

或者
#include <stdio.h>

main()
{
    void size(int r);
    void area(int r);
    int r;       //半径
    puts("请输入园的半径");
    scanf("%d",&r);
    size(r);
    area(r);

}
void size(int r)    //计算园的周长
{
   
     float l=r*2*3.14;
     printf("园的周长为:%.2f\n",l);
    
}
void area(int r)    //计算园的面积
{
     float m=r*r*3.14;
     printf("园的面积为:%.2f\n",m);

}


另外说一点,函数类型是如果不写也不会报错,但是如果你不写,系统始终默认这个函数类型为int型,下面这个程序就很好的说明了这一点

#include <stdio.h>

a()
{
     int x=1;
     return x;
}
b()
{
     float y=3.14;
     return y;
}
c()
{
     char z='a';
     return z;
}

main()
{
     printf("%d,%.2f,%c",a(),b(),c());
}

3个函数都省略了数据类型,但最终能成功打印出来的就是int型
可见,如果你定义的函数为int型,你是可以省略前面这个int的
但是最好不要这样做,如果程序很大,你总有一次会为了你省略了nt类型而后悔
所以,什么类型的函数都该定义好,不该偷懒的不要偷,这样程序会清晰简单

下面来说说函数里的参数带数组的问题
数组可以作为函数的参数使用,进行数据传送。数组用作函数参数有两种形式,一种是把数组元素(下标变量)作为实参使用;另一种是把数组名作为函数的形参和实参使用。
数组元素就是下标变量,它与普通变量并无区别。 因此它作为函数实参使用与普通变量是完全相同的,在发生函数调用时,把作为实参的数组元素的值传送给形参,实现单向的值传送。

下面说第一种,很好理解,因为数组元素就是变量,跟我们上面说的实参传形参没什么两样,只是多个循环而已
这个程序很简单
#include <stdio.h>

main()
{
     void a(int);
     int i[5];
     puts("输入5个数");
     for(int x=0;x<=4;x++)
     {
         scanf("%d",&i[x]);
         a(i[x]);
     }
    
}
void a(int i)
{
     if(i>0)
         printf("%d\n",i);
     else
         printf("%d\n",0);
}

输入5个数,每个数的元素依次传递给函数a的形参i
如果某个数大于0就输出本身,小于或等于0就输出0

注意开头部分做了省略
本来是
void a(int i)
可以省略
void a(int)

如int max(int a,int b)可以省略为int max(int,int);

还有注意,有返回值的函数函数自己本身带值,调用它的结果只会得到一个值
无返回值的函数不带值,调用它的结果会得到这个函数自己本身代码执行出来的结果(可能是值可能不是,例如打印就不是的)

第二种数组名作为函数的形参 比第一种有点复杂

数组名作为函数形参,意味着形参不再是一个变量,而是一个数组

如int a(int i[5])

下面一个例题,求5门成绩的平均分
#include <stdio.h>

float avg(float a[5])
{
     float av,s=0; 
     for(int i=0;i<5;i++) 
     s=s+a[i];
     av=s/5;
     return av;
}
void main()
{
     float score[5],av;
     printf("请输入5门成绩:\n");
     for(int i=0;i<5;i++)
     scanf("%f",&score[i]);
     av=avg(score);
     printf("平均分为 %5.2f",av);
}

首先输入5个数,注意这里是输完才调用函数传递的
这里av=avg(score);为什么不是av=avg(score[i]);
这个要学习了指针才明白,score是数组名,数组名就是数组的首地址,这里的数组参数传递只是这个数组首地址的传递,也就是传递给形参的只是数组第一个元素的首地址,得到数组首地址就等于得到了整个数组元素,不明白这里等以后学指针就慢慢明白了

前面说了形参改变值实参并不改变,但数组名作为参数就不同了
看程序
#include <stdio.h>

void a(int i[5])
{
     i[0]=1;
     i[1]=2;
     i[2]=3;
     i[3]=4;
     i[4]=5;
}
void main()
{
     int i[5];
     printf("请输入5个数:\n");
     for(int x=0;x<5;x++)
     scanf("%d",&i[x]);
     a(i);
     for(x=0;x<5;x++)
     printf("实参i[%d]为:%d\n",x,i[x]);
}

这里实参的数组传递给形参的数组,但形参数组的值改变,实参数组的值会跟着改变
最后实参的值变为1,2,3,4,5
可以理解为实参和形参用的同一内存的数组,可以说它们共享同一数组

在变量作函数参数时,所进行的值传送是单向的。即只能从实参传向形参,不能从形参传回实参。形参的初值和实参相同,而形参的值发生改变后,实参并不变化,两者的终值是不同的。而当用数组名作函数参数时,情况则不同。由于实际上形参和实参为同一数组,因此当形参数组发生变化时,实参数组也随之变化。当然这种情况不能理解为发生了“双向”的值传递。但从实际情况来看,调用函数之后实参数组的值将由于形参数组值的变化而变化

下面引用复制的一段话
用数组名作函数参数与用数组元素作实参有几点不同:
1)     用数组元素作实参时,只要数组类型和函数的形参变量的类型一致,那么作为下标变量的数组元素的类型也和函数形参变量的类型是一致的。因此,并不要求函数的形参也是下标变量。换句话说,对数组元素的处理是按普通变量对待的。用数组名作函数参数时,则要求形参和相对应的实参都必须是类型相同的数组,都必须有明确的数组说明。当形参和实参二者不一致时,即会发生错误。
2)     在普通变量或下标变量作函数参数时,形参变量和实参变量是由编译系统分配的两个不同的内存单元。在函数调用时发生的值传送是把实参变量的值赋予形参变量。在用数组名作函数参数时,不是进行值的传送,即不是把实参数组的每一个元素的值都赋予形参数组的各个元素。因为实际上形参数组并不存在,编译系统不为形参数组分配内存。那么,数据的传送是如何实现的呢?在我们曾介绍过,数组名就是数组的首地址。因此在数组名作函数参数时所进行的传送只是地址的传送,也就是说把实参数组的首地址赋予形参数组名。形参数组名取得该首地址之后,也就等于有了实在的数组。实际上是形参数组和实参数组为同一数组,共同拥有一段内存空间。


另外形参数组和实参数组的长度可以不相同,因为在调用时,只传送首地址而不检查形参数组的长度,不过形参数组的长度要大于实参数组的长度,不能比它小
例如上面float avg(float a[5])改成float avg(float a[10])也没问题

但是当形参数组的长度与实参数组不一致时,虽不至于出现语法错误(编译能通过),但程序执行结果可能将与实际不符,这是应予以注意的,所以长度最好一致

当然也允许形参数组不给长度
像这样float avg(float a[])
也可以用一个变量来表示数组元素的个数
float avg(float a[],int n)

上面这个怎么用
在调用的时候加长度
avg(score,5);

来源:幻想编程//所属分类:站长原创/更新时间:2014-03-07 20:57
顶一下
(9)
81.8%
踩一下
(2)
18.2%
上一篇:从零开始学编程---第一步-C语言(十八)
下一篇:从零开始学编程---第一步-C语言(二十)
相关内容