C语言入门(七)函数

C语言函数及变量相关知识详解
本文围绕C语言展开,介绍了函数的概念、组成、分类、定义、调用方式等,如函数可分为标准函数和用户自定义函数,调用方式有函数语句、表达式、参数等。还阐述了形参和实参、返回值的规则,以及数组作参数的特点。此外,讲解了局部和全局变量的区别、存储方式和类别等知识。

函数:实现一个较大程序中的一个特定功能的每一个模块

一个C程序可由一个主函数和若干个子函数组成

主函数调用其他函数,其他函数可以互相调用,同一个函数可以被一个或多个函数任意调用多次

例子1

/*打招呼~*/
#include <stdio.h>

void main()
{
    void printstar();
    void print_message();

    printstar();
    print_message();
    printstar();
}

void printstar()
{
    printf("************\n");
}

void print_message()
{
    printf("Hey bro!\n");
}

1.一个C程序由一个或多个程序模块组成,每一个程序模块作为一个源程序文件。对于较大的程序,一般

不希望把所有内容全放在一个文件中,而是将他们分别放在若干个源文件中,再由若干源程序文件组成

一个C程序,这样便于分别编写、编译,从而提高调试效率。一个源程序文件可以作为多个C程序公用

2.一个源程序文件由一个或多个函数以及其他有关内容(如命令行、数据定义等)组成。一个源程序文

件是一个编译单位,在程序编译时是以源程序文件为单位进行编译的,而不是以函数为单位进行编译的

3.C程序执行从main函数开始

4.所有函数都是平行的,定义函数只能分别进行,不允许嵌套定义(除了main函数)

5.函数分为标准函数(即库函数,由系统提供)和用户自己定义的函数

6.函数从形式上可分为无参函数(无数据传递,一般用void)和有参函数两类

定义有参函数一般形式

类型标识符 函数名(形式参数表列)

{

        声明部分

        语句部分

}

形式参数:定义函数时,函数名后面括弧中的变量名称

实际参数:主函数在调用一个函数时,函数名后面括弧中的参数

在不同的函数间传递数据,可以采用参数、返回值和全局变量三种方法

例子2

/*输入两个数,输出较大的那个*/
#include <stdio.h>

void main()
{
	int a,b,c;
	int max(int x,int y); //形参

	printf("input two numbers:");
	scanf("%d,%d",&a,&b);
	c = max(a,b); //实参
	printf("the max is %d\n",c);
}

int max(x,y)
{
	int z;
	if(x >= y)
	{
		z = x;
	}
	else
	{
		z = y;
	}
	return z;
}

在VC++6.0中,编译scanf("%d,%d",&a,&b);时,在程序中输入两个数(中间加逗号),程序无法读取第二个数,当编译scanf("%d,%d",&a,&b);时,在程序中同样输入两个数(中间加逗号),程序可读取第二个数,注意此时%d旁边的逗号为中文逗号

形参和实参:

形参在被调用之前形参不占内存空间

实参可以是常量、变量或表达式,并要求有确定的值,在调用时会将实参的值赋给形参

实参与形参的类型基本保持一致,类型不同时遵从不同类型数值的赋值规则

函数的返回值:return语句(只能返回一个确定的值)

1.return后面的值可以是一个表达式

2.函数的返回值应当属于某一个确定的类型,在定义函数时指定函数返回值的类型,如double min(int x, int y),此语句函数返回值为双精度型

3.在C语言中,凡不加类型说明的函数,自动按整型处理

4.在定义函数时指定的函数类型一般应该和return语句中的表达式类型一致(如果不一致,则以函数类型为准)

5.对于不带返回值的函数,应当用"void"

例子3

/*返回值类型与函数类型不同,当前程序在输入实型数据时,会出现数据精确度丢失*/
#include <stdio.h>

int main()
{
    int max(float x, float y);
    float a, b;
    int c;
    scanf("%f%f",&a,&b);
    c = max(a,b);
    printf("max is %d\n",c);
}

int max(float x, float y)
{
    float z;      /*z为实型变量*/
    z = x>y ? x : y;
    return(z);
}

调用方法:1.老版C语言调用方法:_cdeal(自右向左) 2.常用调用方法(windows支持):_stdcall (自右向左)3.passcall?(自左向右)

例子4

/*实参求值的顺序:编译后结果为0,由此可得C语言采用的是_stdcall*/
#include <stdio.h>

void main()
{
    int f(int a, int b);  /*函数声明*/
    int i = 2, p;
    p = f(i, ++i);   /*函数调用*/
    printf("%d\n",p);
}

int f(int a, int b)   /*函数定义*/
{
    int c;
    if(a > b)
        c = 1;
    else if(a == b)
        c = 0;
    return(c);
}

按函数在程序中出现的位置来分,可以有以下三种函数调用方式:

1.函数语句(不要求函数返回值)

2.函数表达式(需要返回一个确定的值)

3.函数参数(把函数当作实参,如m = max(a, max(b, c));)

例子5

/*对被调用的函数作声明*/
#include <stdio.h>

void main()
{
    float add(float x, float y);  /*对被调用函数add的声明*/
    float a, b, c;
    scanf("%f,%f",&a,&b);
    c = add(a, b);
    printf("sum is %f\n",c);
}

float add(float x, float y)
{
    float z;
    z = x + y;
    return (z);
}

如果被调用的函数定义在主函数之前,可以不用声明该函数

例子6

/*验证pow()函数的功能*/
#include <math.h>
#include <stdio.h>

int main(void)
{
    double x = 2.0, y = 3.0;
    double z;
    z = pow(x, y);
    printf("%lf的%lf次方是%lf\n",x,y,z);
    return(0);
}

例子7

/*自己实现pow()函数并验证*/
#include <stdio.h>

void main()
{
    float power(float x, int y);     /*声明函数*/
    float a, c;
    int b;
    printf("请输入所求数以及所求次方:");
    scanf("%f%d",&a,&b);
    c = power(a, b);     /*调用函数*/
    printf("%0.3f的%d次方的值为%0.3f\n",a,b,c);
}

float power(float x, int y)     /*定义函数*/
{
    float z = 1.0;
    int i;
    for(i=0 ; i<y ; i++)
    {
        z = z*x;    
    }
    return (z);
}

例子8

/*自己实现sqrt()函数(只求整型数据)并完成验证*/
#include <stdio.h>

void main()
{
    int sqrt(int x);
    int a, b;
    printf("请输入被平方根数:");
    scanf("%d",&a);
    b = sqrt(a);
    if(b == -1)
    {
        printf("数字%d的平方根不是整数!\n",a);
    }
    else
    {    
        printf("数字%d的平方根是%d\n",a,b);
    }
}

int sqrt(int x)
{
    int y,z;
    for(y=0 ; y<=x ; y++)
    { 
        z = y*y;   
        if(z == x)
        { 
            return (y);   
            break;
        }        
    } 
    y = -1;
    return (y);       
}

例子9

/*编写一个用来统计输入的各个数字、空白符(空格、制表符和换行符)以及所有
其他字符出现次数的程序,分别存储在num[10],blank,others里边并打印出来*/
#include <stdio.h>

void main()
{
	char a[20];
	int num[10] = {0,0,0,0,0,0,0,0,0,0};
	int blank = 0, others = 0, i, j;
	for(i=0 ; i<20 ; i++)
	{
		scanf("%c",&a[i]);
	}
	for(i=0 ; i<20 ; i++)
	{
		switch(a[i])
		{
			case ('\t'):
				blank = blank + 1;
				continue;
			case ('\r'):
				blank = blank + 1;
				continue;
			case ('\0'):
				blank = blank + 1;
				continue;
			case ('0'):
				num[0] = num[0] + 1;
				continue;
			case ('1'):
				num[1] = num[1] + 1;
				continue;
			case ('2'):
				num[2] = num[2] + 1;
				continue;
			case ('3'):
				num[3] = num[3] + 1;
				continue;
			case ('4'):
				num[4] = num[4] + 1;
				continue;
			case ('5'):
				num[5] = num[5] + 1;
				continue;
			case ('6'):
				num[6] = num[6] + 1;
				continue;
			case ('7'):
				num[7] = num[7] + 1;
				continue;
			case ('8'):
				num[8] = num[8] + 1;
				continue;
			case ('9'):
				num[9] = num[9] + 1;
				continue;
			default:
				others = others + 1;
				continue;
		}
	}

	for(j=0 ; j<10 ; j++)
	{
		printf("该字符串中出现%d个数字%d\n",num[j],j);
	}
	printf("该字符串中出现%d个空白符\n",blank);
	printf("该字符串中出现%d个其他字符\n",others);
}

/*该程序识别空白符有误*/

函数的嵌套调用

函数的嵌套定义:在定义一个函数时,其函数体内又包含另一个函数的完整定义(C语言不能嵌套定义函数,但可以嵌套调用函数)

例子10

/*计算s = 2^2! + 3^2!*/
#include <stdio.h>

void main()
{
    int sqrt(int x, int y);
    int fact(int z);
    int a, i, b, j, c, d, s;

    printf("请输入第一个数和它的次方:");
    scanf("%d%d",&a,&i);
    printf("请输入第二个数和它的次方:");
    scanf("%d%d",&b,&j);

    c = fact(sqrt(a, i));
    d = fact(sqrt(b, j));
    s = c + d;

    printf("%d的%d次方的阶乘和%d的%d次方的阶乘的和为%d\n",a,i,b,j,s);
}

int sqrt(int x, int y)
{
    int p, z=1;
    for(p=0 ; p<y ;p++)
    {
        z = z*x;
    }
    return (z);
}

int fact(int z)
{
    int q, r=1;
    for(q=1 ; q<z+1 ; q++)
    {
        r = r*q;
    }
    return (r);
}

递归:在调用一个函数的过程中又出现直接或间接地调用该函数本身,称为函数的递归调用

例子11

/*使用递归调用实现阶乘*/

#include <stdio.h>

void main()
{
    int recursion(int x);
    int x, s;
    
    printf("请输入数字x:");
    scanf("%d",&x);

    s =  recursion(x);

    printf("数字%d的阶乘为%d\n",x,s);   
}

int recursion(int x)
{
    int ts;
    if(x<0)
    {
        ts = -1;
    }
    else if(x==0 || x==1)
    {
        ts = 1;
    }
    else
    {
        ts = recursion(x-1)*x;
    }
    return (ts);
}

递归算法是效率低下的算法,仅适用于只能使用递归算法完成的问题(如汉诺塔问题)

递归算法使用次数过多时,会出现数据的滞栈

例子12

/*汉诺塔问题*/
#include <stdio.h>

void main()
{
    int recursion(int x);
    int x, y;

    printf("请输入汉诺塔中需要移动的盘子数:");
    scanf("%d",&x);

    y = recursion(x);

    if(y == -1)
    {    
        printf("输入有误!\n");
    }
    else
    {    
        printf("%d个盘子需要移动%d次才能完成\n",x,y);
    }    
}

int recursion(int x)
{
    int z;
    if(x < 1)
    {    
        z = -1;
    }
    else if(x == 1)
    {
        z = 1;
    }
    else 
    {    
        z = 2*recursion(x-1)+1;
    }
    return (z);
}

数组可以作为函数的参数使用:一种是把数组元素(下标变量)作为实参使用,另一种是把数组名作为函数的形参和实参使用

用数组名作函数参数与用数组元素作实参有几点不同

1.对数组元素的处理是按普通变量对待的,用数组名作函数参数时,要求形参和实参都必须是类型相同的数组,都必须有明确的数组说明

2.在普通变量或下标变量作函数参数时,形参变量和实参变量是由编译系统分配的两个不同的内存单元,在函数调用时发生的值传送是把实参变量的值赋给形参变量;在用数组名作函数参数时,不进行值的传送,而是进行址的传送(存储数组第一个元素地址),实际上形参数组并不存在,编译系统并不为形参数组分配内存

例子13

/*数组作函数形参*/
#include <stdio.h>

//void test(int b[10]); //也可定义在主函数外
void main()
{
    int a[10] = {1,2,3,4,5,6,7,8,9,0};
	void test(int b[10]);
	test(a);

    putchar('\n');
}

void test(int b[10])
{
    int i = 0;
    for( ; i<10 ; i++)
    {    
        printf("%2d",b[i]);
    }
}

例子14

/*有一个一维数组score,内放10个学生的成绩,求平均成绩*/
#include <stdio.h>

void main()
{
    int average(int a[10]);  /*数组可以不用标注大小,主函数在调用时会自动将实参数组的地址赋给形参数组*/
    int score[10];
    int i, s;

    printf("请输入10个成绩(0~100):");
    for(i=0 ; i<10 ; i++)
    {
        scanf("%d",&score[i]);
    }

    s = average(score);

    printf("10位学生的平均成绩为%d\n",s);
}

int average(int a[10])
{
    int ave, j, x=0;
    for(j=0 ; j<10 ; j++)
    {
        x = x + a[j];
    }
    ave = x/10;
    return (ave);
}

局部变量:在函数内部定义,只在本函数内有效的变量,形参也属于局部变量

全局变量:在函数外部定义的变量,有效范围从定义变量的位置开始直至程序结束

例子15

/*有一个一维数组,里面存放了10位学生的成绩,求最高分,最低分和平均分*/
# include <stdio.h>

int max = 0, min = 0;

void main()
{
    int average(int a[]);
    int score[10];
    int ave, i;

    printf("请输入10位学生的成绩:");
    for (i=0 ; i<10 ; i++)
    {
        scanf("%d",&score[i]);
    }

    ave = average(score);
    
    printf("10位学生中最高分为%d,最低分为%d,平均分为%d\n",max,min,ave);
}

int average(int a[])
{
    int ave = a[0];
	int i;
    max = min =a[0];
    for (i=1 ; i<10 ; i++)
    {
        if(a[i] > max)
        {    
            max = a[i];
        }
        if(a[i] < min)
        {
            min = a[i];
        }
        ave+=a[i];
    }
	ave/=10;
    return (ave);
}

不建议经常使用全局变量

1.全局变量在程序的执行过程中都占用内存单元(局部变量存放在栈中)

2.使用全局变量过多,会降低程序的清晰性,在函数执行时可能会改变变量的值,程序容易出错

3.使函数的通用性和可移植性降低

根据时间可分为动态存储方式和静态存储方式

静态存储方式:在程序运行开始时由系统分配固定的存储空间的方式

动态存储方式:在程序运行期间根据需要进行动态分配存储空间的方式

用户存储空间包括程序区,静态存储区和动态存储区

变量的存储类别;
auto变量:函数中的局部变量,如不专门声明为static存储类别,都是动态分配存储空间的(栈),数据存储在动态存储区中,函数中的形参以及在函数中定义的变量都属于此类,在调用时分配内存,调用结束自动释放

static变量: 静态局部变量在函数内定义,不会因为调用结束而被释放,并在编译时赋初值且只赋值一次(虽然静态局部变量在函数调用结束后仍然存在,但其他函数不能引用它)

例子16

/*输出1到5的阶乘值*/
#include <stdio.h>

void main()
{
    int fact(int x);
    int x, i;

    printf("请输入数字x:");
    scanf("%d",&x);    

    for(i=1 ; i<x+1 ; i++)
    {
        printf("%d的阶乘为%d\n",i,fact(i));
    }
}

int fact(int x)
{
    static int y = 1; //定义静态局部变量y
    y = x * y; //y不被释放,值逐渐变大
    return (y);
}

register变量:对于需要经常被调用的局部变量,可存储于寄存器中,用关键字register作声明(运算速度变快,不建议定义过多)

extern声明外部变量:当外部变量定义在下方时,在函数内使用extern不会出错

进阶:在多个文件的程序中声明外部变量

可以将每一个函数做成一个文件,对于全局变量可用extern

关于变量的声明和定义:声明包括定义

内部函数和外部函数:根据函数能否被其他源文件调用来区分

内部函数:只能被本文件中其他函数引用

外部函数:最左端加extern(可省略)即为外部函数

例子17

/*有一个字符串,内有若干个字符,要求输入一个字符,程序便将字符串中的该字符删去*/

/*文件1*/
#include <stdio.h>

void main()
{
    void print(char a[]);
    char delete(char a[], char x);
    char a[20] = {'q','w','e','r','t','y','u','i','o','p','a','s','d','f','g','h','j','k','l','?'}; 
    char x;
    
    printf("请输入字符x:");
    scanf("%c",&x);
    
    print(a);
    a == delete(a, x);
    print(a);    
}



/*文件2*/
#include <stdio.h>

void print(char a[])
{
    int i = 0;
    for(i=0 ; i<20 ; i++)
    {
        printf("%c",a[i]);
    }
	printf("\n");
}



/*文件3*/
char delete(char a[], char x)
{
    int i;
    for(i=0 ; i<20 ;i++)
    {
        if(a[i] == x)
        {
            a[i] = '0';
        }
    }
    return (a);
}



/*更优解,定义外部函数enter_string*/
/*
#include <stdio.h>

void enter_string(char str[80])
{
    gets(str);
}
*/

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值