Bootstrap

指针的魔法世界:从字符指针到函数指针,解锁C语言的终极奥秘!

目录

1. 字符指针变量:不仅仅是字符的地址

1.1 字符指针与字符串常量

1.2 字符指针与字符数组的区别

2. 数组指针变量:指向数组的指针

2.1 数组指针的初始化

2.2 数组指针的使用

3. 二维数组传参的本质

4. 函数指针变量:指向函数的指针

4.1 函数指针的类型

5. 函数指针数组:实现“转移表”

6. 总结


1. 字符指针变量:不仅仅是字符的地址

字符指针变量是指向字符类型数据的指针,通常用 char* 表示。我们来看一个简单的例子:

int main() {
    char ch = 'w';
    char *pc = &ch;
    *pc = 'w';
    return 0;
}

 在这个例子中,pc 是一个字符指针,指向字符变量 ch 的地址。通过 *pc 我们可以修改 ch 的值。

1.1 字符指针与字符串常量

字符指针的另一种常见用法是用于指向字符串常量。例如:

int main() {
    const char* pstr = "hello bit.";
    printf("%s\n", pstr);
    return 0;
}

这里需要注意的是,pstr 并不是存储了整个字符串,而是存储了字符串 "hello bit." 的首字符 'h' 的地址。字符串常量在内存中是连续存储的,pstr 指向的是这个字符串的起始地址。

1.2 字符指针与字符数组的区别

我们来看一个经典的面试题:

#include <stdio.h>
int main() {
    char str1[] = "hello bit.";
    char str2[] = "hello bit.";
    const char *str3 = "hello bit.";
    const char *str4 = "hello bit.";
    if (str1 == str2)
        printf("str1 and str2 are same\n");
    else
        printf("str1 and str2 are not same\n");
    if (str3 == str4)
        printf("str3 and str4 are same\n");
    else
        printf("str3 and str4 are not same\n");
    return 0;
}

输出结果:

str1 and str2 are not same 
str3 and str4 are same

 

  • str1 和 str2 是两个不同的字符数组,虽然内容相同,但它们存储在内存中的不同位置,因此 str1 和 str2 不相等。

  • str3 和 str4 是两个字符指针,指向同一个字符串常量。C/C++ 会将相同的字符串常量存储在内存的同一位置,因此 str3 和 str4 相等

2. 数组指针变量:指向数组的指针

数组指针变量是指向数组的指针。与指针数组不同,数组指针变量本身是一个指针,指向一个数组。我们来看一个例子

int (*p)[10];

这里的 p 是一个数组指针,指向一个包含10个整型元素的数组。p 的类型是 int(*)[10],表示指向一个大小为10的整型数组的指针。

2.1 数组指针的初始化

数组指针变量用于存放数组的地址。我们可以通过以下方式初始化数组指针:

int arr[10] = {0};
int (*p)[10] = &arr;

 

这里 &arr 表示数组 arr 的地址,p 指向这个数组。

2.2 数组指针的使用

数组指针可以用于遍历二维数组。例如:

void printArray(int (*p)[5], int r, int c) {
    for (int i = 0; i < r; i++) {
        for (int j = 0; j < c; j++) {
            printf("%d ", *(*(p + i) + j));
        }
        printf("\n");
    }
}

int main() {
    int arr[3][5] = {{1, 2, 3, 4, 5}, {2, 3, 4, 5, 6}, {3, 4, 5, 6, 7}};
    printArray(arr, 3, 5);
    return 0;
}

 在这个例子中,p 是一个数组指针,指向一个包含5个整型元素的一维数组。通过 *(*(p + i) + j) 可以访问二维数组中的元素。

3. 二维数组传参的本质

在C语言中,二维数组的传参本质上是传递数组的地址。我们通常会将二维数组传递给函数,形参可以写成数组形式,也可以写成指针形式。例如:

void test(int a[3][5], int r, int c) {
    for (int i = 0; i < r; i++) {
        for (int j = 0; j < c; j++) {
            printf("%d ", a[i][j]);
        }
        printf("\n");
    }
}

 或者使用指针形式:

void test(int (*p)[5], int r, int c) {
    for (int i = 0; i < r; i++) {
        for (int j = 0; j < c; j++) {
            printf("%d ", *(*(p + i) + j));
        }
        printf("\n");
    }
}

 这两种方式都可以正确地传递二维数组,并且通过指针形式可以更清晰地理解二维数组的传参本质。

4. 函数指针变量:指向函数的指针

函数指针变量是指向函数的指针,它存储的是函数的地址。我们可以通过函数指针来调用函数。例如:

int Add(int x, int y) {
    return x + y;
}

int main() {
    int (*pf)(int, int) = Add;
    printf("%d\n", (*pf)(2, 3));  // 输出 5
    printf("%d\n", pf(3, 5));     // 输出 8
    return 0;
}

 

在这个例子中,pf 是一个函数指针,指向 Add 函数。通过 pf 我们可以调用 Add 函数。

4.1 函数指针的类型

函数指针的类型由函数的返回类型和参数类型决定。例如,int (*pf)(int, int) 表示 pf 是一个指向返回类型为 int,参数为两个 int 类型的函数的指针。

5. 函数指针数组:实现“转移表”

函数指针数组是一个数组,数组中的每个元素都是一个函数指针。我们可以通过函数指针数组来实现类似“转移表”的功能。例如:

int add(int a, int b) { return a + b; }
int sub(int a, int b) { return a - b; }
int mul(int a, int b) { return a * b; }
int div(int a, int b) { return a / b; }

int main() {
    int (*p[5])(int, int) = { 0, add, sub, mul, div };
    int x, y, input = 1, ret = 0;
    do {
        printf("1:add 2:sub 3:mul 4:div 0:exit\n");
        scanf("%d", &input);
        if (input >= 1 && input <= 4) {
            printf("输入操作数: ");
            scanf("%d %d", &x, &y);
            ret = (*p[input])(x, y);
            printf("ret = %d\n", ret);
        }
    } while (input);
    return 0;
}

 

6. 总结

通过本篇博文,我们深入探讨了字符指针、数组指针、二维数组传参的本质、函数指针以及函数指针数组的使用。指针是C语言中非常强大的工具,理解指针的用法可以帮助我们编写更高效、更灵活的代码。希望本文的内容能够帮助你更好地理解指针的高级用法。

;