函数:实现一个较大程序中的一个特定功能的每一个模块
一个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);
}
*/