文章目录
- 笔记
- 函数
- 参数
- 函数调用
- 返回值
- 函数的嵌套
- 函数的递归
- 数组作为函数参数
- 多维数组名作函数参数
- 局部变量和全局变量
- 全局变量
- 变量的存储方式和生存期
- 自动变量(auto变量)
- static静态局部变量
- 寄存器变量(register变量)
- 全局变量
- 在一个文件内扩展外部变量的作用域
- 将外部变量的作用域扩展到其他文件---extern
- 将外部变量的作用域限制在本文件中---static
- 存储类别小结
- 作用域角度
- 变量存在的时间(生存期)
- 变量值存放的位置
- 内部函数和外部函数
- 内部函数
- 外部函数
- 例题
- 例题 2
- 例题 3
- 例题 4
- 例题 5
- 例题 6
- 例题 7
- 例题 8
- 例题 9
- 例题 10
- 例题 11
- 例题 12
- 例题 13
- 例题 14
- 例题 15
- 例题 16
- 例题 17
- 例题 18
- 例题 19
- 例题 20
- 习题
- 习题1
- 习题 2
- 习题 3
- 习题 4
- 习题 5
- 习题 6
- 习题 7
- 习题 8 作业
- 上课更新
- 习题 9 作业
- 作业
- 习题 10
- 习题 11
- 习题 12
- 习题 13
- 习题 14
- 习题 15 作业
- 指针数组
- 结构体
- 习题 16 作业
- 习题 17
- 习题 18
笔记
函数
一个源程序文件由一个或多个函数以及其他有关内容(如指令、数据声明与定义等)组成。
一个源程序文件是一个编译单位,在程序编译时是以源程序文件为单位进行编译的,而不是以函数为单位进行编译的。
函数不能嵌套定义。函数间可以互相调用,但不能调用main函数。main函数是被操作系统调用的。
参数
从函数的形式看,函数分两类。
① 无参函数。在调用无参函数时,主调函数不向被调用函数传递数据。
② 有参函数。主调函数在调用被调用函数时,通过参数向被调用函数传递数据。
形参可以与已有变量重名!
实参与形参的类型应相同或赋值兼容。赋值兼容是指实参与形参类型不同时能按不同类型数值的赋值规则进行转换。
函数调用
在定义函数中指定的形参,在未出现函数调用时,它们并不占内存中的存储单元。在发生函数调用时,函数的形参才被临时分配内存单元。
将实参的值传递给对应形参。
在执行函数期间,由于形参已经有值,就可以利用形参进行有关的运算。
通过return语句将函数值带回到主调函数。应当注意返回值的类型与函数类型一致。如果函数不需要返回值,则不需要return语句。这时函数的类型应定义为void类型。
(5) 调用结束,形参单元被释放。注意: 实参单元仍保留并维持原值,没有改变。如果在执行一个被调用函数时,形参的值发生改变,不会改变主调函数的实参的值。因为实参与形参是两个不同的存储单元。
!!!!注意
实参向形参的数据传递是“值传递”,单向传递,只能由实参传给形参,而不能由形参传给实参。实参和形参在内存中占有不同的存储单元,实参无法得到形参的值。
返回值
如果函数值的类型和return语句中表达式的值不一致,则以函数类型为准。
对数值型数据,可以自动进行类型转换。即函数类型决定返回值的类型。
(4) 对于不带回值的函数,应当用定义函数为“void类型”(或称“空类型”)
。这样,系统就保证不使函数带回任何值,即禁止在调用函数中使用被调用函数的返回值。此时在函数体中也没有return语句。
函数的嵌套
C语言的函数定义是互相平行、独立的,也就是说,在定义函数时,一个函数内不能再定义另一个函数,即不能嵌套定义,但可以嵌套调用函数,即在调用一个函数的过程中,又调用另一个函数。
函数的递归
在调用一个函数的过程中又出现直接或间接地调用该函数本身,称为函数的递归调用。
数组作为函数参数
数组元素可以用作函数实参,但是不能用作形参。因为形参是在函数被调用时临时分配存储单元的,不可能为一个数组元素单独分配存储单元(数组是一个整体,在内存中占连续的一段存储单元)。
在用数组元素作函数实参时,把实参的值传给形参,是“值传递”方式。数据传递的方向是从实参传到形参,单向传递。
用数组元素作实参时,向形参变量传递的是数组元素的值,而用数组名作函数实参时,向形参(数组名或指针变量) 传递的是数组首元素的地址。
多维数组名作函数参数
可以用多维数组名作为函数的实参和形参,在被调用函数中对形参数组定义时可以指定每一维的大小,也可以省略第一维的大小说明。
int array[3][10]; 或 int array[][10]; //二者等价
在第2维大小相同的前提下,形参数组的第1维可以与实参数组不同。
例如,实参数组定义为int score[5][10];
而形参数组定义为int array[ ][10];
或int array[8][10];
均可以。这时形参数组和实参数组都是由相同类型和大小的一维数组组成的。C语言编译系统不检查第一维的大小。
局部变量和全局变量
定义变量可能有3种情况:
(1) 在函数的开头定义;
(2) 在函数内的复合语句内定义;
(3) 在函数的外部定义。
- (1) 主函数中定义的变量也只在主函数中有效。主函数也不能使用其他函数中定义的变量。
- (2) 不同函数中可以使用同名的变量,它们代表不同的对象,互不干扰。
- (3) 形式参数也是局部变量。只在定义它的函数中有效。其他函数中不能直接引用形参。
- (4) 在一个函数内部,可以在复合语句中定义变量,这些变量只在本复合语句中有效,这种复合语句也称为“分程序”或“程序块”。
全局变量
在函数内定义的变量是局部变量,在函数外定义的变量是全局变量。
变量的存储方式和生存期
变量的存储有两种不同的方式: 静态存储方式和动态存储方
式。
- 静态存储方式是指在程序运行期间由系统分配固定的存储空间的方式。
- 动态存储方式则是在程序运行期间根据需要进行动态的分配存储空间的方式–malloc
在动态存储区中存放以下数据:
-
① 函数形式参数。在调用函数时给形参分配存储空间。
-
② 函数中定义的没有用关键字static声明的变量,即自动变量。
-
③ 函数调用时的现场保护和返回地址等。
对以上这些数据,在函数调用开始时分配动态存储空间,函数结束时释放这些空间。在程序执行过程中,这种分配和释放是动态的,如果在一个程序中两次调用同一函数,而在此函数中定义了局部变量,在两次调用时分配给这些局部变量的存储空间的地址可能是不相同的。
如果一个程序中包含若干个函数,每个函数中的局部变量的生存期并不等于整个程序的执行周期,它只是程序执行周期的一部分。在程序执行过程中,先后调用各个函数,此时会动态地分配和释放存储空间。
C的存储类别包括4种
自动的(auto)、静态的(statis)、寄存器的(register)、外部的(extern)
根据变量的存储类别,可以知道变量的作用域和生存期。
自动变量(auto变量)
函数中的局部变量,如果不专门声明为static(静态)存储类别,都是动态地分配存储空间的,数据存储在动态存储区中。函数中的形参和在函数中定义的局部变量(包括在复合语句中定义的局部变量),都属于此类。
在调用该函数时,系统会给这些变量分配存储空间,在函数调用结束时就自动释放这些存储空间。因此这类局部变量称为自动变量。自动变量用关键字auto作存储类别的声明。
关键字auto可以省略,不写auto则隐含指定为“自动存储类别”,它属于动态存储方式。程序中大多数变量属于自动变量。
对自动变量赋初值,不是在编译时进行的,而是在函数调用时进行的,每调用一次函数重新给一次初值,相当于执行一次赋值语句。
对自动变量来说,它的值是一个不确定的值。这是由于每次函数调用结束后存储单元已释放,下次调用时又重新另分配存储单元,而所分配的单元中的内容是不可知的。
auto int b,c=3; //等价于int b,c=3;
static静态局部变量
有时希望函数中的局部变量的值在函数调用结束后不消失而继续保留原值,即其占用的存储单元不释放,在下一次再调用该函数时,该变量已有值(就是上一次函数调用结束时的值)。这时就应该指定该局部变量为“静态局部变量”,用关键字static进行声明。
参考例题 17
对静态局部变量是在编译时赋初值的,即只赋初值一次,在程序运行时它已有初值。以后每次调用函数时不再重新赋初值而只是保留上次函数调用结束时的值。
如果在定义局部变量时不赋初值的话,则对静态局部变量来说,编译时自动赋初值0(对数值型变量)或空字符′\0′(对字符变量)。
虽然静态局部变量在函数调用结束后仍然存在,但其他函数是不能引用它的。因为它是局部变量,只能被本函数引用,而不能被其他函数引用。
对静态局部变量是在编译时赋初值的,即只赋初值一次,在程序运行时它已有初值。以后每次调用函数时不再重新赋初值而只是保留上次函数调用结束时的值。而对自动变量赋初值,不是在编译时进行的,而是在函数调用时进行的,每调用一次函数重新给一次初值,相当于执行一次赋值语句。
寄存器变量(register变量)
如果有一些变量使用频繁(例如,在一个函数中执行10 000次循环,每次循环中都要引用某局部变量),则为存取变量的值要花费不少时间。为提高执行效率,允许将局部变量的值放在CPU中的寄存器中,需要用时直接从寄存器取出参加运算,不必再到内存中去存取。由于对寄存器的存取速度远高于对内存的存取速度,因此这样做可以提高执行效率。这种变量叫做寄存器变量,用关键字register作声明。如
register int f; //定义f为寄存器变量
3种局部变量的存储位置是不同的: 自动变量存储在动态存储区;静态局部变量存储在静态存储区;寄存器存储在CPU中的寄存器中。
全局变量
全局变量都是存放在静态存储区中的。因此它们的生存期是固定的,存在于程序的整个运行过程。
一般来说,外部变量是在函数的外部定义的全局变量,它的作用域是从变量的定义处开始,到本程序文件的末尾。在此作用域内,全局变量可以为程序中各个函数所引用。但有时程序设计人员希望能扩展外部变量的作用域。
在一个文件内扩展外部变量的作用域
如果外部变量不在文件的开头定义,其有效的作用范围只限于定义处到文件结束。 在定义点之前的函数不能引用该外部变量。
如果由于某种考虑,在定义点之前的函数需要引用该外部变量,则应该在引用之前用关键字extern对该变量作“外部变量声明”,表示把该外部变量的作用域扩展到此位置。有了此声明,就可以从“声明”处起,合法地使用该外部变量。
例题 18参考
用extern声明外部变量时,类型名可以写也可以省写。例如,“extern int A,B,C;”也可以写成“extern A,B,C;”。因为它不是定义变量,可以不指定类型,只须写出外部变量名即可
将外部变量的作用域扩展到其他文件—extern
如果一个程序包含两个文件,在两个文件中都要用到同一个外部变量Num,不能分别在两个文件中各自定义一个外部变量Num,否则在进行程序的连接时会出现“重复定义”的错误。正确的做法是: 在任一个文件中定义外部变量Num,而在另一文件中用extern对Num作“外部变量声明”,即“extern Num; ”。
在编译和连接时,系统会由此知道Num有“外部链接”,可以从别处找到已定义的外部变量Num,并将在另一文件中定义的外部变量Num的作用域扩展到本文件,在本文件中可以合法地引用外部变量Num。
将外部变量的作用域限制在本文件中—static
有时在程序设计中希望某些外部变量只限于被本文件引用,而不能被其他文件引用。这时可以在定义外部变量时加一个static声明。
这种加上static声明、只能用于本文件的外部变量称为静态外部变量。在程序设计中,常由若干人分别完成各个模块,各人可以独立地在其设计的文件中使用相同的外部变量名而互不相干。只须在每个文件中定义外部变量时加上static即可。这就为程序的模块化、通用性提供方便。
如果已确认其他文件不需要引用本文件的外部变量,就可以对本文件中的外部变量都加上static,成为静态外部变量,以免被其他文件误用。至于在各文件中在函数内定义的局部变量,本来就不能被函数外引用,更不能被其他文件引用,因此是安全的。
!!!!!!!!!!!!!
不要误认为对外部变量加static声明后才采取静态存储方式(存放在静态存储区中),而不加static的是采取动态存储(存放在动态存储区)
声明局部变量的存储类型和声明全局变量的存储类型的含义是不同的。
!!!!!!!!!!!!!
对于局部变量来说,声明存储类型的作用是指定变量存储的区域(静态存储区或动态存储区)以及由此产生的生存期的问题
而对于全局变量来说,由于都是在编译时分配内存的,都存放在静态存储区,声明存储类型的作用是变量作用域的扩展问题。
用static声明一个变量的作用是
- (1) 对局部变量用static声明,把它分配在静态存储区,该变量在整个程序执行期间不释放,其所分配的空间始终存在。
- (2) 对全局变量用static声明,则该变量的作用域只限于本文件模块(即被声明的文件中)。
用auto,register和static声明变量时,是在定义变量的基础上加上这些关键字,而不能单独使用。
存储类别小结
作用域角度
从作用域角度分,有局部变量和全局变量。它们采用的存储类别如下:
变量存在的时间(生存期)
从变量存在的时间(生存期)来区分,有动态存储和静态存储两种类型。静态存储是程序整个运行时间都存在,而动态存储则是在调用函数时临时分配单元。
变量值存放的位置
内部函数和外部函数
根据函数能否被其他源文件调用,将函数区分为内部函数和外部函数
内部函数
static 类型名 函数名(形参表);
内部函数又称静态函数,因为它是用static声明的。使用内部函数,可以使函数的作用域只局限于所在文件。这样,在不同的文件中即使有同名的内部函数,也互不干扰,不必担心所用函数是否会与其他文件模块中的函数同名。
通常把只能由本文件使用的函数和外部变量放在文件的开头,前面都冠以static使之局部化,其他文件不能引用。这就提高了程序的可靠性。
外部函数
extern int fun(int a,int b)
//表示fun可以被其他文件调用
在需要调用此函数的其他文件中,需要对此函数作声明(不要忘记,即使在本文件中调用一个函数,也要用函数原型进行声明)。在对此函数作声明时,要加关键字extern,表示该函数“是在其他文件中定义的外部函数”。
使用extern声明就能够在本文件中调用在其他文件中定义的函数,或者说把该函数的作用域扩展到本文件。extern声明的形式就是在函数原型基础上加关键字extern。
由于函数在本质上是外部的,在程序中经常要调用其他文件中的外部函数,为方便编程,C语言允许在声明函数时省写extern。
用函数原型能够把函数的作用域扩展到定义该函数的文件之外(不必使用extern)。只要在使用该函数的每一个文件中包含该函数的函数原型即可。函数原型通知编译系统: 该函数在本文件中稍后定义,或在另一文件中定义。
利用函数原型扩展函数作用域最常见的例子是#include指令的应用。在#include指令所指定的“头文件”中包含调用库函数时所需的信息。
例题
例题 2
输入两个整数,要求输出其中值较大者。要求用函数来找到大数
#include <stdio.h>
int main()
{ int max(int x,int y); //对max函数的声明int a,b,c;printf("please enter two integer numbers:"); //提示输入数据scanf("%d,%d",&a,&b); //输入两个整数c=max(a,b); //调用max函数,有两个实参。大数赋给变量cprintf("max is %d\n",c); //输出大数creturn 0; }
int max(int x,int y) //定义max函数,有两个参数
{int z; //定义临时变量zz=x>y?x:y; //把x和y中大者赋给zreturn(z); //把z作为max函数的值带回main函数
}
定义函数,名为max,函数类型为int。指定两个形参x和y,形参的类型为int。
主函数中包含了一个函数调用max(a,b)。max后面括号内的a和b是实参。
a和b是在main函数中定义的变量,x和y是函数max的形式参数。
通过函数调用,在两个函数之间发生数据传递,实参a和b的值传递给形参x和y,在max函数中把x和y中的大者赋给变量z,z的值作为函数值返回main函数,赋给变量c。
例题 3
将例7.2稍作改动,将在max函数中定义的变量z改为float型
函数返回值的类型与指定的函数类型不同,分析其处理方法。
#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); return 0; }
int max(float x,float y)
{float z; //z为实型变量z=x>y?x:y;return(z);
}
max函数的形参是float型,实参也是float型,在main函数中输入给a和b的值是1.5和2.6。在调用max(a,b)时,把a和b的值1.5和2.6传递给形参x和y。执行函数max中的条件表达式“z=x>y?x:y”,使得变量z得到的值为2.6。现在出现了矛盾: 函数定义为int型,而return语句中的z为float型,要把z的值作为函数的返回值,二者不一致。
按赋值规则处理,先将z的值转换为int型,得到2,它就是函数得到的返回值。
如果将main函数中的c改为float型,用%f格式符输出,输出2.000000。因为调用max函数得到的是int型,函数值为整数2。
例题 4
输入两个实数,用一个函数求出它们之和。
#include <stdio.h>
int main()
{ float add(float x, float y); //对add函数作声明float a,b,c;printf("Please enter a and b:"); //提示输入scanf("%f,%f",&a,&b); //输入两个实数c=add(a,b); //调用add函数printf("sum is %f\n",c); //输出两数之和return 0;
}float add(float x,float y) //定义add函数
{ float z;z=x+y;return(z); //把变量z的值作为函数值返回
}
函数的声明和函数定义中的第1行(函数首部)基本上是相同的,只差一个分号(函数声明比函数定义中的首行多一个分号)。
函数的首行(即函数首部)称为函数原型(function prototype)。
因为在函数的首部包含了检查调用函数是否合法的基本信息(它包括了函数名、函数值类型、参数个数、参数类型和参数顺序),因此,在函数调用时检查函数原型是否与函数声明一致。这样就能保证函数的正确调用。
在函数声明中的形参名可以省写,而只写形参的类型。
如果已在文件的开头(在所有函数之前),已对本文件中所调用的函数进行了声明,则在各函数中不必对其所调用的函数再作声明。
例题 5
输入4个整数,找出其中最大的数。用函数的嵌套调用来处理。
主函数里面不能直接调用max2!!
#include<stdio.h>
#include<math.h>int main()
{ int max4(int a,int b,int c,int d); //max4函数声明int a,b,c,d,max;printf("请输入:");scanf("%d,%d,%d,%d",&a,&b,&c,&d);max = max4(a,b,c,d);printf("max = %d",max);return 0;
}
int max4(int a,int b,int c,int d)
{int max2(int a,int b); //max2函数的声明int m;m = max2(max2(max2(a,b),c),d);return m;
}
int max2(int a,int b){return (a>b?a:b);
}
改进
#include<stdio.h>
#include<math.h>int main()
{ int max4(int a,int b,int c,int d);int a,b,c,d,max;printf("请输入:");scanf("%d,%d,%d,%d",&a,&b,&c,&d);max = max4(a,b,c,d);printf("max = %d",max);return 0;
}
int max4(int a,int b,int c,int d)
{int max2(int a,int b);return max2(max2(max2(a,b),c),d);
}
int max2(int a,int b){return (a>=b?a:b);
}
例题 6
有5个学生坐在一起,问第5个学生多少岁,他说比第4个学生大2岁。问第4个学生岁数
他说比第3个学生大2岁。问第3个学生,又说比第2个学生大2岁
问第2个学生,说比第1个学生大2岁。最后问第1个学生,他说是10岁
请问第5个学生多大。
#include<stdio.h>
int main()
{int age(int n);printf("年龄是:%d",age(5));return 0;
}int age(int n)
{int c;if(n==1)c=10;else c=age(n-1)+2;return c;
}
例题 7
用递归方法求n!。
#include<stdio.h>
int main()
{int fac(int n);int n;scanf("%d",&n);printf("第%d个斐波那契数是%d",n,fac(n));return 0;
}int fac(int n)
{int c;if(n==1||n==0)return 1;else return fac(n-1)*n;}
例题 8
汉诺塔问题
#include<stdio.h>
#include<math.h>int main()
{ void hanno(int n,char one,char two,char three);int n;scanf("%d",&n);hanno(n,'A','B','C');return 0;
}void hanno(int n,char one,char two,char three){void move(char x,char y);if(n==1)move(one,three);else {hanno(n-1,one,three,two);move(one,three);//一个盘hanno(n-1,two,one,three); }
}void move(char x,char y)
{printf("%c>>>%c\n",x,y);
}
例题 9
数组元素作函数实参
输入10个数,要求输出其中值最大的元素和该数是第几个数。
#include <stdio.h>
int main()
{ int max(int x,int y); //函数声明int a[10],m,n,i;printf("enter 10 integer numbers:");for(i=0;i<10;i++) //输入10个数给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) //若max函数返回的值大于m{ m=max(m,a[i]); //max函数返回的值取代m原值n=i; //把此数组元素的序号记下来,放在n中}}printf("The largest number is %d\nit is the %dth number.\n",m,n+1);
}int max(int x,int y) //定义max函数
{ return(x>y?x:y); //返回x和y中的大者
}
例题 10
一维数组名作函数参数
有一个一维数组score,内放10个学生成绩,求平均成绩。
#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); //调用average函数printf("average score is %5.2f\n",aver);return 0;
} float average(float array[10]) //定义average函数
{ int i;float aver,sum=array[0];for(i=1;i<10;i++)sum=sum+array[i]; //累加学生成绩aver=sum/10;return(aver);
}
例题 11
有两个班级,分别有35和30名学生
调用average函数,分别求这两个班的学生的平均成绩。
#include<stdio.h>
#define len1 10
#define len2 5
int main()
{double aver(double arr[],int n);int i=0,j=0;double arr1[len1],arr2[len2];printf("请输入一班10名同学的成绩:\n");while((i-len1)!=0)scanf("%lf",&arr1[i++]);printf("请输入二班5名同学的成绩:\n");while((j-len2)!=0)scanf("%lf",&arr2[j++]);printf("一班的平均成绩为:%lf",aver(arr1,len1));printf("二班的平均成绩为:%lf",aver(arr2,len2));return 0;}double aver(double arr[],int n)
{int i=1;double sum = arr[0],aver;for(;i<n;i++){sum+=arr[i];}aver = sum/n;return aver;
}
例题 12
用选择法对数组中10个整数按由小到大排序。
#include<stdio.h>
int main()
{void select(double arr[],int n);const int len = 10;double arr[len];int i=0;printf("请输入%d个数字:",len);while((i-len)!=0)scanf("%lf",&arr[i++]);select(arr,len);i=0;while((i-len)!=0)printf("%.3lf\t",arr[i++]);return 0;
}
void select(double arr[],int n)
{int k;//锚点int i,j,temp;for(i=0;i<n-1;i++){k=i;for(j=i+1;j<n;j++){if(arr[j]<arr[k])k=j;}temp = arr[k];arr[k] = arr[i];arr[i] = temp;}
}
形参数组array已用选择法进行排序了,形参数组改变也使实参数组随之改变。
例题 13
有一个3×4的矩阵,求所有元素中的最大值。
#include <stdio.h>
int main()
{ int max_value(int array[][4]); //函数声明int a[3][4]={{1,3,5,7},{2,4,6,8},{15,17,34,12}}; //对数组元素赋初值printf("Max value is %d\n",max_value(a));//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]; //把大者放在max中return(max);
}
例题 14
有一个一维数组,内放10个学生成绩,写一个函数
当主函数调用此函数后,能求出平均分、最高分和最低分。
#include<stdio.h>
#include<math.h>double Max=0,Min=0;int main()
{double average(double array[],int n);double ave,sorce[10];int i;printf("Please enter 10 scores:");for(i=0;i<10;i++)scanf("%lf",&sorce[i]);ave=average(sorce,10);printf("max=%6.2lf\nmin=%6.2lf\naverage=%6.2lf\n",Max,Min,ave);return 0;
}double average(double array[],int n) //定义函数,有一形参是数组
{ int i;double aver,sum=array[0];Max = Min = array[0];for(i=1;i<n;i++){ if(array[i]>Max) Max=array[i];else if(array[i]<Min) Min=array[i];sum=sum+array[i]; }aver=sum/n;return(aver);}
例题 15
外部变量与局部变量同名,分析结果。
#include <stdio.h>
int a=3,b=5; //a,b是全局变量
int main()
{int max(int a,int b); //函数声明。a,b是形参int a=8; //a是局部变量printf("max=%d\n",max(a,b));return 0;
}int max(int a,int b) //a,b是函数形参
{ int c;c=a>b?a:b; //把a和b中的大者存放在c中return(c);
}
程序第2行定义了全局变量a和b,并对其初始化。
第3行是main函数,在main函数中(第6行)定义了一个局部变量a。局部变量a的作用范围为第6~8行。在此范围内全局变量a被局部变量a屏蔽,相当于全局变量a在此范围内不存在(即它不起作用)
而全局变量b在此范围内有效。因此第6行中max(a,b)的实参a应是局部变量a,所以max(a,b)相当于max(8,5)。它的值为8。
第10行起定义max函数,形参a和b是局部变量。全局变量a和b在max函数范围内不起作用,所以函数max中的a和b不是全局变量a和b,而是形参a和b,它们的值是由实参传给形参的,即8和5。
- 不同数据结构可以用相同的变量名字
- 变量不能与函数重名
例题 16
考察静态局部变量的值。
#include <stdio.h>
int main()
{ int f(int); int a=2,i; for(i=0;i<3;i++)printf("%d\n",f(a)); return 0;
}int f(int a)
{ auto int b=0; static int c=3; b=b+1;c=c+1;return(a+b+c);
}
例题 17
考察静态局部变量的值。
输出1到5的阶乘值
输出1到5的阶乘值。
#include <stdio.h>
int main()
{ int fac(int n);int i;for(i=1;i<=5;i++) //先后5次调用fac函数printf("%d!=%d\n",i,fac(i)); //每次计算并输出i!的值return 0;
}
int fac(int n)
{ static int f=1; //f保留了上次调用结束时的值f=f*n; //在上次的f值的基础上再乘以nreturn(f); //返回值f是n!的值
}
例题 18
调用函数,求3个整数中的大者。
#include <stdio.h>
int main()
{ int max();extern int A,B,C; //把外部变量A,B,C的作用域扩展到从此处开始printf("Please enter three integer numbers:");scanf("%d %d %d",&A,&B,&C); //输入3个整数给A,B,Cprintf("max is %d\n",max());return 0;
}
int A,B,C; //定义外部变量A,B,C
int max()
{ int m;m=A>B?A:B; //把A和B中的大者放在m中if(C>m) m=C; //将A,B,C三者中的大者放在m中return(m); //返回m的值
- 提倡将外部变量的定义放在引用它的所有函数之前,这样可以避免在函数中多加一个extern声明。
- 用extern声明外部变量时,类型名可以写也可以省写。例如,“
extern int A,B,C;
”也可以写成“externA,B,C;”
。因为它不是定义变量,可以不指定类型,只须写出外部变量名即可。
例题 19
给定b的值,输入a和m,求a*b和a m的值
。
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);
file2.c
extern A;
//把file1中定义的外部变量的作用域扩展到本文件
int power(int n)
{ int i,y=1;for(i=1;i<=n;i++)y*=A;return(y);
}
用这种方法扩展全局变量的作用域应十分慎重,因为在执行一个文件中的操作时,可能会改变该全局变量的值,会影响到另一文件中全局变量的值,从而影响该文件中函数的执行结果。
例题 20
有一个字符串,内有若干个字符,现输入一个字符,要求程序将字符串中该字符删去。用外部函数实现。
- file1.c
#include<stdio.h>
int main()
{extern void enter(char str[80]);extern void delete(char str[80],char c);extern void print(char str[80]);char c,str[80];printf("输入字符串:\n");enter(str);printf("输入字符C:\n");scanf("%c",c);delete(str,c);print(str);return 0;}
- file2.c
void enter(char str[80])
{gets(str);
}
- file3.c
void delete(char str[80],char c)
{int i,j;for(i=0;str[i]!='\0';i++){if(str[i]!=ch){str[j++] = str[i]; }}str[j] = '\0';
}
- file4.c
extern void print(char str[80])
{puts(str);
}
习题
习题1
写两个函数,分别求两个整数的最大公约数和最小公倍数,用主函数调用这两个函数,并输出结果。两个整数由键盘输人
该题直接使用“辗转相除法”来求解最大公约数和最小公倍数
最大公约数找出两数中的最小值,然后直接相模,当能够同时被两数整除时,则为最大公约数。
最小公倍数找出两数中的最大值,然后直接进入死循环,直到找到一个数能够同时被两数整除时,则为最小公倍数
#include<stdio.h>int maxf(int a,int b);
int minf(int a,int b);int main()
{ int a,b;int max,min;scanf("%d,%d",&a,&b);maxf(a,b);minf(a,b);return 0;
}// 辗转相除法 求最大公约数
int maxf(int a,int b)
{int t,c;if(a<b){t=a;a=b;b=t;}c=a%b;while(c!=0){a=b;b=c;c=a%b;}printf("最大公约数是:\n%d\n",b);
}int minf(int a,int b)
{int t,c;int max = a*b;if(a<b){t=a;a=b;b=t;}c=a%b;while(c!=0){a=b;b=c;c=a%b;}max = max/b;printf("最小公倍数是:\n%d\n",max);
}
习题 2
???用数组返回??
习题 3
写一个判素数的函数,在主函数输人一个整数,输出是否为素数的信息。
#include<stdio.h>
#include<math.h>void sushu(int a);int main()
{ int a;scanf("%d",&a);sushu(a);return 0;
}void sushu(int a)
{int i,k,flag=1;k = sqrt(a);for(i=2;i<k;i++){if(a%i==0){printf("%d不是素数",a);flag=1;break;}}if(flag==0){printf("%d是素数",a);}
}
简洁
#include<stdio.h>
#include<math.h>
int main()
{int flag, n;int is_prime(int);scanf("%d", &n);flag = is_prime(n);if (flag == 1)printf("prime\n");elseprintf("not prime\n");return 0;
}
int is_prime(int n)
{int flag = 1, i;for (i = 2; i < sqrt(n); i++)if (n % i == 0)flag = 0;return flag;
}
习题 4
写一个函数,使给定的一个3X3的二维整型数组转置,即行列互换。
#include<stdio.h>
int main()
{// 二维数组作为形参不指定列数,是非法的 void PrintArray(int arr[][3]);void ReverseArray(int arr[][3]);int arr[3][3] = {1,2,3,4,5,6,7,8,9};printf("转置前:\n");PrintArray(arr);ReverseArray(arr);printf("转置后:\n");PrintArray(arr);return 0;
}void ReverseArray(int arr[][3])
{int i,j,tmp;for(i=0;i<3;i++){for(j=0;j<i;j++){if(i!=j){tmp = arr[i][j];arr[i][j] = arr[j][i];arr[j][i] = tmp;}}}
}void PrintArray(int arr[][3])
{int i,j;for(i=0; i<3; ++i){for(j=0; j<3; ++j){printf("%d ", arr[i][j]);}printf("\n");}
}
习题 5
写一个函数,使输人的一个字符串按反序存放,在主函数中输入和输出字符串。
#include<stdio.h>
#include<stdio.h>
#include<math.h>
int main()
{char arr[100];void jiaohuan(char arr[]);printf("enter:");scanf("%s", arr);printf("原始字符串为:> %s\n", arr);jiaohuan(arr);return 0;
}void jiaohuan(char arr[])
{int index=0,begin=0,end=0;char temp;while(arr[index++]!='\0');for(end=index-2;begin<=end;begin++,end--){temp = arr[begin];arr[begin] = arr[end];arr[end] = temp;}printf("反序字符串为:> %s\n", arr);printf("index = %d\n",index);
}
注意++index与index++导致的end起点
习题 6
写一个函数,将两个字符串连接。
#include<stdio.h>
#include<math.h>
int main()
{void connect(char arr1[],char arr2[]);char arr1[100],arr2[50];printf("输入arr1:");gets(arr1);printf("输入arr2:");gets(arr2);connect(arr1,arr2);printf("连接后为:");puts(arr1);return 0; }void connect(char arr1[],char arr2[])
{int index=0,i;while(arr1[++index]!='\0');while((arr1[index++]=arr2[i++])!='\0');arr1[index-1] = '\0';printf("index = %d",index);}
习题 7
写一个函数,将一个字符串中的元音字母复制到另一字符串,然后输出。
#include<stdio.h>
#include<math.h>
int main()
{void yuanyinf(char s[]);char arr[100];printf("enter arr:");gets(arr);yuanyinf(arr);return 0;
}
void yuanyinf(char s[]){int index=0,i=0,j=0;char yuanyin[100]={'\0'};while(s[++index]!='\0');for(;i<index;i++){if (s[i] == 'a' || s[i] == 'A' || s[i] == 'e' || s[i] == 'E' || s[i] == 'i' ||s[i] == 'I' || s[i] == 'o' || s[i] == 'O' ||s[i] == 'u' || s[i] == 'U')yuanyin[j++] = s[i];}printf("index = %d\n",index);puts(yuanyin);
}
习题 8 作业
写一个函数,输人一个4位数字,要求输出这4个数字字符,但每两个数字间空一个空格。如输人1990,应输出“1 9 9 0”。
对字符串进行遍历输出,没输出一个字符,后面就跟着输出一个空格,关键点在于如果输出的是最后一个字符,则不能在输出字符,所以要对是否是最后一个字符的输出进行判断。
??整数转换成字符串
求一个整数的各个位数
log
数字转字符
str[i-1] = a/(int)pow(10,n-i)%10 +'\0'
一共五个元素! arr[5];
不要漏了 最后的’\0’
生成一个新的字符数组输出
字符串转整数?
a=0;
for(int i =0;i<strlen(str);i++
a+=a*10+(str[i]-'\0')
#include<stdio.h>
int main()
{void outstr(char str[]);char str[5] = {'\0'};printf("输入四个数字");scanf("%s", str);outstr(str);return 0;
}void outstr(char str[])
{ int i=0;while(str[i]!='\0'){printf("%c",str[i]);//是不是最后一个if(str[i+1]=='\0')break;printf(" "); i++;}
}
上课更新
#include<stdio.h>
#include<math.h>
int main()
{void outstr(int num);int num;char str[5] = {'\0'};printf("输入四个数字");scanf("%d",&num);outstr(num);return 0;
}void outstr(int num)
{int len = (int)log10(num)+1;int i=0;char str[10] = {'\0'};for(i=1;i<=len;i++){str[i-1] = num/(int)pow(10,len-i)+'0';num= num%(int)pow(10,len-i);}for(i=len-1;i>0;i--){str[2*i] = str[i];str[2*i-1] = ' ';}puts(str);
}
习题 9 作业
编写一个函数,由实参传来一个字符串,统计此字符串中字母、数字、空格和其他字符的个数,在主函数中输入字符串以及输出上述的结果
**注意点!! 全局变量!
用数组返回吧
outstr里面,i有没有自增!!**
while(str[i++]!='\0')
#include<stdio.h>
#include <string.h>
// 重点!!! 全局变量
int word=0,num=0,spa=0,oth=0;
int main()
{void tongji(char str[]);char str[100] ;
// int word=0,num=0,spa=0,oth=0;printf("输入字符串\n");gets(str); tongji(str);printf("\nletter:%d\ndigit:%d\nspace:%d\nothers:%d\n", word, num, spa, oth);return 0;
}void tongji(char str[])
{int i=0;
// int word=0,num=0,spa=0,oth=0;while(str[i++]!='\0') {if((str[i]>='a'&&str[i]<='z')||(str[i]>='A'&&str[i]<='tZ'))word++;else if(str[i]>='1'&&str[i]<='9')num++;else if(str[i]==' ')spa++;else oth++;}
}
作业
#include<stdio.h>
#include <string.h>int main()
{void tongji(int *count,char str[]);char str[100] ;int count[4] = {0};
// int word=0,num=0,spa=0,oth=0;printf("输入字符串\n");gets(str); tongji(count,str);printf("\nletter:%d\ndigit:%d\nspace:%d\nothers:%d\n", count[0], count[1], count[2], count[3]);return 0;
}void tongji(int *count,char str[])
{int i=0;while(str[i]!='\0') {if((str[i]>='a'&&str[i]<='z')||(str[i]>='A'&&str[i]<='Z'))*(count+0)+=1;else if(str[i]>='0'&&str[i]<='9')*(count+1)+=1;else if(str[i]==' ')*(count+2)+=1;else *(count+3)+=1;i++; }
}
习题 10
写一个函数,输人一行字符,将此字符串中最长的单词输出。
题目解析及答案:
单词以空格进行分隔,因此寻找空格出现的位置即为关键,每次从单词的起始到空格出现的位置即为一个单词,此时计算单词的长度,如果比当前最大的还长,就进行跟新最长单词信息,当整个字符串遍历完成,word即保存最长字符串。
#include<stdio.h>
#include <string.h>int main()
{void longword(char s1[]);char s1[100];gets(s1);
// puts(s1);longword(s1);return 0;
}
void longword(char s1[])
{// 1.求最长的长度// 2. 临时数组 储存目前最大的单词// 3. 空格后判断,重新储存目前单词 int i=0;int j=0;//循环临时数组 char linshi[20]={'\0'},max[20]={'\0'};while(s1[i++]!='\0') {if(s1[i]!=' '){linshi[j++]=s1[i];}else{//是否超过maxif((j-1)>strlen(max)) {// 把linshi 拷贝到maxstrcpy(max,linshi); // 重置 临时数组 j=0;}// 没有超过else{//重置临时数组 j=0;} }}// 处理最后一个单词if((j-1)>strlen(max)) {// 把linshi 拷贝到maxstrcpy(max,linshi); }puts(linshi);puts(max);}
习题 11
函数–起泡法
#include <stdio.h>
void main()
{void bubbleSort(int *arr);printf("请输入:");int i=0,j,arr[5];while((i-5)!=0){scanf("%d",&arr[i]) ;i++;}bubbleSort(arr);printf("排序后的数组为:\n");for (j = 0; j<5; j++)printf("%d ", arr[j]);printf("\n");
}void bubbleSort(int *arr)
{int i,j,tmp;for(i=0;i<5-1;i++){for(j=0;j<5-1-i;j++){if(arr[j]>arr[j+1]){tmp = arr[j];arr[j]=arr[j+1];arr[j+1]=tmp;}}}
}
习题 12
#include<stdio.h>
#include <string.h>int main()
{double niudun(double a, double b,double c,double d);double a, b, c, d,x;scanf("%lf,%lf,%lf,%lf",&a,&b,&c,&d);printf("x=%10.7f\n", niudun(a, b, c, d));return 0;
}
double niudun(double a, double b,double c,double d)
{double x = 1, x0, f, f1;do{x0 = x;f = ((a*x0 + b)*x0 + c)*x0 + d;f1 = (3 * a*x0 + 2 * b)*x0 + c;x = x0 - f / f1;}while (fabs(x - x0) >= 1e-3);return(x);
}
习题 13
用递归方法求n阶勒让德多项式的值,递归公式为
#include<stdio.h>
int main()
{double leirand(int n,int x);int n,x;scanf("%d,%d",&n,&x);printf("%.2lf\n",leirand(n,x));main();return 0;
}
double leirand(int n,int x)
{double result;if(n==0)result = 1;if(n==1)result = x;if(n>1) return ((2*n-1)*x*leirand(n-1, x)-(n-1)*leirand(n-2, x))/n;return result;
}
习题 14
习题 15 作业
写几个函数:
①输人10个职工的姓名和职工号;
②按职工号由小到大顺序排序,姓名顺序也随之调整;
③要求输人一个职工号,用折半查找法找出该职工的姓名,从主函数输人要查找的职工号,输出该职工姓名。
#include<stdio.h>
#include<string.h>
#define N 5
int main()
{void input(int num[], char name[N][8]);void sort(int num[], char name[N][8]);void search(int n, int num[], char name[N][8]);int num[N], number; char name[N][8],c;input(num, name);sort(num, name);while(1){printf("输入查找的职工号:\n");scanf("%d",&number);search(number,num,name);printf("是否继续(Y/N)?");getchar();c = getchar();if (c == 'N' || c == 'n')break;}return 0;
}
void input(int num[], char name[N][8])
{int i;for (i = 0; i < N; i++){printf("输入职工号:");scanf("%d", &num[i]);printf("输入姓名: ");getchar();// 吸收回车 gets(name[i]);}}
void sort(int num[], char name[N][8])
{int i, j, min, temp1;char temp2[8];for (i = 0; i <N-1; i++){min = i;for (j = i; j<N; j++)if (num[min]>num[j]) min = j;
// 对调职工号 temp1 = num[i];num[i] = num[min];num[min] = temp1;// 对调 姓名 strcpy(temp2, name[i]);strcpy(name[i], name[min]);strcpy(name[min], temp2);}printf("\n排序结果\n");for (i = 0; i < N; i++)printf("\n %5d %10s", num[i], name[i]);printf("\n");
}void search(int n, int num[], char name[N][8])
{int top = N-1;int bot = 0;int mid;if(n<num[bot]||n>num[top])printf("输入错误!");else if(n==num[bot])printf("输入错误!");else{while(1){mid = (top+bot)/2;
// printf("mid = %d",mid);if(n==num[mid]){printf("姓名是%s\n",name[mid]);break;}else if(n<num[mid]) {top=mid-1;}else if(n>num[mid]) {bot=mid+1;}} }
}
指针数组
数据结构
指针数组
char *num[10]={"","",""};
char *name[10]={"","",""};
结构体
习题 16 作业
写一个函数,输人一个十六进制数,输出相应的十进制数。
转换的过程需要乘的基数为16,其次注意十六进制中的a~f的字母转换,并且无论大小写都要能够转换。
#include<stdio.h>int HextoDec(char s[])
{int i, n;n = 0;for (i = 0; s[i] != '\0'; i++){if (s[i] >= '0'&& s[i] <= '9')n = n * 16 + s[i] - '0';if (s[i] >= 'a' && s[i] <= 'f')n = n * 16 + s[i] - 'a' + 10;if (s[i] >= 'A' && s[i] <= 'F')n = n * 16 + s[i] - 'A' + 10;}return n;
}int main()
{size_t result = 0;char hex[9] = {0}; printf("input a HEX number:");scanf("%s", hex);result = HextoDec(hex);printf("0x%s = %u\n", hex, result);return 0;
}
习题 17
习题 18
18、给出年、月、日,计算该日是该年的第几天。
闰年 leap = year % 4 == 0 && year % 100 != 0 || year % 400 == 0;
#include<stdio.h>int sum_day(int month, int day);
int leap(int year);int main()
{int year, month, day, days;printf("input date(year,month,day):");scanf("%d %d %d", &year, &month, &day);printf("%d/%d/%d ", year, month, day);days = sum_day(month, day); if (leap(year) && month >= 3) //包含2月的闰年 days = days + 1;printf("is the %dth day in this year.\n", days);return 0;
}int sum_day(int month, int day){int day_tab[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };int i;for(i=1;i<month;i++){day +=day_tab[i];}return day;
}int leap(int year){int leap;// 公历年份是4的倍数,且不是100的倍数的,为闰年 leap = year % 4 == 0 && year % 100 != 0 || year % 400 == 0;return leap;
}