C语言——函数

  • Post author:
  • Post category:其他



C语言目录:


1. 概述


2. 数据类型


3. 量


4. 运算符


5. 流程控制


6. 函数


7. C程序编译过程


8. 文件


9. 内存管理


将一个常用的功能封装起来,方便以后调用



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);
}
  • 存在一个条件能够让递归结束

  • 问题的规模能够缩小

  • 能用循环实现的功能,用递归都可以实现



代码理解难度大



内存消耗大(易导致栈溢出)

, 所以考虑到代码理解难度和内存消耗问题, 在企业开发中一般能用循环都不会使用递归