C语言
5.函数概述与引用
-
一个c语言程序由一个或多个程序模块组成,每一个程序模块作为一个源程序文件。对于较大的程序, 一般不希望把所有的内容全放在一个文件中, 而是分别放在若干个源文件中, 由若干个源程序组成一个c程序。
-
一个源程序文件由一个或多个函数以及其他有关内容(如指令, 数据声明与定义等)组成。
一个源程序文件是一个编译单位, 在程序编译时是以源程序文件为单位进行编译,而不是以函数为单位进行编译。
-
c程序的执行是从main函数开始的
-
所有函数都是平行的, 也就是说在定义时是分别进行的, 互相独立的。函数间能互相调用,但是不能调用main函数, main函数是由操作系统调用的。
-
在定义函数时要指定函数的类型
-
使用库函数时应该在本文件开头用#include指令将调用有关库函数时所需要用到的信息”包含“到本文件中来。
-
函数的声明和定义只差一个分号;
5.1函数形式
(1).有参数函数
#include <stdio.h>
int max(int x, int y)
{
/* 局部变量声明 */
if(x > y){
return (x);
//z=x>y?x:y;/* 函数返回两个数中较大的那个数 */
} else{
return (y);
}
}
int main(){
int max(int x, int y);//在调用时可以不写形参名,但参数类型必须写
int a, b, c;
printf("put in int:");
scanf("%d, %d, ",&a, &b);
c = max(a, b);
printf("max is %d\n", c);
return 0;
}
(2.1)
#include <stdio.h>
int main(){
int max4(int , int , int , int );
int a, b, c,d,max;
printf("please enter 4 interger numbers");
scanf("%d%d%d%d", &a,&b,&c,&d);
max = max4(a, b, c, d);
printf("max=%d\n", max);
}
int max4(int a, int b, int c, int d){
int max2(int , int );
int m;
m= max2(a, b);
m = max2(m, c);
m= max2(m,d);
return (m);
}
int max2(int a, int b){
if (a>=b)
return a;
else
return b;
}
(2).无参数函数:
# include <stdio.h>
int main(){
void print_star();
void print_message();
print_star();
print_message();
return 0;
}
//定义两个无参数函数
void print_star(){
printf("**********\n");
}
void print_message(){
printf("How do you do!\n");
}
5.2函数的递归调用
在调用函数的过程中直接或间接地调用该函数本身
函数不需要返回值时,则不需要return语句,此时函数的类型应该为void类型
# include "stdio.h"
int age(int n){
int c=0;
if (n==1)
c =10;
else
c = age(n -1)+2;
return (c);
}
int main(){
int age(int ); //对age函数声明
printf("NO.5,age:%d\n", age(5));
return 0;
}
(2)求递归n!
# include <stdio.h>
int main(){
int fac(int n);
int n;
int y;
printf("input an integer number:");
scanf("%d",&n);
y = fac(n);
printf("%d!=%d\n",n,y);
return 0;
}
int fac(int n ){
int f;
if(n<0)
printf("n<0,data error!");
else if(n==0||n==1)
f=1;
else f=fac(n-1)*n;
return (f);
}
(3)汉诺塔
# include "stdio.h"
int main(){
void hanoi(int n, char one, char two, char three);
int m;
printf("input the diskes:");
scanf("%d",&m);
printf("the step to move %d diskes\n",m);
hanoi(m,'A','B','C');
return 0;
}
void move(char x, char y){
printf("%c-->%c\n", x, y);
}
void hanoi(int n, char one, char two, char three){
void move(char x, char y);
if(n==1)
move(one, three);
else
{
hanoi(n-1,one,three,two);
move(one,three);
hanoi(n-1,two,one,three);
}
}
5.3数组作为函数参数
- 数组元素可以用作函数实参,但是不可用作形参(形参是在被调用时临时分配储存单元的)
- 数据传递是从实参到形参,单向传递
- 数组名可做实参和形参
//输入十个数,输出最大的数及其位置
# include <stdio.h>
int main(){
int max(int x,int y);
int a[10],m,n,i;
printf("enter 10 integer number:");
for(i=0;i<10;i++) //输入十个数给a[0]~a[9]
scanf("%d", &a[i]);
printf("\n");//不知有何意义???
for(i=1,m=a[0],n=0;i<10;i++)
{
if(max(m,a[i]>m)){
m= max(m,a[i]);
n=i;
}
}
printf("The largest number is%d\nit is the %dth number.\n",m,n+1);
return 0;
}
int max(int x,int y){
return (x>y? x:y);
}
(2)一维数组score内放十个数据,求平均值
#include "stdio.h"
int main(){
float average(float array[10]);
float score[10],aver;
int i;
printf("input 10 scores:\n");
for(i=0;i<10;i++)
scanf("%f",&score[i]);
printf("\n");
aver = average(score);
printf("average score is %5.2f\n",aver);
return 0;
}
float average(float array[10])//array为形参数组名
{
int i;
float aver,sum=array[0];
for(i=1;i<10;i++)
sum = sum+array[i];
aver=sum/10;
return (aver);
}
(3)3x4矩阵,求所有元素中的最大值
#include "stdio.h"
int main(){
int max_value(int array[][4]);
int a[3][4]={{1,3,4,5},{4,8,11,6},{13,53,3,15}};
printf("max value is %d\n", max_value(a));
return 0;
}
//函数的定义
int max_value(int array[][4]){
int i,j,max;
max=array[0][0];
for (i=0;i<3;i++)
for(j=0;j<4;j++)
if(array[i][j]>max)
max=array[i][j];
return max;
}
5.4变量储存方式和生存期
从变量的作用域来看:变量可分为全局变量和局部变量
从另一个角度来看,变量值存在的时间来观察——生存期
1.储存空间分为:
(1)程序区
(2)静态存储区:
在程序开始执行时给全局变量分配储存区,程序执行完毕就释放,
对于局部变量来说:它使变量由动态储存方式改变为静态储存方式
对于全局变量来说:使得变量局部化(仅限于本文件中)。
(3)动态储存区:
以下这些数据,在函数的调用时,分配动态储存空间,即函数结束时释放这些空间。
- 函数形式参数,在调用函数时给形参分配储存空间
- 函数中定义的没有用关键字static声明的变量,即自动变量
- 函数调用时的现场保护和返回地址
每个函数中的局部变量的生存期并不等于整个程序的执行周期,他只是程序执行周期的一个部分
2.数据类型和储存类型
(1)局部变量的储存类别
- 自动变量(auto变量):函数中不专门声明为static储存类别,都是动态地分配储存空间
- 静态局部变量(static):局部变量的值在调用结束后不消失二继续保持原值,即其占用的储存单元不释放。多次调用,相当于会覆盖掉自身局部变量的值。
eg:
#include "stdio.h"
int main(){
int f(int );
int a=2,i;
for(i=0;i<3;i++){
printf("%d\n",f(a));
printf("%d\n",f(a));
printf("%d\n",f(a));
//调用结束后,每一次调用c的值没消失,而是保存到程序的结束
return 0;
}
}
int f(int a){
auto int b=0;
static int c=3;
b=b+1;
c=c+1;
return a+b+c;
}
求阶乘
#include "stdio.h"
int main() {
int fac(int n);
int i;
for(i=1;i<=5;i++){
printf("%d!=%d\n",i,fac(i));
}
return 0;
}
int fac(int n){
static int f=1;
f=f*n;
return f;
}
3.寄存器变量(register变量)
使用频繁的变量将放在寄存器中,读取寄存器的速度要大于内存
但优化的编译系统能够识别使用频繁的变量,从而自动将这些变量放在寄存器中。
(2)全局变量的存储类别:
存放在静态存储中,生存期是固定的,存在与程序的整个运行过程,,但作用域是从什么位置到那个位置。有以下几种情况:
1.在一个文件内扩展外部变量的作用域
如果外部变量不在文件的开头定义,则其有效的作用范围只限定于定义处到文件结束。在定义点之前的函数不能引用该2外部变量。如果要用,则在引用之前用关键字extern对改变量作“外部变量声明”
#include "stdio.h"
int main() {
int max();
extern int A,B,C;
printf("input number:");
scanf("%d%d%d",&A,&B,&C);
printf("max is %d\n",max());
return 0;
}
//全局变量 A,B,C使用static后对main函数不起作用
int A,B,C;
int max(){
int m;
m=A>B?A:B;
if(C>m)m=C;
return m;
}
提倡将外部变量的定义放在引用它的函数之前。
2.将外部变量的作用域扩展到其他文件
一个c程序可以由一个或多个源程序文件组成。当两个或以上的文件都要用到同一个外部变量时,不能分别在两个文件中各自定义一个变量,这时需要使用extern对其做外部声明。
//file1.c
#include "stdio.h"
int A;
int main(){
int power(int);
int b=3,c,d,m;
printf("enter the number a and its power m:\n");
scanf("%d,%d",&A,&m);
c= A*b;
printf("%d*%d=%d\n",A,b,c);
d= power(m);
printf("%d**%d=%d\n",A,m,d);
return 0;
}
//file2.c
#include "stdio.h"
extern A;
int power(int n){
int i,y=1;
for(i=1;i<=n;i++){
y*=A;
}
return y;
}
3.将外部变量的作用域限制在文本中
使用static则可以将外部变量的作用域限制在本文件内。
这种只能用于本文件的外部变量称为静态外部变量。
小结:
1.声明变量时是在定义变量的基础上加上关键字,而不是单独使用。
2.对于一个数据的定义,需要指定两种属性:数据类型和存储类型
3.对于变量大的属性可以从两个方面:一是变量的作用域,二是变量存在时间的长短。前者从空间的角度,后者从时间的角度。
5.5关于变量的声明和定义
将建立储存空间的声明称为定义
把不需要建立储存空间的声明称为声明
函数中除了用extern声明的以外都是定义
5.6外部函数和内部函数
1.内部函数:
如果一个函数只能被本文件中其他函数所调用,它称为内部函数
static 类型名 函数名(形参表)
2.外部函数:
如果在定义函数时,在函数首部的最左端加关键字extern,则此函数是外部函数,可供其他函数调用。
但是c语言规定,如果在定义函数是省略extern,则默认为外部函数
//main
#include "stdio.h"
int main(){
extern void enter(char str[]);
extern void delete(char str[],char ch);
extern void print(char str[]);
char c,str[50];
enter(str);
scanf("%c",&c);
delete(str,c);
print(str);
return 0;
}
//f1.c
#include "stdio.h"
void enter(char str[50]){
gets(str);
}
//f2.c
#include "stdio.h"
void delete(char str[],char ch){
int i,j;
for(i=j=0;str[i]!='\0';i++)
if(str[i]!=ch)
str[j++]=str[i];
str[j]='\0';
}
//f3.c
#include "stdio.h"
void print(char str[]){
printf("%s\n",str);
}
6.指针
6.1数据如何储存,读取
存取一个数据,除了需要位置信息外,还需要该数据的类型信息。
即C语言中的地址包括位置信息和它所指向的数据的类型,或者说带:类型的地址。如:&a:“整形变量啊的地址”
- 直接访问:直接按变量名进行访问的方法
- 间接访问:将变量i的地址存放在另一个变量中,然后通过该变量来找到变量i的地址。
6.2指针变量
存放地址的是指针变量
*如何定义指针变量:类型名 指针变量名
*表示该变量是指针变量。
在赋值时,值是给指针变量名的,而不是,*指针变量名
例:
int a,b;
int *point_1 = &a,*point_2 = &b;
//a的地址给了point_1而不是*point_1
int a,b;
int *point_1,*point_2;
point_1 = &a;
point_2 = &b;
//a的地址给了point_1而不是*point_1
指针变量中只能存放地址
6.2.1引用指针变量
-
引用指针变量的值:printf(“%o”,p);
-
引用指针变量指向的变量:如果已经执行“p=&a;”,即变量指针p指向了整型变量a
& 取地址运算符。
*指针运算符,星号p代表指针变量p指向的对象;
// // Created by 20248 on 2021/7/25. // #include <stdio.h> int main() { int *p1,*p2,*p,a,b; printf("input 2 number:"); scanf("%d%d",&a,&b); p1 =&a; p2=&b; if(a<b) {p=p1;p1=p2;p2=p;} printf("a=%d,b=%d\n",a,b); printf("max=%d,min=%d\n",*p1,*p2); return 0; }
6.2.2指针变量作为函数参数
//
// Created by 20248 on 2021/7/25.
//
#include <stdio.h>
int main(){
void swap(int *p1,int *p2);
int a,b;
int *point1,*point2;
printf("input a and b:");
scanf("%d%d",&a,&b);
point1=&a;
point2=&b;
printf("%o\n%o\n",point1,point2);
if(a<b)
swap(point1,point2);
printf("%d%d",a,b);
printf("%o\n%o\n",point1,point2);
printf("max=%d,min=%d\n",a,b);
return 0;
}
void swap(int *p1,int *p2){
int temp;
temp=*p1;
*p1=*p2
*p2=temp;/*完成交换后,p1和p2的地址是不变的,但是 地址包含的值已变化
上一个例子,地址变化*/
}
/*
int *p;
p=p1;
p1=p2;
p2=p;
不能通过改变指针形参的值而使指针实参的值改变*/
6.3通过指针引用数组
数组中每个元素都需要在内存中战用储存单元。
所谓数组元素的指针就是数组元素的地址。
int c[10]={10个值};
int *p;
p=&a[0];//用一个指针指向一个数组元素。
//等价于:p=a;数组名代表数组中首元素的地址
//等价于:int *p=a;将a数组首元素的地址赋值给指针变量p
6.3.1指针的运算
在指针已指向一个数组元素时,可以做以下运算
加一个整数,(用+或者+=),如p+1;
减一个整数,(-或-=),如p-1;
自加运算,p++,++p;
自减运算,p–,--p;
两个指针相减,如p1-p2(只有都指向同一数组时才有意义)
通过数组名计算数组元素的地址:
#include "stdio.h"
int main(){
int a[10];
int i;
printf("input 10 int number:\n");
for(i=0;i<10;i++)
scanf("%d",&a[i]);
for(i=0;i<10;i++)
printf("%d",*(a+i));//a中序号为i的元素;
printf("\n");
return 0;
}
用指针变量指向数组元素
#include "stdio.h"
int main(){
int a[10];
int *p,i;
printf("input 10 int number:\n");
for(i=0;i<10;i++)
scanf("%d",&a[i]);
//for(p=a;p<(a+10);p++):a+10等价于&a[10]
// scanf("%d",p);用指针变量表示元素的地址
for(p=a;p<(a+10);p++)
printf("%d",*p);
//无法用数组名a变化的方法,因为数组名a代表数组首元素的地址,它是一个指针型常量,它的值在运行期间固定不变
printf("\n");
return 0;
}
错例子:
#include "stdio.h"
int main(){
int i,*p,a[5];
p=a;
printf("input 5 number:");
for(i=0;i<5;i++)
scanf("%d",p++);//此时p的指针指向的是5以后的地址,i此时也等于5!!!
//p=a;重新指向a[0]
for (i = 0; i <5;i++,p++)
printf("%d",*p);
printf("\n");
return 0;
}
6.3.2数组名作函数参数:
#include "stdio.h"
int main()
{
void inv(int *,int n);
int i,a[10]={1,3,4,5,1,5,7,8,4,2};
printf("the original array:\n");
for(i=0;i<10;i++)
printf("%d",a[i]);
printf("\n");
inv(a,10);
printf("the array has been:\n");
for(i=0;i<10;i++){
printf("%d",a[i]);
}
printf("\n");
return 0;
}
void inv (int *x,int n){
int *p,temp,*i,*j,m=(n-1)/2;
i=x;
j=x+n-1;
p=x+m;
for(;i<=p;i++,j--)
{
temp=*i;*i=*j;*j=temp;
}
return;
}