目录

引言

第一阶段:🔍 独特的字符指针

什么是字符指针?

字符指针的用途

演示:使用字符指针拷贝字符串

字符指针与字符串常量

小试牛刀

第二阶段:🎯 玩转指针数组

指针数组是什么?

指针数组的用途

演示:创建和使用指针数组

第三阶段:🎯 探索数组指针的神奇之旅

数组指针:是指针还是数组?

数组指针的定义

解释数组指针的应用

小试牛刀

int *p1[10];:指针数组

int (*p2)[10];:数组指针

&数组名 与 数组名:剖析引用与地址

&数组名:取地址操作

数组名:首元素的指针

比较一下

代码解释

数组指针的使用

print_arr1 函数解释

print_arr2 函数解释

练习

第四阶段:🔗 数组参数、指针参数的传递方式

一维数组传参

二维数组传参

一级指针传参

二级指针传参

第五阶段:🔗 探索函数指针的神奇世界

函数指针简介

使用函数指针

函数指针数组

指向函数指针数组的指针

第六阶段:🔗 回调函数:让程序更灵活

回调函数简介

回调函数的应用

结语:尖叫指针,飞跃编程界!🚀


引言

第一阶段:🔍 独特的字符指针

什么是字符指针?

字符指针的用途

演示:使用字符指针拷贝字符串

让我们通过一个示例来演示如何使用字符指针拷贝一个字符串。以下是一个自定义的字符串拷贝函数:

#include <stdio.h>

void stringCopy(char *dest, const char *src) {
    while (*src != '\0') {
        *dest = *src;
        dest++;
        src++;
    }
    *dest = '\0';  // 添加字符串结尾的空字符
}

int main() {
    char source[] = "Hello, World!";
    char destination[20];

    stringCopy(destination, source);
    printf("Copied string: %s\n", destination);

    return 0;
}

在这个例子中,我们使用字符指针 srcdest 分别指向源字符串和目标字符串。通过逐个字符地拷贝,我们可以将源字符串的内容复制到目标字符串中,然后在末尾添加一个空字符

字符指针与字符串常量

#include <stdio.h>

int main() {
    const char *message = "Hello, C Pointers!";
    printf("Message: %s\n", message);

    return 0;
}

小试牛刀

#include <stdio.h>

int main() {
    char str1[] = "Hello, World!";
    char str2[] = "Hello, World!";
    const char *str3 = "Hello, World!";
    const char *str4 = "Hello, World!";

    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;
}

在这段代码中,我们比较了字符数组和指向字符串常量的字符指针之间的区别。以下是代码的详细解释:

第二阶段:🎯 玩转指针数组

指针数组是什么?

指针数组的用途

指针数组在许多编程场景中都扮演着关键角色。它的灵活性使得它在多种情况下都非常有用:

演示:创建和使用指针数组

让我们通过一个示例来演示如何创建和使用指针数组,并展示它在字符串数组中的应用:

#include <stdio.h>

int main() {
    char *fruits[] = {
        "Apple",
        "Banana",
        "Orange",
        "Grapes"
    };

    int numFruits = sizeof(fruits) / sizeof(fruits[0]);

    for (int i = 0; i < numFruits; i++) {
        printf("Fruit %d: %s\n", i + 1, fruits[i]);
    }

    return 0;
}

在这个例子中,我们创建了一个指针数组 fruits其中的每个元素都是指向字符串的指针。我们使用花括号初始化语法,将几个水果名字的字符串赋值给指针数组。然后,我们通过循环遍历指针数组,并打印出每个水果的名称。

第三阶段:🎯 探索数组指针的神奇之旅

数组指针:是指针还是数组?

数组指针的定义

解释数组指针的应用

小试牛刀

在该小节,我们将深入探索两种看似相似但实际截然不同的指针类型:int *p1[10];int (*p2)[10];。虽然它们都涉及指针和数组,但它们的应用和含义却截然不同。

int *p1[10];:指针数组

int (*p2)[10];:数组指针

注意:[]的优先级要高于*号的,所以必须加上()来保证p先和*结合。

&数组名 与 数组名:剖析引用与地址

&数组名:取地址操作

数组名:首元素的指针

比较一下

  • &数组名:表示整个数组的起始地址,是指向数组的指针。
  • 数组名:指向数组首元素的指针,可以用来表示整个数组或访问数组元素。

代码解释

#include <stdio.h>

int main() {
    int arr[10] = {0};
    printf("%p\n", arr);   // 打印数组首元素的地址
    printf("%p\n", &arr);  // 打印整个数组的起始地址
    return 0;
}

指针进阶大冒险:解锁C语言中的奇妙世界!-LMLPHP

再来一个

 

#include <stdio.h>

int main() {
    int arr[10] = {0};
    printf("arr = %p\n", arr);       // 打印数组首元素的地址
    printf("&arr = %p\n", &arr);     // 打印整个数组的起始地址
    printf("arr+1 = %p\n", arr+1);   // 打印数组第二个元素的地址
    printf("&arr+1 = %p\n", &arr+1); // 打印下一个数组的起始地址
    return 0;
}

指针进阶大冒险:解锁C语言中的奇妙世界!-LMLPHP

数组指针的使用

看一段代码.

#include <stdio.h>

// 打印二维数组,使用数组作为参数
void print_arr1(int arr[3][5], int row, int col) {
    int i, j;
    for(i = 0; i < row; i++) {
        for(j = 0; j < col; j++) {
            printf("%d ", arr[i][j]);
        }
        printf("\n");
    }
}

// 打印二维数组,使用数组指针作为参数
void print_arr2(int (*arr)[5], int row, int col) {
    int i, j;
    for(i = 0; i < row; i++) {
        for(j = 0; j < col; j++) {
            printf("%d ", arr[i][j]);
        }
        printf("\n");
    }
}

int main() {
    int arr[3][5] = {
        {1, 2, 3, 4, 5},
        {6, 7, 8, 9, 10},
        {11, 12, 13, 14, 15}
    };

    printf("Printing using print_arr1:\n");
    print_arr1(arr, 3, 5);
    
    printf("\nPrinting using print_arr2:\n");
    print_arr2(arr, 3, 5);
    
    return 0;
}

print_arr1 函数解释

void print_arr1(int arr[3][5], int row, int col)

print_arr2 函数解释

void print_arr2(int (*arr)[5], int row, int col)

值得注意的是,尽管在 print_arr2 中,我们传递的是 arr,但实际上传递的是指向第一行的指针。这是因为在 C 语言中,数组名作为函数参数传递时,会退化为指向数组首元素的指针。因此,print_arr2 函数可以通过指针来遍历整个二维数组。

练习

int arr[5];
int *parr1[10];
int (*parr2)[10];
int (*parr3[10])[5];

第四阶段:🔗 数组参数、指针参数的传递方式

一维数组传参

#include <stdio.h>

void printArray(int arr[], int size) {
    for (int i = 0; i < size; i++) {
        printf("%d ", arr[i]);
    }
}

int main() {
    int arr[5] = {1, 2, 3, 4, 5};
    printArray(arr, 5); // 传递数组和数组长度作为参数

    return 0;
}

二维数组传参

#include <stdio.h>

// 函数接受二维数组和行列数作为参数
void print2DArray(int arr[][3], int rows, int cols) {
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
            printf("%d ", arr[i][j]);
        }
        printf("\n");
    }
}

int main() {
    int arr[3][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
    
    printf("Original 2D Array:\n");
    print2DArray(arr, 3, 3); // 传递二维数组和行列数作为参数
    
    return 0;
}

一级指针传参

#include <stdio.h>

void modifyPointer(int *ptr) {
    *ptr = 42; // 修改指针指向的内容
}

int main() {
    int num = 10;
    int *ptr = &num;
    
    modifyPointer(ptr); // 传递指针作为参数

    printf("Value: %d\n", *ptr); // 输出修改后的值

    return 0;
}

二级指针传参

#include <stdio.h>

void modifyDoublePointer(int **pptr) {
    int newVal = 42;
    *pptr = &newVal; // 修改二级指针指向的值
}

int main() {
    int num = 10;
    int *ptr = &num;
    int **pptr = &ptr;
    
    modifyDoublePointer(pptr); // 传递二级指针作为参数

    printf("Value: %d\n", **pptr); // 输出修改后的值

    return 0;
}

第五阶段:🔗 探索函数指针的神奇世界

函数指针简介

使用函数指针

以下是一个简单的例子,演示如何声明、赋值和调用函数指针:

#include <stdio.h>

// 声明函数指针类型
typedef int (*Operation)(int, int);

// 加法函数
int add(int a, int b) {
    return a + b;
}

// 减法函数
int subtract(int a, int b) {
    return a - b;
}

int main() {
    Operation operationPtr; // 声明函数指针变量
    operationPtr = add; // 赋值为add函数

    int result = operationPtr(5, 3); // 通过指针调用函数
    printf("Result: %d\n", result); // 输出结果

    return 0;
}

函数指针数组

函数指针可以组成数组,称为函数指针数组。这使我们可以通过索引来选择不同的函数。以下是一个使用函数指针数组的示例:

#include <stdio.h>

int add(int a, int b) {
    return a + b;
}

int subtract(int a, int b) {
    return a - b;
}

int multiply(int a, int b) {
    return a * b;
}

int main() {
    int (*operationPtr[3])(int, int); // 声明函数指针数组
    operationPtr[0] = add;
    operationPtr[1] = subtract;
    operationPtr[2] = multiply;

    int result = operationPtr[1](10, 5); // 调用subtract函数
    printf("Result: %d\n", result);

    return 0;
}

指向函数指针数组的指针

我们可以声明指向函数指针数组的指针,让我们一起看看它的用法:

#include <stdio.h>

int add(int a, int b) {
    return a + b;
}

int subtract(int a, int b) {
    return a - b;
}

int multiply(int a, int b) {
    return a * b;
}

int main() {
    int (*(*operationPtrPtr)[3])(int, int); // 声明指向函数指针数组的指针
    operationPtrPtr = &operationPtr; // 赋值为函数指针数组的地址

    int result = (*operationPtrPtr)[2](7, 3); // 通过指针调用multiply函数
    printf("Result: %d\n", result);

    return 0;
}

第六阶段:🔗 回调函数:让程序更灵活

回调函数简介

回调函数的应用

以下是一个简单的示例,展示如何使用回调函数来排序一个整数数组:

#include <stdio.h>
#include <stdlib.h>

// 比较函数:升序排列
int compareAsc(const void *a, const void *b) {
    return (*(int *)a - *(int *)b);
}

// 比较函数:降序排列
int compareDesc(const void *a, const void *b) {
    return (*(int *)b - *(int *)a);
}

// 使用回调函数对数组进行排序
void sortArray(int arr[], int size, int (*compare)(const void *, const void *)) {
    qsort(arr, size, sizeof(int), compare);
}

int main() {
    int arr[5] = {5, 2, 8, 1, 3};
    
    sortArray(arr, 5, compareAsc); // 使用升序比较函数排序
    printf("Ascending Order: ");
    for (int i = 0; i < 5; i++) {
        printf("%d ", arr[i]);
    }
    
    sortArray(arr, 5, compareDesc); // 使用降序比较函数排序
    printf("\nDescending Order: ");
    for (int i = 0; i < 5; i++) {
        printf("%d ", arr[i]);
    }

    return 0;
}

在这个示例中,我们使用了两个不同的比较函数,分别用于升序和降序排序。通过将比较函数作为参数传递给 sortArray 函数,我们实现了动态选择排序方式的功能。

结语:尖叫指针,飞跃编程界!🚀

来到本篇博客的终点,你是否感受到了指针的神奇魅力呢?从字符指针到数组指针,从函数指针到回调函数,我们一路探索,揭开了指针的奥秘面纱。

指针,如同瞬间连接你与计算机内部的魔法纽带,让操作数据、调用函数变得轻松自如。无论是在数组的海洋中航行,还是在函数的宇宙中穿梭,指针都是你最忠实的伙伴。

相信你已经在这趟指针之旅中汲取了丰富的知识。保持好奇心,继续探索,愿你的编程旅程一路飞升,创造出更多令人惊叹的代码艺术!不忘初心,一起向编程的星辰大海进发吧!🌟

08-13 00:30