指针和函数
指针和函数
苏丙榅函数和指针在 C/C++ 中常常一起使用,指针可以用于传递函数参数、返回函数结果或者作为函数的返回值。这样做可以实现更灵活和高效的程序设计。
下面是一些常见的函数和指针的使用方式:
1. 指针做函数参数
1.1 参数为变量
可以将指针作为函数的参数,从而在函数内部直接访问并修改指针所指向的变量。这样可以避免在函数调用时进行变量的拷贝,提高程序的运行效率。
需求:编写一个函数实现两个变量之间的值的交换
1 |
|
程序输出的结果如下:
1 | x = 5, y = 3 |
在上面代码中一共编写了两个交换函数swap1
和swap2
,参数个数相同但是类型不同,通过输出的结果可以看出swap2
函数完成了变量之间的数据交换,swap1
没有,其主要原因是这样的:
swap1
函数的参数是数值,参数传递的是值,在传递过程中实参会发生拷贝,此时形参和实参变量对应的内存地址是不同的,在函数体内部交换的是参数的值,而不是外部实参的值swap2
函数的参数是指针,参数传递的是地址,形参指针指向的地址和实参变量的地址是相同的,因此在函数体内部通过形参指针就可以交换实参变量内部的数值了。
1.2 参数为数组
数组名也可以做函数参数,但是数组类型的形参最终会退化为指针:
1 | //void printArrary(int a[10], int n) |
通过上面的示例代码可以得知,数组做函数参数的时候,对于形参而言有三种写法:
- 指定为数组类型,并且指定了数组的最大容量,必须和实参容量保持一致
void printArrary(int a[10], int n)
- 指定为数组类型,没有指定数组的最大容量,需要通过实参进行推导
void printArrary(int a[], int n)
- 指定为指针类型,通过形参指针指向实参数组的地址
void printArrary(int* a, int n)
由于数组类型的形参最终会退化为指针
,所以有一个细节需要额外注意:在函数体内部不能通过运算符 sizeof(形参) 的方式对数组进行内存大小的计算,因为这样计算出的是指针自身所占用的内存大小(得到的结果是4字节或者8字节),而我们想要知道的是指针指向的内存的大小。所以在进行数组传递的时候,都会给函数添加一个额外的整形参数用于标记当前这个数组的容量。
2. 指针做函数返回值
函数可以返回指针,这样可以在函数外部获取指针所指向的数据。需要额外注意返回的指针指向的内存的生命周期。
2.1 返回局部数据
局部数据是在函数内部定义的变量,它们的作用范围被限制在所在的函数块内。局部数据只在函数执行期间存在,并且在函数执行结束时会自动被销毁,释放相关的内存空间。
以下是一个示例,展示了局部数据的使用:
1 |
|
执行上面的程序打印出的结果有两种:正确
或者错误
。上面的程序有一个隐藏的Bug:getValue 函数内部的 number 是一个局部变量,函数调用结束之后,这个变量对应的存储空间也就被释放了,此时函数调用者通过函数返回值拿到的地址就是非法的,这块地址随时都可能被分配给其它的变量并且被写入新的值,所以我们通过指针取出的数据可能正确也可能错误。
2.2 返回全局数据
全局数据是在函数外部定义的变量,它们在整个程序的执行期间都是有效的,其作用范围跨越了多个函数的调用。
以下是一个示例,展示了全局数据的使用:
1 |
|
上面的代码中通过使用全局变量,解决了变量内存生命周期问题,但仔细思考一下会发现这么写没有任何意义可言,全局部变量可以直接使用,完全不需要通过返回值的方式将其传递给函数调用者,在后面的章节中会给大家详细讲解如何动态申请存储空间,这样函数返回指针就变得有意义了。
3. 函数指针
函数指针是指向函数的指针变量。它们可以用于动态地调用不同的函数,实现程序的灵活性和可扩展性。
以下是一个示例,展示了函数指针的使用:
1 |
|
在上述示例中,我们首先定义了一个函数指针 funcPtr
,它指向一个具有两个整型参数和整型返回值的函数。然后,我们将 funcPtr
分别指向 add
和 subtract
函数,并使用函数指针调用相应的函数。
需要注意的是,对于函数指针变量,我们需要显式地使用 &
取得函数的地址。然而,在大多数情况下,可以直接将函数名赋给函数指针,编译器会自动进行地址获取。
函数指针可以用于实现回调机制,使函数能够动态地选择要执行的代码段。此外,函数指针还可以在实现函数库、处理函数指针数组等方面发挥重要作用。需要注意的是,函数指针的类型必须与所指向的函数的类型保持一致,包括参数类型和返回值类型。
函数指针的灵活性使得我们能够编写更加通用和可扩展的代码,动态地适应不同的应用场景。