第五弹:C语言基础——函数详解

目录

 函数的概述

1 函数的概念

2 函数的意义

 函数的定义和使用

1 函数的定义

2 函数的调用

2.1 在同一文件中函数定义后函数调用

2.2 在同一文件中函数定义前函数调用

2.3 调用其它文件中定义的函数

2.3.1 在函数调用文件中进行声明

2.3.2 在头文件中进行函数的声明

 函数的参数

1 函数传参

1.1 值传参

1.2 地址传参

1.3 全局变量传参

2 数组作为函数参数

3 主函数的参数

3.1 main函数的返回值

3.2 main函数的参数

 函数和指针

1 指针函数

2 函数指针

3 函数指针数组

4 函数指针作为函数的参数

 字符串处理函数

1 字符串

2 字符串处理函数

2.1 字符串长度计算——strlen

2.2 字符串拷贝——strcpy/strncpy

2.3 字符串的连接——strcat/strncat

2.4 字符串比较——strcmp/strncmp

2.5 字符串查找——strstr

3 字符串数据和数据类型数据转换处理函数

3.1 数字字符串数据转换为整型数据

3.2 基本数据的格式输出到字符数组空间

3.3 字符数组数据格式化到数据变量

4 字符串切割函数

知识点5 递归函数

 内存管理

1 内存管理的方式

1.1 静态内存管理

1.2 动态内存管理

2 动态内存管理实现

2.1 动态内存开辟和释放

2.2 野指针和内存泄漏

3 内存分别

 程序的编译过程


 函数的概述

1 函数的概念

所谓的函数,指的是具有特定功能的有序指令语句的集合,可以提供使用者调用以完成功能语句的执行。此时的功能语句块由设计者实现模块化编程设计,使用者通过函数的调用实现代码重用的效果。

函数的实质:是实现代码的模块化编程和代码重用的设计思想。

2 函数的意义

1) 函数的定义可以实现功能语句的模块化编程设计和代码重用;

2) 对于共用性的功能型语句通过函数的调用,简化程序设计的过程;

3) 简化项目的设计的框架或者逻辑;

4) 通过函数的模块化设计,以实现项目的分工协作。

 函数的定义和使用

1 函数的定义

存储类型 数据类型 函数名(形参列表)/* 称为函数头,花括号包含的部分称为函数体 */

{

函数体中的功能性有序指令集合;

return语句 ;

}

注意:

1. 存储类型:修饰整个函数的属性,只能使用关键字static

        1) 没有使用static关键字修饰,此时函数的作用域(或者链接属性)为整个程序域,也就是可以在当前程序中的任意位置访问;

        2) 有使用static关键字修饰,此时函数的作用域为文件域,也就是只能在当前定义函数的文件中访问;

2. 数据类型:修饰函数返回结果数据值的数据类型

        1) 如果函数不需要返回数据值,此时使用void数据类型表示;此时的return语句可以省略,也可以不省略但是只能使用(return ;)表示,不能跟返回结果;

        2) 如果函数需要返回数据值,使用返回的数据值的数据类型表示

                i. 返回实际数据值;

                ii. 返回值函数调用的状态;

                iii 返回值同时是数据和状态;

3. 函数名:提供给使用者访问有序执行指令集合的接口名称。

4. 形参列表:表示函数数据的输入和输出的参数

        1) 无参数,函数功能语句的执行不需要数据的输入和输出,此时使用void表示。

        2) 有参数:

                可以有一个或者多个形参构成

                每一个形参的组成:数据类型 形参变量名,需要注意形参变量为局部变量,在函数模块内有效;

                如果是多个形参,形参之间使用逗号分隔;

5. 函数体的部分:使用花括号{}所包含的有序指令语句的集合。

/* 定义无参无返回值的函数 */
void test1(void)
{
        return;        /* 此处的return语句可以省略;同时如果不省略的时候,不能接返回数据值 */
}

/* 定义有参有返回值的函数 */
int add(int a, int b)    /* 形参变量作用域为函数域 */
{
        return a+b;
}

2 函数的调用

所谓函数的调用,指的是使用者根据函数名访问函数,在调用的同时会根据函数形参列表传递实际参数的过程。

2.1 在同一文件中函数定义后函数调用

函数定义和函数调用在同一个文件中,并且函数调用在函数定义之后,此时可以直接函数调用。

#include <stdio.h>
/* 函数的定义,在定义的同时也完成函数的声明过程 */
int add(int a, int b)
{
        return a+b;
}

int main()
{
        int x = 3;
        int y = 4;
        int z;

        z = add(x, y);     /* 在同一个文件中,函数定义后实现函数的调用 */
    
        printf("z = %d\n", z); 
}

2.2 在同一文件中函数定义前函数调用

函数定义和函数调用在同一个文件中,函数的调用在函数定义之前,需要对函数进行声明之后在调用。

函数的声明:

1. 函数声明的目的

是告诉编译器,函数定义在其它地方已经完成,在当前文件中可以直接调用;

2. 函数声明的语法:

extern 数据类型 函数名(形参列表);

        1) extern 是对函数进行外部声明引用,可以省略;

        2) 形参列表中的形参变量名可以省略;

int sub(int a, int b);        /* 函数声明,可以等价于以下几种情况 */
#if 0
extern int sub(int a, int b);
extern int sub(int, int);
int sub(int, int);
#endif

int main()
{
        int x = 3;
        int y = 4;
        int z;

        z = sub(x, y);    /* 函数的调用 */
    
        printf("z = %d\n", z); 
}
/* 函数定义 */
int sub(int a, int b)
{
        return a-b;
}

2.3 调用其它文件中定义的函数

函数调用之前,需要对其它文件中所定义的函数进行函数声明

2.3.1 在函数调用文件中进行声明

根据所需要调用的函数,选择函数进行声明

#include <stdio.h>
/* 定义无参无返回值函数 */
void test1(void)
{
        printf("test1\n");
}

int add(int a, int b)
{
        return a+b;
}

int sub(int a, int b)
{
        return a-b;
}

2. 函数调用的源文件:app.c

#include <stdio.h>
/* 函数声明:对需要调用的函数在函数调用之前进行函数声明 */
void test1(void);
extern int add(int a, int b); 

int main()
{
        test1();

        printf("%d\n", add(5,2));
}

注意:程序的编译需要对函数定义和函数调用的源文件进行编译

gcc app.c func.c/* 编译成功默认生成的可执行程序文件为a.out */

2.3.2 在头文件中进行函数的声明

所定义的函数很多,且函数调用频率很高,设计者使用头文件进行函数的声明;对于使用者只需要包含头文件后直接进行函数的调用。

1. 函数定义的源文件:func.c

#include <stdio.h>

void test1(void)
{
        printf("test1\n");
}

int add(int a, int b)
{
        return a+b;
}

int sub(int a, int b)
{
        return a-b;
}

2. 函数声明的头文件:func.h

#ifndef _FUNC_H_
#define _FUNC_H_
/* 函数声明 */
void test1(void);
extern int add(int a, int b); 
extern int sub(int a, int b); 

#endif

头文件中注意:

         1) 防止头文件被重复展开标识符

#ifndef _FUNC_H_
#define _FUNC_H_
    /* 数据类型的定义,或者函数和变量的外部声明应用*/
#endif

        2) 在头文件中不能做数据类型变量的定义或者函数的定义,否则可能出现重复定义。

3. 函数调用的源文件:app.c

#include <stdio.h>    /* 头文件的包含:尖括号<>所包含的头文件是在系统环境变量路径中所能找到的头文件 */

#include "func.h"     /* 头文件的包含:双引号""所包含的头文件,在系统环境变量路径下不能找到,一般为自定义的头文件 */

int main()
{
        test1();

        printf("%d\n", add(5,2));
}

注意:程序的编译需要对函数定义和函数调用的源文件进行编译,不需要对函数声明的头文件进行编译

gcc app.c func.c/* 编译成功默认生成的可执行程序文件为a.out */

 函数的参数

1 函数传参

在函数定义的时候,可以给所定义的函数设置形式参数,在函数调用的时候,需要将相同数据类型的实际参数传递给形式参数,需要完成函数传参的问题。在C语言中函数的传参注意包含三种形式,分别为值传参、地址传参和全局变量传参。

1.1 值传参

所谓的值传参,指的是形参为数据类型变量,在函数调用的时候,将相同数据类型实参值传递给形参的过程。其实质将实参值赋值给形参变量值。

特定:

1) 值传参只能实现数据值的输入,不能实现数据值的输出;

2) 对于形参变量值的修改,不会影响实参数据值。

3) 对于函数的返回值,也是采用值传参实现。

4) 形参变量存储空间和实参变量存储空间之间无任何关联。

#include <stdio.h>


void setVal(int val)
{
        printf("val = %d\n", val);
        val = 100;    /* 对形参变量val值的修改不会影响实参变量a的数据值 */
        printf("val -> %d\n", val);
}

int main()
{
        int a = 1;

        setVal(a);    /* 函数的调用,将实参变量a的值赋值给形参变量val, 此时val和a存储空间无任务管理 */
        printf("a = %d\n", a); 
}

1.2 地址传参

所谓地址传参,指的是在形参为指针变量的时候,将实参数据的地址赋值给形参变量;此时形参指针变量便指向的空间为实参数据空间。

特定:

        1) 地址传参,可以实现数据的输入和输出。

        2) 通过形参地址的解引用访问的是实参,通过形参修改实参数据值;

        3) 形参为指针变量所指向空间为实参空间。

#include <stdio.h>
void Swap(int *p, int *q) 
{
        int temp;
        temp = *p;
        *p = *q;
        *q = temp;
}

int main()
{
        int x = 3;  
        int y = 5;


        printf("%d: x = %d, y = %d\n", __LINE__, x, y);    /* x = 3, y = 5 */
        Swap(&x, &y);
        printf("%d: x = %d, y = %d\n", __LINE__, x, y);     /* x = 5, y = 3 */
}

注意:

地址传参的使用,在需要在函数内部去修改实参数据值的时候使用。

如果需要修改的是数据变量,则使用数据变量相同数据类型的一级指针作为形参;

如果需要修改数据是指针变量,则使用指针变量数据类型的指针作为形参。

1.3 全局变量传参

所谓的全局变量传参,不是真正意义上利用参数实现数据的传递,实质是利用全局变量的属性实现数据值的传递过程。由于全局变量的作用域为整个程序域,所以可以在不同的函数模块内访问到全局变量的数据值。从而实现不同模块之间数据传递的过程。

特定:

        1) 全局变量传参,实现简单。

        2) 如果在函数模块内访问全局变量,则此时的模块依赖于全局变量的存在,模块的兼容性和可移植性会更低。

        3) 在一般情况下,不建议使用。

#include <stdio.h>
int m = 4;
int n = 5;

void SWAP()
{
        int temp;
        temp = m;
        m = n;
        n = temp;
}

int main()
{
        printf("%d: m = %d, n = %d\n", __LINE__, m, n);
        SWAP();
        printf("%d: m = %d, n = %d\n", __LINE__, m, n);
}

2 数组作为函数参数

数组的访问不能整体访问,只能通过数组名和元素序号逐一元素访问;

数组名[下标];下标范围:[0, 数组元素个数)

对于数组参数的传递,也不能整体传参;

#include <stdio.h>

extern void showArr(int a[10]);

int main()
{
        int arr[10] = {1,2,3,4,5,6,7,8,9,10};
        showArr(arr);
}

void showArr(int a[10])
{
        int i;
        for (i = 0; i < sizeof(a)/sizeof(a[0]); i++)
                printf("a[%d] = %d\n", i, a[i]);
}

注意:

程序的执行结果,不能将整个

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值