Skip to content

指针型函数与函数型指针

博客园链接

存储区域划分

  1. 栈 Stack 可读可写,存储程序运行时函数或代码中的局部变量(非 static 变量),编译器自动分配和释放。栈属于动态内存分配,它的生存期为:代码块运行就分配空间,代码块结束,就自动回收空间
  2. 堆 Heap 可读可写,存储程序运行时被程序员动态分配的内存段,它的大小并不固定,可动态扩张或缩减。堆属于动态内存分配,主要通过 malloc, calloc, realloc, free 等方法来管理
  3. 代码段 Code Segment / Text Segment 一般只读,存储程序代码指令,还有可能包含一些只读的常数变量,例如字符串常量等。代码段的空间大小在代码运行前就已经确定,其空间分配从可执行文件中加载而来,属于静态内存分配。
  4. 数据段 Data Segment 可读可写,存储初始化的(全局和局部范围的)静态变量,数据段的空间分配从可执行文件中加载而来,其空间大小运行时不会改变,属于静态内存分配。
  5. ROdata Segment (Read-Only data Segment) 只读,存储只读数据,比如字符串常量、静态常量、const 修饰的变量、字面量以及预编译指令(#define 等)定义的常量
  6. BSS Segement (Block Started by Symbol) 可读可写,属于静态内存分配,存储全局范围(file scope)中未初始化的变量和常量,以及局部范围中未初始化的静态(static)变量,还包括被显示地初始化为 0 的全局变量和静态变量

附图 Linux x86-64 run-time memory image

Linux x86-64 run-time memory image

函数 Function

在 C 语言中,函数名也称为函数的指针,函数名表示的就是函数体的首地址

指针型函数 Pointer Function

定义

指针型函数(a function that returns a pointer),其本质是个函数,但是这种函数的返回值为指针类型的数据,即内存中的某一个地址,简单来说需要在函数体中返回一个指针变量。指针型函数声明或定义的时候需要遵循的规则为:函数返回值类型* 函数名(函数形参列表)

注意:在返回指针变量时,该指针变量不能够指向函数体内部的局部变量,因为函数体内的局部变量是存放在栈 Stack 当中的,函数在执行结束后,其内存会被编译器自动释放,该指针变量指向的内容将会变得不确定。举个例子:

 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
#include <stdio.h>

void correct_get_str(char* text)
{
    sprintf(text, "%s", "correct");
}

char* wrong_get_str()
{
    char text[100] = { '\0' };
    sprintf(text, "%s", "wrong");
    return text;
}

int main()
{
    printf("============Wrong Get Str===========\n");
    char* wrong = wrong_get_str();
    printf("%s\n", wrong);
    printf("============Correct Get Str===========\n");
    char text[100] = {'\0'};
    correct_get_str(text);
    printf("%s\n", text);
    return 0;
}

运行结果:

============Wrong Get Str===========
烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫
============Correct Get Str===========
correct

错误的用法是在函数中定义一个指针变量,并初始化其内容,在用指针型函数返回该指针变量,而正确的用法是在函数的形参中声明一个指针变量,并在函数体内部改变,调用该函数时,传入在函数外部定义的指针变量实参,或者是在函数体内使用 malloc 控制堆内存,但是需要注意内存的分配与释放

函数型指针 Function Pointer

定义

函数型指针(a pointer-to-function),本质是一个指针,其形式与其他数据类型的指针类似,但是这种指针是指向某一个函数,其功能是通过该指针调用所指向的函数。函数型指针声明或定义的时候需要遵循的规则为:函数返回值类型 (*指针名)(函数形参列表),其中

  • 函数返回值类型: 需要与待指向函数的返回值类型一致
  • (*指针名): 前后必须有括号,否则*属于函数返回值类型,则声明变为函数返回值类型* 函数名(函数形参列表),是一个指针型函数
  • 函数形参列表: 必须写出函数形参的类型,需要与待指向函数的形参类型一致,可以选择不写出形参的名称

函数型指针的使用方式,第 1 步指向函数:指针名 = 函数名,第 2 步使用指针名调用函数:指针名(实参列表)或者(*指针名)(实参列表)

指针型函数与函数型指针的使用实例

 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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#include <stdio.h>

//普通函数
int print(int i)
{
    printf("%d\n", i);
    return i;
}

//指针型函数,返回值为指针类型的数据
int* pointer_function(int* i)
{
    printf("%d\n", *i);
    return i;
}

//函数型指针,指向函数的指针,与被指向函数的返回值类型和参数完全一致
//注意(*function_pointer1)的括号必须要加上
int (*function_pointer1)(int);

//参数位函数型指针,使用方法与function_pointer1类似
int function_pointer2(int (*print)(int))
{
    print(2);
    return 2;
}

//参数位函数型指针,此处不写*也可以,参数类型与function_pointer3相同
int function_pointer3(int print(int))
{
    print(3);
    return 3;
}

int main()
{
    printf("指针型函数\n");
    printf("Pointer Function\n");
    int a = 1;
    int* pointer = pointer_function(&a);
    printf("================================\n");
    printf("函数型指针\n");
    printf("Function Pointer 1\n");
    function_pointer1 = print;
    //调用所指方法的两种方式
    function_pointer1(1);
    (*function_pointer1)(1);
    printf("Function Pointer 2\n");
    function_pointer2(print);
    printf("Function Pointer 3\n");
    function_pointer3(print);
    return 0;
}

运行结果:

指针型函数
Pointer Function
1
================================
函数型指针
Function Pointer 1
1
1
Function Pointer 2
2
Function Pointer 3
3

在 VS2019 中通过 Debug,查看指针型函数与函数型指针的类型:

指针型函数与函数型指针的类型

  • pointer_funtion指针型函数,其类型为int* (int *),返回值是一个int指针
  • funtion_pointer1函数型指针,其类型为int (*)(int),指向函数printprint的类型为int (int)
  • funtion_pointer2形参int (*print)(int)为函数型指针,其本身类型为int (int (*)(int)),形参类型为 int (*)(int)
  • funtion_pointer3形参int print(int)也是函数型指针,与 funtion_pointer2 完全一致,此处(*print)print没有区别,因为函数名就是函数的指针

Comments