指针与函数

指针与函数

指针做函数参数

参数为变量

可以将指针作为函数的参数,从而在函数内部直接访问并修改指针所指向的变量。最经典的就是数值交换函数的编写。

#include <stdio.h>

void swap1(int x, int y)
{
    int tmp;
    tmp = x;
    x = y;
    y = tmp;
    printf("x=%d,y=%d\n", x, y);
    printf("addr_x=%p,addr_y=%p\n", &x, &y);
}

void swap2(int* x, int* y)
{
    int tmp;
    tmp = *x;
    *x  *y;
    *y = tmp;

    printf("*x=%d,*y=%d\n", *x, *y);
    printf("addr_x=%p,addr_y=%p\n", x, y);
}

int main()
{
    int a = 3;
    int b = 5;
    printf("before swap1, a=%d, b=%d, addr_a:%p, addr_p:%p\n", a, b, &a, &b);
    swap1(a, b); // 值传递
    printf("after swap1, a=%d, b=%d\n", a, b);

    a = 3;
    b = 5;
    printf("before swap2, a=%d, b=%d, addr_a:%p, addr_p:%p\n", a, b, &a, &b);
    swap2(&a, &b); // 地址传递
    printf("after swap1, a=%d, b=%d\n", a, b);

    return 0;
}

/*
before swap1, a=3, b=5, addr_a:0x7ffee70759e0, addr_p:0x7ffee70759e4
x=5,y=3
addr_x=0x7ffee70759bc,addr_y=0x7ffee70759b8
after swap1, a=3, b=5
before swap2, a=3, b=5, addr_a:0x7ffee70759e0, addr_p:0x7ffee70759e4
*x=5,*y=3
addr_x=0x7ffee70759e0,addr_y=0x7ffee70759e4
after swap1, a=5, b=3
*/

在上面代码中一共编写了两个交换函数swap1和swap2,参数个数相同但是类型不同,通过输出的结果可以看出swap2函数完成了变量之间的数据交换,swap1没有,其主要原因是这样的:

  1. swap1函数的参数是数值,参数传递的是值,在传递过程中实参会发生拷贝,此时形参和实参变量对应的内存地址是不同的,在函数体内部交换的是参数的值,而不是外部实参的值

  2. swap2函数的参数是指针,参数传递的是地址,形参指针指向的地址和实参变量的地址是相同的,因此在函数体内部通过形参指针就可以交换实参变量内部的数值了。

参数为数组

在C语言中,数组名在大多数情况下会被转换为指向数组首元素的指针。因此,当数组作为函数参数时,实际上传递的是指向数组第一个元素的指针。数组作为函数参数传递有2大重要特性:

  1. 大小信息丢失

    • 数组作为参数传递时会"退化"为指针,sizeof运算符无法获取数组大小

    • 必须额外传递数组大小参数

  2. 修改影响原数组

    • 函数内对数组元素的修改会影响原数组

    • 因为传递的是指针,不是数组的副本

一维数组作为函数参数

#include <stdio.h>

void printArray(int arr1[], int size1, int *arr2, int size2) 
{
    for(int i = 0; i < size1; i++)
    {
        printf("%d ", arr1[i]);
    }
    printf("\n");
    arr1[4] = 15;
    for(int j = 0; j < size2; j++)
    {
        printf("%d ", arr2[j]);
    }
    printf("*arr2 size:%ld\n", sizeof(arr2));
}

int main()
{
    int str1[5] = {1,2,3,4,5};
    int str2[3] = {10, 20, 30};
    printArray(str1, 5, str2, 3);
    printf("str1[4]:%d\n", str1[4]);
}
/*
1 2 3 4 5 
10 20 30 *arr2 size:8
str1[4]:15
*/

数组作为函数入参时,int arr[]int *arr是等价的,两种方式都是以指针的形式传入数组,因此函数内做的修改,都可以保留到函数外。如果如果不希望函数修改数组内容,可以使用const限定符。

多维数组作为函数参数

对于多维数组,需要指定除第一维外的所有维度大小:

#include <stdio.h>

void print2DArray(int arr1[][4], int rows1, int (*arr2)[3], int rows2) 
{
    for(int i = 0; i < rows1; i++) 
    {
        for(int j = 0; j < 4; j++) 
        {
            printf("%d ", arr1[i][j]);
        }
        printf("\n");
    }
    arr1[1][1] = 12;

    for(int m = 0; m < rows2; m++)
    {
        for(int n = 0; n < 3; n++)
        {
            printf("%d ", arr2[m][n]);
        }
        printf("\n");
    }

    arr2[1][1] = 15;
}

int main() {
    int matrix1[3][4] = {{1,2,3,4}, {5,6,7,8}, {9,10,11,12}};
    int matrix2[2][3] = {{11,12,13}, {15,16,17}};
    print2DArray(matrix1, 3, matrix2, 2);
    printf("matrix1[1][1]:%d, matrix2[1][1]:%d\n", matrix1[1][1], matrix2[1][1]);
    return 0;
}
/*
1 2 3 4 
5 6 7 8 
9 10 11 12 
11 12 13 
15 16 17 
matrix1[1][1]:12, matrix2[1][1]:15
*/

指针函数与函数指针

指针函数

指针函数(Pointer Function)是指返回值类型为指针的函数,即该函数返回一个指针(内存地址)。它本质上是一个函数,只不过它的返回值是一个指针,而不是普通的数据类型(如 intfloat 等)。格式如下:

返回类型 *函数名(参数列表) {
    // 函数体
    return 指针;  // 返回一个地址
}

测试用例

#include <stdio.h>
#include <stdlib.h>
struct Node {
    int data;
    struct Node *next;
};
struct Node *create_node(int data);
int *get_max(int *a, int *b);
int main()
{
    struct Node *p1, *p2;
    p1 = create_node(1);
    p2 = create_node(2);
    int *max_num = get_max(&p1->data, &p2->data);
    printf("p1_data:%d, p2_data:%d, max:%d\n", p1->data, p2->data, *max_num);
    int x = 5;
    int y = 6;
    int *max = get_max(&x, &y);
    printf("max:%d\n", *max);
    free(p1);
    free(p2);
}

//返回指向较大值的指针
int *get_max(int *a, int *b) 
{
    if(*a > *b) 
    {
        return a;
    } 
    else 
    {
        return b;
    }
}

struct Node *create_node(int data) 
{
    struct Node *new_node = (struct Node *)malloc(sizeof(struct Node));
    new_node->data = data;
    new_node->next = NULL;
    return new_node;
}
/*
p1_data:1, p2_data:2, max:2
max:6
*/

在上面的例子中,我们通过指针函数返回了入参的指针和动态分配内存的指针。

注意事项

#include <stdio.h>

int y = 20;

int *return_global_num()
{
    return &y;
}

int *return_static_num()
{
    //静态变量的生命周期是到程序结束,而普通变量生命周期是到函数结束,这里不能用普通变量
    static int x = 10;
    return &x;
}

char* return_const_num()
{
    return "abcd";
}

int main()
{
    int *a = return_static_num();
    int *b = return_global_num();
    char* c = return_const_num();
    printf("a:%d, b:%d, c:%s\n", *a, *b, c);
}
/*
a:10, b:20, c:abcd
*/

在使用指针函数时注意,比如在return_static_num函数中,如果使用的是局部变量是不行的。局部变量放在栈上面,函数调用结束后会被回收,返回它的指针会导致 悬垂指针(Dangling Pointer),访问它可能引发未定义行为(UB)

函数指针

函数指针(Function Pointer)是指向函数的指针变量,它存储的是函数的入口地址,可以通过该指针间接调用函数。函数指针最常用的就是回调机制。其格式如下:

返回类型 (*指针名)(参数列表);
int (*func_ptr)(int, int);  // 定义一个函数指针,指向返回 int,接受两个 int 参数的函数

回调函数

回调函数是一种通过函数指针实现的编程机制,它允许将一个函数作为参数传递给另一个函数,并在适当的时候被调用。其工作原理如下:

  1. 定义回调函数:编写一个符合特定签名的函数。

  2. 注册回调函数:将函数指针传递给另一个函数(如库函数)。

  3. 触发回调:在特定条件满足时(如事件发生),调用注册的函数。

#include <stdio.h>

// 定义回调函数类型
typedef void (*Callback)(int);

// 接收回调函数作为参数
void process_data(int data, Callback callback) {
    printf("处理数据: %d\n", data);
    callback(data * 2);  // 触发回调
}

// 实际回调函数
void print_result(int result) {
    printf("回调结果: %d\n", result);
}

int main() {
    process_data(10, print_result);  // 传递回调函数
    return 0;
}
/*
处理数据: 10
回调结果: 20
*/

区别

在声明时,函数指针的*号被括号括起来,这是区分两者的关键语法特征。

特征 指针函数 函数指针
本质 函数 指针
声明形式 int *func(...) int (*func_ptr)(...)
用途 返回指针 指向函数
调用方式 直接调用 通过指针间接调用
posted @ 2025-05-25 20:52  随机人生  阅读(20)  评论(0)    收藏  举报