从Hello World 之C程序起步浅谈函数




        到目前为止,我已经接触过三种语言了:C 、C++ 、JAVA 、C# 。每一种语言的第一个程序都是 “Hello world!”。


【Hello World 之C程序起步】

#include <stdio.h>
int main(void)
{
        printf("Hello World! \n");
        return 0;
}

(1)main是主函数的函数名,表示这是一个主函数。
(2)每一个C源程序都必须有,且只能有一个主函数(main函数)。
(3)函数调用语句,printf函数的功能是把要输出的内容送到显示器去显示。
(4)printf函数是一个由系统定义的标准函数,可在程序中直接调用。

【Hello World 之C++程序起步】

#include <iostream>
using namespace std;
int main()
{
        cout << "Hello, world!" << endl;
        return 0;
}



【Hello World 之JAVA程序起步】

HelloWorld.java                        //文件名        
public class HelloWorld {            //声明一个类
                             //声明一个方法
            public static void main(String[] args){     //程序的入口
                    System.out.println("hello world!");    //输出        
            }

}

【Hello World 之C#程序起步】

using System;
 
class HelloWorldApp
{
        public static void Main()
        {
                Console.WriteLine("Hello world!");
        }
}


--------------------------------------------------------------------------------------------------------------------------------------------------


        此处只谈嵌入式编程方向的(Linux环境下的)C/C++。
        初识“Hello world”后就有了程序的基本概念`:源程序是由函数组成的。所以我们接下来需要了解函数的基本概念:

一、 函数的分类
         1. 从函数定义的角度看,函数可分为库函数和用户自定义函数两种。
             (1)库函数
            由C系统提供,用户无须定义, 也不必在程序中作类型说明,只需在程序前包含有该函数原型的头文件即可在程序中直接调用。
             (2)用户定义函数
            由用户按需要写的函数。对于用户自定义函数, 不仅要在程序中定义函数本身, 而且在主调函数模块中还必须对该被调函数进行类型说明,然后才能使用。
         2. C语言的函数兼有其它语言中的函数和过程两种功能,从这个角度看,又可把函数分为有返回值函数和无返回值函数两种。
              (1)有返回值函数
             此类函数被调用执行完后将向调用者返回一个执行结果, 称为函数返回值。如数学函数即属于此类函数。 由用户定义的这种要返回函数值的函数,必须在函数定义和函数说明中明确返回值的类型。
              (2)无返回值函数
             此类函数用于完成某项特定的处理任务, 执行完成后不向调用者返回函数值。这类函数类似于其它语言的过程。 由于函数无须返回值,用户在定义此类函数时可指定它的返回为“空类型”, 空类型的说明符为“void”。
         3. 从主调函数和被调函数之间数据传送的角度看又可分为无参函数和有参函数两种。
              (1)无参函数
             函数定义、函数说明及函数调用中均不带参数。 主调函数和被调函数之间不进行参数传送。 此类函数通常用来完成一组指定的功能,可以返回或不返回函数值。
             (2)有参函数
             也称为带参函数。在函数定义及函数说明时都有参数, 称为形式参数(简称为形参)。在函数调用时也必须给出参数, 称为实际参数(简称为实参)。 进行函数调用时,主调函数将把实参的值传送给形参,供被调函数使用。


二、 函数定义的基本形式


         1. 无参函数的一般形式

类型说明符 函数名()
{
      函数体;
}

无参函数例子
#include <stdio.h>
void pr1()
{
        printf(“Hello World!”);
}
int main()
{
        pr1();
        return 0;
}

       2. 有参函数的一般形式

类型说明符 函数名(形式参数表)
{
        函数体;
}

有参函数例子

#include <stdio.h>

void pr1(int i,int j )

{
        printf(“Hello,%d,%d”,i,j);
}
int main()
{
        int a=5,b=2;
        pr1(a,b);
       return 0;
}

         tip:
              [1]有参时,发生函数调用,此时把a的值传递给i,把b的值传递给j。这种形式的函数传递称为值传递。i,j称为形式参数,a,b称为实际参数。

              [2]函数名只需遵循普通变量名规则即可。



三、 函数的返回值
         发生函数调用时,允许把实参的值传递给形参。在函数定义时,我们还发现有函数类型说明符;允许函数有返回值。
         1. 无返回值函数   
         2. 有返回值函数

程序一:
#include <stdio.h>
void add1(int a, int b)
{
      printf(“%d”,a+b);
}
int main()
{
      add1(5,2);
      return 0;
}
        tip:从这个例子可以看出,实参可以是变量,也可以是表达式,或者是最直接的值。目的都是把实参的值传递给自定义函数中的形参。

程序二:
#include  <stdio.h>
int add2(int a,int b)
{
        int sum;
        sum=a+b;
        return sum;
}
int main()
{
        int c;
        c=add2(5,2);
        printf(“%d”,c);
        return 0;
}
        tip:在程序二中,add2(5,2)发生了函数调用,此时把5传给a变量,2传给了b变量,然后完成求和的计算,最后把7作为函数值返回,返回到调用点,此时相当于c=7;然后最后输出。

程序三:
#include <stdio.h>
int add3(int a,int b)
{  
        return a+b;
}
int main()
{
        printf(“%d”,add3(5,2));
        return 0;
}
        tip:在程序三中只是对程序二进行了化简,最后printf(“%d”,add3(5,2))完成函数调用后相当于执行了printf(“%d”,7);

注意:

        (1) 函数的值只能通过return语句返回主调函数。
              return 语句的一般形式为:
                              return 表达式;
                              或者为:return (表达式);

             该语句的功能是计算表达式的值,并返回给主调函数。 在函数中允许有多个return语句,但每次调用只能有一个return 语句被执行, 因此只能返回一个函数值。

例如:
#include <stdio.h>
int add4(int a,int b,int c)
{
        if(c==1)
                return a+b;
        else if(c==0)
                return a-b;
        else
               return 0;
}
int main()
{
        printf(“%d”,add3(5,2,1));
        return 0;
}
        tip:add4()函数内有三个return语句,但只能执行其中的一条。

小贴士:
[1]函数的类型、返回值的类型必须保持一致,如本例中都是int类型。
[2]调用后,因为返回的是int类型,所以输出这个返回值时,也必须是%d输出。
[3]或在执行c=add2(5,2);时,必须用一个int类型的变量来获取这个返回值。即c变量应该是int c。

          (2) 函数值的类型和函数定义中函数的类型应保持一致。
          (3) 不返回函数值的函数,可以明确定义为“空类型”, 类型说明符为“void”。


四、 其他几点说明


         C语言中,可以用以下几种方式调用函数:

         1. 函数表达式
       函数作表达式中的一项出现在表达式中,以函数返回值参与表达式的运算。这种方式要求函数是有返回值的。例如: z=add(x,y)是一个赋值表达式,把max的返回值赋予变量z。

         2. 函数语句
       函数调用的一般形式加上分号即构成函数语句。例如: printf ("%D",a);scanf ("%d",&b);都是以函数语句的方式调用函数。

         3. 函数实参
       函数作为另一个函数调用的实际参数出现。 这种情况是把该函数的返回值作为实参进行传送,因此要求该函数必须是有返回值的。例如: printf("%d",max(x,y)); 即是把max调用的返回值又作为printf函数的实参来使用的。

            tip:函数的参数分为形参和实参两种,形参出现在函数定义中,实参出现在函数调用中,发生函数调用时,将把实参的值传送给形参。 函数的值是指函数的返回值,它是在函数中由return语句返回的。


五、 函数实例


         1. 输出50行hello world

程序一:
#include <stdio.h>
int main()
{
        int i;
        for(i=0;i<50;i++)
                printf(“Hello World!\n”);
        return 0;
}

程序二:
#include <stdio.h>
void he1()
{
        printf(“Hello World!\n”);
}
int main()
{
        int i;
        for(i=0;i<50;i++)
                he1();
        return 0;
}

程序三:
#include  <stdio.h>
void he2()
{
        int i;
        for(i=0;i<50;i++)
                printf(“Hello World!\n”);
}
int main()
{
        he2();
        return 0;
}

         2. 素数程序:输入一个正整数,判断其是否是素数。

程序一:
#include  <stdio.h>
int main()
{
        int n,i;
        do
        {
                scanf("%d",&n);
        }while(n<0);
        for(i=2;i<n;i++)
                if(n%i==0)
                        break;
        if(i<n)  
                printf("No,%d 不是一个素数",n);
        else    
                printf("Yes,%d 是一个素数",n);

        return 0;
}

程序二:
#include <stdio.h>
void su1(int x)
{
        int i;
        for(i=2;i<x;i++)
                if(x%i==0)
                        break;
        if(i<x)  
                printf("No,%d 不是一个素数",n);
        else    
                printf("Yes,%d 是一个素数",n);
}
int main()
{
        int n;
        do
        {
                scanf("%d",&n);
        }while(n<0);
        su1(n);

        return 0;
}

程序三:
#include <stdio.h>
int su2(int x)
{
        int i;
        for(i=2;i<x;i++)
                if(x%i==0)
                        break;
        if(i<x)  
                return 0;
        else    
                return 1;
}
int main()
{
        int n;
        do
       {
                scanf("%d",&n);
        }while(n<0);
        if(su2(n))  
                printf("Yes,%d 是一个素数",n);
        else
                printf("No,%d 不是一个素数",n);

        return 0;
}

程序四:
#include <stdio.h>
int su3(int x)
{
        int i;
        for(i=2;i<x;i++)
               if(x%i==0)
                       break;
        return x-i;
}
int main()
{
        int n;
        do
        {
                scanf("%d",&n);
        }while(n<0);
        if(su3(n)>0)
                printf("No,%d 不是一个素数",n);
        else
                printf("Yes,%d 是一个素数",n);

        return 0;
}

        tip:写成函数的形式的好处是如果需要判断输入的两个数或者多个数是否是素数时,函数可以重复的调用。

例如:

#include<stdio.h>
int su3(int x)
{
        int i;
        for(i=2;i<x;i++)
                if(x%i==0)
                        break;
        return x-i;
}
int main()
{
        int n,m;
        do
       {
                scanf("%d",&n);
        }while(n<0);
        if(su3(n)>0)
                printf("No,%d 不是一个素数",n);
        else
                printf("Yes,%d 是一个素数",n);
        do
       {
                scanf("%d",&m);
        }while(m<0);
        if(su3(m))
                printf("No,%d 不是一个素数",n);
        else
                printf("Yes,%d 是一个素数",n);

        return 0;
}


----------------------------------------------------------------------------------------------------------------------------------------------------

    接下来我们再对函数进行一个比较深入的分析:


一、 变量的作用域

         对于有多个函数的程序而言,每个函数都可以定义属于自己的变量,那么就需要探讨一下这些变量的作用域(即作用范围)。
         在讨论函数的形参变量时曾经提到, 形参变量只在被调用期间才分配内存单元,调用结束立即释放。 这一点表明形参变量只有在函数内才是有效的, 离开该函数就不能再使用了。这种变量有效性的范围称变量的作用域。不仅对于形参变量, C语言中所有的量都有自己的作用域。变量说明的方式不同,其作用域也不同。 C语言中的变量,按作用域范围可分为两种, 即局部变量和全局变量。

         1. 局部变量

             局部变量也称为内部变量。局部变量是在函数内作定义说明的。其作用域仅限于函数内, 离开该函数后再使用这种变量是非法的。

例如:
#include<stdio.h>
void pr1(int i,int j )
{
        printf(“Hello,%d,%d”,i,j);
}
int main()
{
        int a=5,b=2;
        pr1(a,b);

        return 0;
}

        解析:pr1()函数定义了形式参数,同时也属于pr1函数的变量i,j,则此时i,j的作用域就仅仅属于pr1这个函数的范围。
同理, main()函数内也定义了a,b两个变量,这两个变量的作用域也仅仅局限于main函数内使用。
                   即main不能调用i的值,如
                                         printf(“%d”,i); 这样的语句是有问题的。

        理论延伸一下,当不同的函数使用相同的变量名时,作用域的范围是一样的。

#include"stdio.h"
int add(int a,int b)
{
        a=a+3;
        b=b-5;
        printf("a=%d,b=%d\n",a,b);
        return a+b;
}
int main()
{
        int a,b=2;
        a=5;
        printf("a=%d,b=%d\n",a,b);
        printf("a+b=%d\n",add(a,b));
        printf("a=%d,b=%d",a,b);

        return 0;
}
程序结果:
a=5,b=2
a=8,b=-3
a+b=5
a=5,b=2

             [1]第一次输出是main函数中的printf语句,此时输出刚开始的main函数中的a,b的值;
             [2]然后调用add函数,第二次输出是add函数中的a,b的值,然后返回,第三个输出是main函数的第二个printf语句,输出函数调用后的结果a+b;
             [3]最后输出main函数的第三个printf语句。

             tip:虽然add函数中,a,b变量的值发生了改变,但并不影响main中的a,b的值。

        2. 全局变量

            如果把变量定义在函数的外面,则它属于全局变量,全局变量也称为外部变量,它是在函数外部定义的变量。 它不属于哪一个函数,它属于一个源程序文件。其作用域是整个源程序。

例如:
#include"stdio.h"
int sum;
void add2(int a,int b)
{
        sum=a+b;
}
int main()
{
        int a=5,b=2;
        add2(a,b);
        printf("a+b=%d",sum);   

        return 0;
}

        tip:sum 变量是定义在add2和main函数的外面,则它的作用域是从定义开始到程序最后。

程序结果:a+b=7

        小贴士:当全局变量名和局部变量名相同时,要特别注意。

例如:
#include"stdio.h"
int sum;
void add2(int a,int b)
{
        int sum;
        sum=a+b;
}
int main()
{
        int a=5,b=2;
        sum=0;
        add2(a,b);
        printf("a+b=%d",sum);  

        return 0;
}

程序结果:a+b=0

tip:
[1]第二行的sum是一个全局变量
[2]add2中的sum是一个局部变量。此时add2中的sum是指这个局部变量。
[3]全局变量先赋初值为0。
[4]调用无返回值函数,把局部变量的sum变为了7,但是全局变量的sum不变,故最后输出的sum是全局变量的值。


二、 用数组名作为实参(引用传递、地址传递)

         回顾一下,值传递是指把实参的值传递给形参,而形参的值的改变并不同时能改变实参。

例如:
#include"stdio.h"
int add(int x,int y)
{
        x=x+3;
        y=y-5;
        printf("x=%d,y=%d\n",x,y);
        return x+y;
}
int main()
{
        int a,b=2;
        a=5;
        printf("a=%d,b=%d\n",a,b);
        printf("a+b=%d\n",add(a,b));
        printf("a=%d,b=%d",a,b);
 
        return 0;
}

程序结果:
a=5,b=2
x=8,y=-3
a+b=5
a=5,b=2

        解析:此例属于值传递,发生函数调用时add(a,b)把实参a的值(5)传递给形参x,此时x=5;把实参b的值(2)传递给形参y,此时y=2,然后运行add函数体,此时x,y的值发生了改变,但这种改变不会影响到main函数的a,b实参的值。


        那么,下面的实例中又会是什么结果呢?

#include"stdio.h"
void change(int x,int y)
{
        int temp;
        printf("@2:x=%d,y=%d\n",x,y);
        temp=x;
        x=y;
        y=temp;
        printf("@3:x=%d,y=%d\n",x,y);
}
int main()
{
        int a[6]={3,2,9,6,11,4},x,y;
        x=a[0];
        y=a[1];
        printf("@1:x=%d,y=%d\n",x,y);
        change(x,y);
        printf("@4:x=%d,y=%d\n",x,y);   

        return 0;
}

输出结果:
@1:x=3,y=2
@2:x=3,y=2
@3:x=2,y=3
@4:x=3,y=2

为什么呢?
[1]第一次@1直接输出main函数中的x,y值。即@1:x=3,y=2
[2]发生函数调用,把main中的x值传递给change中的x,即x=3; 把main中的y值传递给change中的y,即y=2;第一次@2直接输出change函数中的x,y的初值。即@2:x=3,y=2
[3]change函数的作用是把x和y的值互换,然后输出互换后的结果。即@3:x=2,y=3
[4]函数返回后,执行main函数最后的printf,此时要注意,x和y的值是指main中的x和y。即@4:x=3,y=2


        重点:上面的例子都是属于值传递类型。当传递的实参变为数组名时,情况就不一样了。

例如:
#include"stdio.h"
float aver(float a[5])
{
        int i;
        float av,s=a[0];
        for(i=1;i<5;i++)
                s=s+a[i];
        av=s/5.0;

        return av;
}
int main()
{
        float sco[5],av;
        int i;
        printf("\ninput 5 scores:\n");
        for(i=0;i<5;i++)
                scanf("%f",&sco[i]);
        av=aver(sco);
        printf("average score is %5.2f",av);

        return 0;
}

        解析:程序从main函数开始运行,先读入了5个实数到sco数组中,然后发生函数调用av=aver(sco);把sco作为实参传递给了a[5],此时a数组跟sco数组是一样的,而aver的功能是运算这个数组的平均分,返回这个平均分给av变量,最后输出。

        值得注意的是:aver函数只是利用了a数组或者是sco数组的值,并没有改变任何数组中的数据。

#include"stdio.h"
float aver(float a[],int n)
{
        int i;
        float av,s=a[0];
        for(i=1;i<n;i++)
                s=s+a[i];
        av=s/n;
        return av;
}
int main()
{
        float sco[5],av;
        int i;
        printf("\ninput 5 scores:\n");
        for(i=0;i<5;i++)
                scanf("%f",&sco[i]);
        av=aver(sco,5);
        printf("average score is %5.2f",av);

        return 0;
}

        解析:这个程序跟上一个程序最大的不同在于函数调用的参数上,上一个程序的调用只有一个实参,而这个程序有两个实参,分别是数组名和一个数字,这个数字的作用是传递这个数组的空间大小。

        同样值得注意的是:aver函数只是利用了a数组或者是sco数组的值,并没有改变任何数组中的数据。


要注意的是下面的一个例子:

#include"stdio.h"
void aver(float a[],int n)
{
        int i;
        for(i=0;i<5;i++)
                printf("%5.1f ",a[i]);
}
int main()
{
        float sco[5]={1.2,3,4.6,5,9.6};
        int i;
        for(i=0;i<5;i++)
                printf("%5.1f ",sco[i]);
        printf("\n");
        aver(sco,3);

        return 0;
}

#include"stdio.h"
void aver(float a[],int n)
{
        int i;
        for(i=0;i<5;i++)
                printf("%5.1f ",a[i]);
}
int main()
{
        float sco[5]={1.2,3,4.6,5,9.6};
        int i;
                for(i=0;i<5;i++)
        printf("%5.1f ",sco[i]);
        printf("\n");
        aver(sco,5);

        return 0;
}
 
        解析:上述两个程序的结果是一样的,都是输出数组的结果,主要的区别是第二个实参的值。从这个例子可以看出,一旦把数组名传递过去,则整个数组都会全部传递过去,而不仅仅是数组的一部分。

        这样的数组名传递会产生新的问题,一旦在函数中把数组的内容发生的改变,此时会不会对原来的main数组产生影响呢?

例如:
#include"stdio.h"
void aver(float a[],int n)
{
        int i;
        float temp;
        temp=a[0];
        a[0]=a[1];
        a[1]=temp;
        for(i=0;i<5;i++)
                printf("%5.1f ",a[i]);
}
int main()
{
        float sco[5]={1.2,3,4.6,5,9.6};
        int i;
        for(i=0;i<5;i++)
                printf("%5.1f ",sco[i]);
        printf("\n");
        aver(sco,3);
        printf("\n");
        for(i=0;i<5;i++)
                printf("%5.1f ",sco[i]);

        return 0;
}

        解析:先输出第一次main函数的初始数组的值,然后换行,发生函数调用,把sco数组以数组名传递的形式传递给了a数组,aver函数主要把a数组中的a[0]和a[1]的值进行了互换,此时输出互换后的a数组的全部值,然后函数返回。
        最后把sco数组的值输出,此时要注意的是,sco的数组值sco[0]和sco[1]也发生了变化。也就是说,aver函数中数组a的值的变化影响到了main函数的sco数组,这是跟我们之前讲的值传递是不一样的。

输出结果:
1.2  3.0   4.6  5.0  9.6
3.0  1.2   4.6  5.0  9.6
3.0  1.2   4.6  5.0  9.6


三、 函数的嵌套调用

#include"stdio.h"
int fx2(int x)
{
        return x*x;
}
int fx1(int n)
{
        return 2*n+fx2(n-1)+1;
}
int main()
{
        printf("%d",fx1(3));
        return 0;
}

        解析:在main函数中调用fx1,在fx1函数中调用fx2,转向执行fx2。


四、 函数的递归调用

         已知1!=1, 2!=1*2, 3!=3*2!  依次类推
                  n!=n*(n-1)!   当n>=2时
                  n!=1        当n=1时

#include"stdio.h"
int digui1(int n)
{
        if(n>1)
                return n*digui1(n-1);
        else
                return 1;
}
int main()
{
        int n;
        do
        {
                printf("input n>0:n=");
                scanf("%d",&n);
         }while(n<0);
        printf("%d",digui1(n));

        return 0;
}

输入5
输出120


五、 实例分析

         1. 求岁数。

                有10个同学围坐在一起,有人问10号同学多少岁,10号说比9号大2岁,9号又说比8号大两岁,以此类推,1号同学说他今年12岁,请问10号同学多少岁?
                即f(x)=2+f(x-1) x>=2
                    f(x)=1  x=1

#include "stdio.h"
int digui2(int n)
{
        if(n>1)
                return 2+digui2(n-1);
        else
                return 12;
}
int main()
{
        printf("%d",digui2(10));
        return 0;
}

 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值