C语言函数

1. 1函数的基本使用

  • 问题背景:在编程中,重复编写相同的代码段会导致程序臃肿且难以维护。
  • 解决方案:将重复的代码段封装成函数,通过函数调用重复使用这些代码段。
  • 好处:实现代码重用,减少冗余,简化代码。
  • 一个C源程序可以由一个或多个源文件构成(C文件扩展名是 “.c”),一个源文件是一个编译单位。一个源文件可以由若干个 函数构成,函数之间可以相互调用。也就是说, 函数是C程序基 本的组成单位 。
1.2 函数的分类
  • 从程序执行的角度看
    • 主函数main()函数,每个C程序必须有一个且只有一个。
    • 子函数:非main()函数,可以被main()函数或其他子函数调用。
  • 从源文件外调用角度看
    • 内部函数:只能被定义它的文件内的函数调用。
    • 外部函数:可以被其他文件内的函数调用。
  • 从用户使用的角度看
    • 库函数:由C系统提供,如字符串操作、字符操作、时间/日期、数学、IO、内存操作等函数。
    • 用户自定义函数:解决用户特定业务需求的函数。
1.3 函数的声明格式
返回值类型 函数名(数据类型1 形参1, 数据类型2 形参2, ..., 数据类型n 形参n) {
函数体;
}
//计算两个整数的和,并返回
int add(int m,int n) {
 return m + n;
}
//计算两个整数的较大值,并返回
int max(int a, int b){  //定义函数max()
    int c;
    c = a > b ? a : b;     //求a,b两个数的最大值,赋给c
    return c;     //将最大值返回
}
void printMax(int x,int y){
 int z;
    z = x > y ? x : y;     //求x,y两个数的最大值,赋给z
    printf("%d\n",z);
}
  • 返回值类型:无返回值的类型 :针对函数无返回值或明确不需返回值的情 况,使用 void (即空类型)表示。 举例:输出函数 void printf(const char *format, ...)。                                       有返回值的类型 :指明具体的类型。比如, int、float、char 等。如果省略,默认为int类型。 有返回值类型,则需要在函数体内与“ return 返回值 ”搭 配使用。返回值需要与返回值类型一致。 举例:int rand(),调用后返回一个随机整数
  • 函数名:遵循标识符命名规则,见名知意。
  • 参数列表:无参函数 ,在调用无参函数时,主调函数不向被调用函数传递 数据。但函数名后的()不能省略。                                                                                                                            举例:abort():立即终止程序的执行,不接受任何形参。                                                           有参函数 ,在调用函数时,主调函数在调用被调用函数时,通 过参数向被调用函数传递数据。 函数参数为多个参数时,其间用逗号隔开。                                                                         举例:add(int m,int n),strcmp(const char *str1, const char *str2)
  • 函数体:函数被调用后要执行的代码块。
  • return语句的作用:① 结束函数的执行 ②将函数运算的结果返 回。 return语句后面就不能再写其它代码了,否则会报错。(与 break、continue情况类似) 下面分两种情况讨论: 情况1:返回值类型不是void时,函数体中必须保证一定有 return 返回值; 语句,并且要求该返回值结果的类型与声 明的返回值类型一致或兼容。 情况2:返回值类型是void时,函数体中可以没有return语 句。如果要用return语句提前结束函数的执行,那么return后 面不能跟返回值,直接写 return; 就可以。
1.4 声明注意事项
  • 函数不能嵌套定义:一个函数内部不能定义另一个函数。
  • 函数名唯一:程序中不能有重名的函数(不支持重载)。
1.5 函数的调用
  • 调用方式:在函数调用时传入实际的参数值。
  • 调用规则
    • 参数个数必须与函数声明中的参数个数一致。
    • 函数间可以相互调用,但不能调用main()函数。
    • 参数和返回值类型会根据需要进行自动类型转换。
void func() {
    printf("这是我的第一个函数!\n");
}

int main() {
    func();
    
    //func(10);     // 报错
 //func(10,20);  // 报错
    
    return 0; //程序正常结束,默认返回0
}
int func(int x, int y) {
    return x + y;
}
int main() {
    int sum = func(3, 5);
    printf("x+y=%d\n", sum);
    
    return 0;
}
说明: 1、调用时,参数个数必须与函数声明里的参数个数一致,参数 过多或过少都会报错。 2、函数间可以相互调用,但不能调用main函数,因为main函数 是被操作系统调用的,作为程序的启动入口。反之,main() 函数 可以调用其它函数。 3、函数的参数和返回值类型,会根据需要进行自动类型转换。
2. 进一步认识函数
2.1 关于main()
  • 作用:程序的入口点,每个C程序必须包含。
  • 返回值int main()函数默认返回0表示成功,返回非零值表示错误。
  • 参数:可以带有两个参数int argc, char *argv[],用于接收命令行参数。
2.2 关于exit()
  • 作用:终止整个程序的运行。
  • 参数:可以返回给操作系统一个状态码,表示程序是正常结束还是异常终止。
2.3 函数原型
  • 定义:在函数声明时,只包含函数的基本信息(不包括函数体),用于在函数调用前告诉编译器函数的存在。
  • 作用:确保函数在使用前已被声明。
3. 参数传递机制
3.1 形参、实参
  • 形参:函数定义时的参数,不占用内存,调用时分配内存。
  • 实参:函数调用时的参数,传递给形参。
int add(int x, int y) {  //x,y是add()中的形参
    int z;
    z = x + y;
    return (z);
}
int main() {
    int c;
    int a = 10,b = 20;
    c = add(a, b); //此时将实参a,b赋值给add()的形参
    printf("sum is %d\n", c);
    return 0;
}
3.2 值传递
  • 定义:将实参的值复制给形参,形参的改变不影响实参。
  • 适用类型:基本数据类型、结构体、共用体、枚举类型。
3.3 地址传递
  • 定义:将实参的地址传递给形参,形参通过地址修改实参的值。
  • 适用类型:指针、数组。
3.4 数组、字符串作为形参
  • 数组名:作为形参时,传递的是数组首元素的地址。把数组名传入一个函数,就等同于传入一个指针变量。在函 数内部,就可以通过这个指针变量获得整个数组。
  • 字符串:作为形参时,与数组类似,传递的是字符串首字符的地址。
4. 函数的高级应用
4.1 递归函数
  • 定义:函数直接或间接调用自身的现象。
// 递归函数,计算n的阶乘
int factorial(int n) {
    // 基本情况:当n为0或1时,阶乘为1
    if (n == 0 || n == 1) {
        return 1;
   } else {
        // 递归情况:n! = n * (n-1)!
        return n * factorial(n - 1);
   }
}
int main() {
    int n = 5;
    // 调用递归函数计算n的阶乘
    int result = factorial(n);
    printf("%d! = %d\n", n, result);
    return 0;
}
  • 注意事项:必须有基本情况以结束递归,避免无限递归导致栈溢出。
4.2 可变参数
  • 定义:函数参数数量不固定,使用省略号...表示。
  • 使用:需要包含头文件<stdarg.h>,并使用va_listva_startva_argva_end宏处理可变
4.3 指针函数(返回值是指针)
  • 定义:函数的返回值是一个指针(地址),这样的函数称为指针函数。
  • 一般格式
    返回值类型 *函数名(形参列表) {
    函数体
    }
  • 示例:获取两个字符串中较长的那个字符串                                                                                 
    #include <stdio.h>
    #include <string.h>
    char *maxLengthStr(char *str1, char *str2) { //函数返回
    char * (指针)
        printf("\nstr1的长度%d,str2的长度%d", strlen(str1),
    strlen(str2));
        if (strlen(str1) >= strlen(str2)) {
            return str1;
       } else {
            return str2
       }
    }
    
    int main() {
        char str1[30], str2[30];
        printf("请输入第1个字符串:");
        gets(str1);
        printf("请输入第2个字符串:");
        gets(str2);
        char *str;
        str = maxLengthStr(str1, str2);
        printf("\nLonger string: %s \n", str);
        return 0;
    }
    

4.4 函数指针(指向函数的指针)
  • 定义:函数指针是指向函数的指针,通过它可以调用函数。
  • 一般格式
    返回值类型 (*指针变量名)(参数列表);
  • 示例:用函数指针来实现对函数的调用,返回两个整数中的最大值。
int max(int a, int b) {
    return a > b ? a : b;
}
int main() {
    int x, y;
    int (*pmax)(int, int) = &max; // 使用函数指针
    printf("输入两个整数:");
    scanf("%d %d", &x, &y);
    int maxVal = (*pmax)(x, y);
    printf("较大值为: %d\n", maxVal);
    return 0;
}
4.5 回调函数
  • 定义:把函数a的地址作为参数传递给函数b,函数b就是回调函数。
  • 示例:使用回调函数给数组赋随机值
4.6 函数说明符
  • 内部函数(静态函数):使用static修饰,只能被定义它的文件调用。
  • 外部函数:默认或显式使用extern修饰,可被程序中的任何文件调用。
5. 变量
5.1 按声明位置的不同分类
  • 局部变量:在函数体内定义的变量或函数的形参,作用域仅限于定义它的函数或代码块内。
  • 全局变量:在函数外部定义的变量,作用域默认是整个程序。
5.2 按存储方式的不同分类
5.2.1 动态存储方式
  • 自动变量:在函数内部定义的局部变量,默认使用动态存储方式。它们在函数调用时自动分配内存,函数返回时自动释放内存。自动变量可以使用auto关键字声明(尽管这通常是隐含的,因为默认就是自动存储类)。
5.2.2 静态存储方式
  • 静态局部变量:使用static关键字声明的局部变量。它们在函数调用之间保持其值,不会被自动销毁和重新初始化。
  • 全局变量和静态全局变量:全局变量默认使用静态存储方式,在整个程序执行期间都保持其值。静态全局变量用static修饰,其作用域仅限于定义它的文件。
5.3 其它变量修饰符
5.3.1 寄存器变量
  • 寄存器变量:使用register关键字声明的变量。编译器会尝试将这些变量存储在CPU的寄存器中,以提高访问速度。然而,现代编译器通常能够自动优化变量存储位置,因此手动声明寄存器变量变得不那么必要。
5.3.2 extern修饰变量
  • 扩展全局变量的作用域:当全局变量不在文件开头定义时,可以使用extern关键字在引用点之前声明该变量,以扩展其作用域。
  • 跨文件共享全局变量:在多个文件中共享同一个全局变量时,在一个文件中定义该变量,在其他文件中使用extern声明它。
5.3.3 const修饰变量
  • 常量变量:使用const声明的变量在编译时就被赋予了一个固定的值,并且在程序的执行期间不能被修改。
  • 指向常量的指针const可以修饰指针所指向的值,确保这些值不会被修改。
  • 常量指针const也可以修饰指针本身,表示指针的值(即地址)不能被修改,但指针指向的值可以被修改(如果指针不是指向常量的话)。
  • 指向常量的常量指针:同时修饰指针和指针指向的值,确保两者都不能被修改。
  • 20
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值