C语言目录:
将一个常用的功能封装起来,方便以后调用
6.1 分类及定义
同一源文件函数名称不能相同
返回值类型 函数名(参数类型 形式参数1,参数类型 形式参数2,…){
函数体;
返回值;
}
6.1.1 库函数与用户定义函数
a. 库函数
由C语言系统提供,用户无需定义,也不必在程序中做类型说明,引入头文件后即可在程序中直接调用
b. 用户定义的函数
由用户按需编写的函数。对于用户自定义的函数,不仅要在程序中定义函数本身,而且在主调函数中必须对该被调函数进行声明,才能调用
6.1.2 有返回值与无返回值函数
a. 有返回值函数
必须指定返回值类型和使用return关键字返回对应数据
返回值类型 函数名(参数列表){
函数体;
返回值;
}
b. 无返回值函数
void 函数名(参数列表) {
函数体;
}
6.1.3 有参函数和无参函数
a. 无参函数
返回值类型 函数名() {
函数体;
return 值;
}
b. 有参函数
返回值类型 函数名(参数类型 形式参数1,参数类型 形式参数2,…) {
函数体;
return 0;
}
6.2 参数与返回值
6.2.1 形参与实参
形参
:定义函数时,函数名后面的
()
称为形参
形参变量只有在函数被调用时分配内存。在函数调用结束,释放内存单元
实参
:函数调用时,传入的值称为实参
实参可以是常量、变量、表达式、函数等
在进行函数调用时,都必须具有
确定的值
,以便把这些值传递给形参
a. 注意
个数
:调用函数时,传递的实参个数和函数的形参个数必须保持一致
类型
:形参与实参类型不一致,会自动转换为形参类型
void change(double number1, double number2) {// 形参
}
int main() {
change(10, 20);
// 传入实参 10.000000, 20.000000
// 自动将实参转换为double类型后保存
return 0;
}
值传递
:当使用基本数据类型(char、int、float)作为实参时,实参和形参之间只是值传递,修改形参的值并不影响实参
void change(int number1, int number2) { // 形式参数
number1 = 250; // 不会影响实参
number2 = 222;
}
int main() {
int a = 88;
int b = 99;
change(a, b);
printf("a = %d, b = %d", a, b); // 输出结果: 88, 99
return 0;
}
6.2.2 返回值
如果没有写返回值类型,默认是
int
return
实际返回的值类型应与函数返回值类型一致,否则以返回值类型为准,自动进行类型转换
int height() {
return 3.14;
}
int main() {
double temp = height();
printf("%lf", temp);// 输出结果: 3.000000
}
一个函数内部可以多次使用
return
,但
return
之后的代码不再执行
6.3 函数的声明
默认情况下,只有后面定义的函数才可以调用前面定义过的函数
函数声明:在函数调用之前告诉系统,函数名称、参数列表、返回值类型
函数实现:定义具体的业务逻辑是怎么运作的
函数声明格式:
返回值类型 函数名(参数列表);
// 函数声明
void getMax(int v1, int v2);
int main(int argc, const char * argv[]) {
getMax(10, 20); // 调用函数
return 0;
}
// 函数实现
void getMax(int v1, int v2) {
int max = v1 > v2 ? v1 : v2;
printf("max = %i\n", max);
}
-
函数的实现不能重复, 而函数的声明可以重复
-
函数声明可以写在函数外面,也可以写在函数里面,,只要在调用之前被声明即可
int main(int argc, const char * argv[]) {
void getMax(int v1, int v2); // 函数声明, 不会报错
getMax(10, 20); // 调用函数
return 0;
}
// 函数实现
void getMax(int v1, int v2) {
int max = v1 > v2 ? v1 : v2;
printf("max = %i\n", max);
}
如果被调函数的返回值是整型,可以不对被调函数做声明,可以直接调用
int main(int argc, const char * argv[]) {
int res = getMin(5, 3); // 不会报错
printf("result = %d\n", res );
return 0;
}
int getMin(int num1, int num2) {// 返回int, 不用声明
return num1 < num2 ? num1 : num2;
}
6.4 输入输出
6.4.1 putchar()和getchar()
putchar()
:向屏幕输出一个字符
#include <stdio.h>
int main(){
char ch = 'a';
putchar(ch); // 输出a
}
getchar()
:从键盘获取一个字符
#include <stdio.h>
int main(){
char ch;
ch = getchar();// 获取一个字符
printf("ch = %c\n", ch);
}
6.4.2 scanf函数
系统会先将用户输入放入输入缓冲区,从缓冲区中逐个取数据赋值给变量,如果输入缓冲区不为空,
scanf
会一直从缓冲区中获取
#include <stdio.h>
int main(){
int num1;
int num2;
char ch1;
scanf("%d%c%d", &num1, &ch1, &num2);
printf("num1 = %d, ch1 = %c, num2 = %d\n", num1, ch1, num2);
char ch2;
int num3;
scanf("%c%d",&ch2, &num3);
printf("ch2 = %c, num3 = %d\n", ch2, num3);
}
a. fflush
利用
fflush(stdin);
清空缓冲区
C和C++标准从未定义过,是C标准的扩充函数,
不是所有平台都支持
b. setbuf
setbuf(stdin,NULL)
设置缓冲区为空
所有平台有效
6.4.3 printf()函数格式
a. 数据类型符
类型 | 含义 |
---|---|
%d / %i | 有符号十进制整型 |
%u |
无符号 十进制整型 |
%o |
无符号 八进制整型 |
%x / %X |
无符号 十六进制整型 |
%f | 单、双精度浮点数 |
%e / %E | 以指数形式输出单、双精度浮点数 |
%c | 字符 |
%s | 字符串 |
%p | 地址 |
%% | 表示%本身 |
#include <stdio.h>
int main(){
int a = 10;
int b = -10;
// 有符号整数(可以输出负数)
printf("a = %d\n", a); // 10
printf("a = %i\n", a); // 10
// 无符号整数(不可以输出负数)
// 输出存储单元内二进制数对应的十进制整数
printf("a = %u\n", a); // 10
printf("b = %u\n", b); // 429496786
// 无符号八进制整数(不可以输出负数)
//输出存储单元内二进制数对应的十进制整数对应的八进制数
printf("a = %o\n", a); // 12
printf("b = %o\n", b); // 37777777766
// 无符号十六进制整数(不可以输出负数)
printf("a = %x\n", a); // a
printf("b = %x\n", b); // fffffff6
// 无符号十六进制整数(不可以输出负数)
printf("a = %X\n", a); // A
printf("b = %X\n", b); // FFFFFFF6
float c = 6.6f;
double d = 3.1415926;
// 单、双精度浮点数(默认保留6位小数)
printf("c = %f\n", c); // 6.600000
printf("d = %lf\n", d); // 3.141593
double e = 10.10;
// 以指数形式输出单、双精度浮点数
printf("e = %e\n", e); // 1.010000e+001
printf("e = %E\n", e); // 1.010000E+001
// 以最短输出宽度,输出单、双精度浮点数
printf("e = %g\n", e); // 10.1
printf("e = %G\n", e); // 10.1
char f = 'a';
// 输出字符
printf("f = %c\n", f); // a
}
实型有效位问题
-
单精度
%f
输出时,仅前6-7位是有效数字;双精度
%lf
输出时,仅前15-16位是有效数字 - 有效位数包含小数点前的非零数位
精度与有效位
有效位:指从第一个非零数字开始,误差不超过本数位半个单位的、正确的数位
原因:计算机存储浮点数时,采用IEEE754,存的是IEEE754可表示的最相近浮点数
#include <stdio.h>
int main(){
// 1234.567871093750000
float a = 1234.567890123456789;
// 1234.567890123456900
double b = 1234.567890123456789;
printf("a = %.15f\n", a); // 前8位数字是准确的, 后面的都不准确
printf("b = %.15f\n", b); // 前16位数字是准确的, 后面的都不准确
}
b. 精度格式符
精度:小数点后位数
%.n数据类型符
,n为十进制整数
- 如果输出数字,则表示小数的位数;
- 如果输出的是字符,则表示输出字符的个数;
- 若实际位数大于所定义的精度数,则截去超过的部分
#include <stdio.h>
int main(){
double a = 3.1415926;
printf("a = %.2f\n", a); // 3.14
}
动态指定保留小数位数
格式:
printf("%.*f",[],a);
#include <stdio.h>
int main(){
double a = 3.1415926;
printf("a = %.*f", 2, a); // 3.14
}
c. 格式控制符
符号 | 含义 |
---|---|
%m… | m指定输出字段的宽度,如果位数小于m,则左端以空格补全;若位数大于,则按实际位数输出 |
– | 结果左对齐,右边补空格 |
# |
对于c,s,d,u类影响 八进制,加前缀o 十六进制,加前缀0x 对于浮点数,只有当结果有小数才给出小数点 |
空格 | 输出值为整数,在输出值前面加上空格,为负数加上负号 |
+ | 当输出值为正数时,在输出值前面加上一个+号, 默认不显示 |
0 | 右对齐时,用0填充 |
#include <stdio.h>
int main(){
int a = 1;
int b = -1;
// -号标志
printf("a =|%d|\n", a); // |1|
printf("a =|%5d|\n", a); // | 1|
printf("a =|%-5d|\n", a);// |1 |
// +号标志
printf("a =|%d|\n", a); // |1|
printf("a =|%+d|\n", a);// |+1|
printf("b =|%d|\n", b); // |-1|
printf("b =|%+d|\n", b);// |-1|
// 0标志
printf("a =|%5d|\n", a); // | 1|
printf("a =|%05d|\n", a); // |00001|
// 空格标志
printf("a =|% d|\n", a); // | 1|
printf("b =|% d|\n", b); // |-1|
// #号
int c = 10;
printf("c = %o\n", c); // 12
printf("c = %#o\n", c); // 012
printf("c = %x\n", c); // a
printf("c = %#x\n", c); // 0xa
}
6.7 递归函数
一个函数在它的函数体内调用它自身称为递归调用
- 递归常用于”回溯”, “树的遍历”,”图的搜索”等问题
void function(int x){
function(x);
}
-
存在一个条件能够让递归结束
-
问题的规模能够缩小
-
能用循环实现的功能,用递归都可以实现
但
代码理解难度大
,
内存消耗大(易导致栈溢出)
, 所以考虑到代码理解难度和内存消耗问题, 在企业开发中一般能用循环都不会使用递归