指针的奇技淫巧三_指针和函数

程序的堆栈

堆和栈的区别其实不用过于的去强调他们了,我感觉理解下概念就好,比如程序哪些操作会在堆里,哪些会分配到栈里,它们实际使用中的差别什么的就好了

堆区:程序员自己申请,声明大小,释放
栈区:系统自动分配,自动释放

通过指针传递和返回数据

作为一个phper来说function的返回值直接一个return就解决问题了,但是C语言的函数返回就多了一些讲究和细节在里面。

用指针传递数据

用指针传递数据的根本原因是为了让函数可以修改数据,并二次调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <stdio.h>

void swap(int* p1, int *p2){
int tmp;
tmp = *p1;
*p1 = *p2;
*p2 = tmp;

}

int main(){

int n1 = 100;
int n2 = 200;

swap(&n1, &n2);
printf("n1:%d\nn2:%d\n",n1,n2);
}

执行结果如下

用指针传递数据

swap函数通过指针操作内存地址的特性,修改了n1,n2内存地址在栈上保存的数据

指针交换内容演示

用值传递内容

这种方式是一种错误的演示方式,对新手有一定的迷惑性。我们要明白swap中的n1,n2只是形参,作用域也仅仅是在swap内,main中的n1,n2是实参他们没有半毛钱的关系哦!!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <stdio.h>

void swap(int n1, int n2){
int tmp;
tmp = n1;
n1 = n2;
n2 = tmp;

printf("n1:%d\nn2:%d\n",n1,n2);
printf("\n------\n");
}

int main(){

int n1 = 100;
int n2 = 200;

swap(n1, n2);
printf("n1:%d\nn2:%d\n",n1,n2);
return 0;
}

内存分配如下图所示

用值传递内容

传递指向常量的指针

传递指向常量的指针是C语言中常用的套路,因为它效率很高,我们只传了数据的地址,能避免某些情况下的复制大量内存,并且常量指针可以避免传递的数据被修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <stdio.h>

void increment(const int* n1, int* n2){

*n2 = *n1 + 1;

}

int main(){

int n1 = 100;
int n2 = 0;

increment(&n1, &n2);
printf("n1:%d\nn2:%d\n",n1,n2);
return 0;
}

输出结果如下

常量指针正确输出结果

如果我们尝试去修改这个常量会发生什么呢?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <stdio.h>

void increment(const int* n1, int* n2){

*n1 = *n1 + 1;//这里尝试修改

}

int main(){

int n1 = 100;
int n2 = 0;

increment(&n1, &n2);
printf("n1:%d\nn2:%d\n",n1,n2);
return 0;
}

当然是会直接报错哦~~

尝试修改常量的报错提示

返回指针

返回指针很容易,只要返回的类型是相同的数据类型即可,从函数返回对象经常用到下面两种技术

  • 使用malloc在函数内部分配内存,返回地址,调用者释放返回内存
  • 传递一个对象给函数并让函数修改它,这样分配内存和释放内存都是调用者负责
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <stdio.h>
#include <stdlib.h>
int* initArray(int size, int value){

int* array = (int*)malloc(sizeof(int) * size);

for (int i = 0; i < size; ++i) {
array[i] = value;
}

return array;
}

int main(){

int* array = initArray(5,45);

for (int i = 0; i < 5; ++i) {
printf("%d\n",array[i]);
}

free(array);
return 0;
}

执行结果
指针的指针

注意:在initArray 内部的实参在函数运行完成后都会被系统自动回收掉,但是我们malloc分配的内存是自己分配处理的。initArray返回的是内存地址

局部数据指针(错误的演示)

这个演示给只懂phper的程序员来说看不出任何问题,因为PHP就是这样完成数据传递的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <stdio.h>
#include <stdlib.h>
int* initArray(int size, int value){

int array[size]; //这块是使用错误的地方

for (int i = 0; i < size; ++i) {
array[i] = value;
}

return array;
}

int main(){

int* array = initArray(5,45);

for (int i = 0; i < 5; ++i) {
printf("%d\n",array[i]);
}

free(array);
return 0;
}

局部数据指针

在部分编译器中能通过,但是本地测试的时候是不通过的。大概的意思是initArray中的数据全部是在程序栈中完成的,一旦函数返回旧GG了,栈针全部弹出虽然空间返还给程序内存,但是其它代码可能会复用这一快区域造成不可预期的结果。有一种解决方法是把array设置为static这一可以把作用域限制在函数内部,栈针在函数外面,避免其它函数覆盖,但是这样做有很多问题还是不建议使用。

传递指针的指针

传递指针的指针时函数内部不需要返回值,但是需要定义一个二维指针,二维指针这要好好理解下。稍不注意就理解的没这彻底了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <stdio.h>
#include <stdlib.h>

void initArray(int** array, int size, int value){


*array = (int*)malloc(sizeof(int)*size);//动态分配传入的空指针

if(*array != NULL){
for (int i = 0; i < size; ++i) {
//*array[i] = value; 这种写法是错误的,*array是传入array的内存地址
*(*array+i) = value;//给内存地址做偏移
}
}

}

int main(){

int* array = NULL;//初始化一个空指针
initArray(&array,5,45);

for (int i = 0; i < 5; ++i) {
printf("%d\n",array[i]);
}

free(array);
return 0;
}

指针的指针

需要注意的2点

  • initArray 传入的第一个参数是再外部定义好的空指针
  • 内存做偏移是 *(*array+i)

initArray 内部的指针讲解

*array main函数传入的 array的内存地址
*(*array) 第一段main传入的值
*(*array+i) main传入的array内存地址偏移i的内存存储值

函数指针

函数指针是持有函数地址的指针,类似于别名但是不是别用,有点像PHP里的依赖注入。通过类名称反射找到需要执行的类函数,但是这里不是,这里是调用函数的内存地址。

声明函数指针

void(*foo)();

void (*foo) ();
返回类型 函数指针变量名字 参数

我们对函数指针在命名约定上的建议是用fptr做前缀

命令 说明
int *f4(); f4是一个函数返回整数指针
int (*f5)(); f5是一个返回整数的函数指针
int* (*f6)(); f6是一个返回整数指针的函数指针

使用函数指针

1
2
3
4
5
6
7
8
9
10
11
12
13
#include<stdio.h>

int square(int num){//定义一个函数方法
return num;
}

int main(){
int (*fptr1)(int);//返回整数的函数指针
fptr1 = square;//fptr1 = &square
int num = 1;
printf("%d\n", fptr1(num));
return 0;
}

传递函数指针

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include<stdio.h>

int add(int num1, int num2){
return num1 + num2;
}

int sub(int num1, int num2){
return num1 - num2;
}

typedef int(*fptrOperation)(int,int);

int compute(fptrOperation operation, int num1, int num2){
return operation(num1, num2);
}

int main(){
printf("%d\n",compute(sub,5,6));
}

返回函数指针

返回指针需要把函数的返回类型声明为函数指针

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include<stdio.h>

int add(int num1, int num2){
return num1 + num2;
}

int sub(int num1, int num2){
return num1 - num2;
}

typedef int(*fptrOperation)(int,int);


fptrOperation select(char opcode){//返回格式定义为自定义函数指针

switch (opcode){
case '+':
return add;
case '-':
return sub;
default:
return add;
}
}

int compute(char opcode, int num1, int num2){
fptrOperation function = select('-');//类似工厂模式
return function(num1, num2);
}


int main(){
printf("%d\n",compute('+',5,6));
}

##函数指针数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include<stdio.h>


int add(int num1, int num2){
return num1 + num2;
}

int sub(int num1, int num2){
return num1 - num2;
}

typedef int(*Operation)(int,int);


Operation function[66] = {NULL};//函数指针数组

void setFunction(){

function['+'] = add;//这里的add是函数的指针地址
function['-'] = sub;
}

int compute(char opcode, int num1, int num2){
Operation functionName = function[opcode];
return functionName(num1, num2);
}


int main(){
setFunction();
printf("%d\n",compute('+',5,6));
}

  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!

请我喝杯咖啡吧~