嵌入式学习之路 16(C语言基础学习——指针操作二维数组、指向函数的指针、指针的指针)

一、指针操作二维整型数组

1、二维数组的本质

        在 C 语言中,二维数组本质上是由多个一维数组组成的。例如,int a[2][3]可以看作是包含两个长度为 3 的一维整数数组。

2、指针与二维数组

        对于二维数组int a[2][3],&a[0]的类型是int (*)[3],因为 C 语言中不直接支持int[3] *这种类型,所以需要使用int (*)[3]来表示指向包含 3 个整数的一维数组的指针。
定义指针int (*p)[3] = a;p指向了二维数组a的首地址,其基类型是int[3]。

3、通过指针访问二维数组元素

*p相当于a[0],即指向二维数组的第一行(内部的一维数组)。
(*p)[0]表示第一行的第一个元素,等同于a[0][0]。
*(*p + 0)也表示第一行的第一个元素。
*(*(p + 1) + 1)相当于a[1][1],即指向二维数组的第二行的第二个元素。
一般地,*(*(p + i) + j)就相当于a[i][j],可以通过这种方式遍历二维数组的元素。

例如,如果要通过指针遍历二维数组a的所有元素,可以使用以下代码:

for (int i = 0; i < 2; i++) 
{
    for (int j = 0; j < 3; j++) 
    {
        printf("%d ", *(*(p + i) + j));
    }
    printf("\n");
}

4、将二维整型数组作为参数传递给函数

以数组形式传递:第二维的大小必须指定

void function(int arr[][3], int rows) 
{
    // 函数体
}

int main() 
{
    int a[2][3] = {{1, 2, 3}, {4, 5, 6}};
    function(a, 2);
    return 0;
}

以指针形式传递:这种方式与以数组形式传递本质上是相同的

void function(int (*arr)[3], int rows) 
{
    // 函数体
}

int main() 
{
    int a[2][3] = {{1, 2, 3}, {4, 5, 6}};
    function(a, 2);
    return 0;
}

二、指针操作二维字符型数组

char s[][10] = {"hello","world","china"};

char (*p)[10] = s; 

定义了一个指针 p ,它指向一个包含 10 个字符的一维字符数组,这里 p 指向了二维数组 s 的起始位置。

*(*(p + i) + j) 可以用于访问二维字符型数组 s 中特定位置的字符。

void putStr(char (*p)[10], int row)
{
	int i=0;
	for ( i=0; i<3; i++ )
	{
		printf("%s \n",*(p+i));
	}
}
int main()
{

	char s[][10] = {"hello","world","China"};
	char (*p)[10] = s;
    
    putStr(s,3);

	return 0;
}

三、函数指针

1、函数指针的概念

        函数指针是一种特殊的指针,它指向函数的入口地址。函数指针的类型由函数的返回值类型和参数类型共同决定。

2、函数指针的定义和初始化

在代码中,例如:

int (*p)(int);

定义了一个函数指针 p ,它指向一个返回值为 int 且有一个 int 类型参数的函数。

可以通过将函数名赋给函数指针来进行初始化,如 p = func1; 。

例:

// 定义一个指向返回值为 int,带有两个 int 型参数的函数的指针
int (*ptr)(int, int);

可以将函数的地址赋给函数指针,例如,如果有函数 int add(int a, int b) ,则可以这样赋值: 

ptr = add;
int result = ptr(2, 3);  // 等价于 int result = add(2, 3);

3、回调函数

        回调函数是一种通过函数指针实现的机制。在某些情况下,一个函数(通常称为主调函数)可能在执行过程中需要调用另一个函数(回调函数)来完成特定的任务。主调函数事先不知道回调函数的具体实现,但通过传递函数指针来在需要的时候调用它。

#include <stdio.h>

// 定义一个函数,返回输入参数的值
int func1(int n)
{
    return n;
}

// 定义一个函数,返回输入参数的平方
int func2(int n)
{
    return n*n;
}

// 定义一个函数,返回输入参数的立方
int func3(int n)
{
    return n*n*n;
}

// 选择排序函数,根据传入的函数指针确定排序规则
void choieSortN(int *a, int len, int (*p)(int))
{
    int i = 0;
    int j = 0;

    for (i = 0; i < len - 1; i++)  // 外层循环控制排序轮数
    {
        for (j = i + 1; j < len; j++)  // 内层循环每一轮比较次数
        {
            if (p(a[i]) > p(a[j]))  // 根据传入的函数计算值进行比较
            {
                int temp = a[i];  // 交换元素
                a[i] = a[j];
                a[j] = temp;
            }
        }
    }
}

// 打印数组函数
void printfArray(int *a, int len)
{
    int i = 0;
    for (i = 0; i < len; i++)  // 遍历数组并打印每个元素
    {
        printf("%d ", a[i]);
    }
    printf("\n");  // 换行
}

// 定义加法函数
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;
}

// 处理数据函数,根据传入的函数指针进行计算并打印结果
void processDate(int a, int b, int (*p)(int, int))
{
    printf("ret = %d\n", p(a, b));  // 打印计算结果
}

int main()
{
    int a[] = {6, -7, 3, -9, 5, 1, 8, -4, 2};  // 定义并初始化整数数组
    int len = sizeof(a) / sizeof(a[0]);  // 计算数组长度

    choieSortN(a, len, func1);  // 以 func1 规则排序
    printfArray(a, len);  // 打印排序后的数组
    choieSortN(a, len, func2);  // 以 func2 规则排序
    printfArray(a, len);  // 打印排序后的数组
    choieSortN(a, len, func3);  // 以 func3 规则排序
    printfArray(a, len);  // 打印排序后的数组

    processDate(3, 2, ADD);  // 计算 3 和 2 的加法
    processDate(3, 2, SUB);  // 计算 3 和 2 的减法
    processDate(3, 2, MUL);  // 计算 3 和 2 的乘法
    processDate(3, 2, DIV);  // 计算 3 和 2 的除法

    return 0;
}

4、回调函数的实现

        choieSortN 函数是一个排序函数,它接受一个整数数组 a 、数组长度 len 和一个函数指针 p 作为参数。在函数内部,通过比较 p(a[i]) 和 p(a[j]) 的结果来进行排序。这就是回调函数的应用,通过传递不同的函数指针(如 func1 、 func2 、 func3 ),可以实现基于不同规则的排序。
        processDate 函数接受两个整数 a 、 b 和一个函数指针 p ,通过 p(a, b) 来调用传入的函数进行计算,并打印结果。

5、函数指针的用途

增加代码的灵活性和可扩展性,使得主调函数可以根据不同的需求调用不同的回调函数。
实现事件驱动编程,当特定事件发生时,调用相应的处理函数。
封装和隐藏函数的实现细节,只暴露函数指针供外部使用。

四、指针的指针

指针的指针(也称为二级指针)是指向指针的指针变量。

#include <stdio.h>

int main() 
{
    int num = 10;  // 定义一个整数变量

    int *ptr = &num;  // 定义一个指针指向 num

    int **ptrPtr = &ptr;  // 定义一个二级指针指向 ptr

    // 通过二级指针访问和修改 num 的值
    printf("初始值:%d\n", **ptrPtr);

    **ptrPtr = 20;  // 修改 num 的值

    printf("修改后的值:%d\n", **ptrPtr);

    return 0;
}

在上述代码中:

int num = 10; 定义了一个整数 num ,值为 10 。
int *ptr = &num; 定义了一个指针 ptr ,并使其指向 num 。
int **ptrPtr = &ptr; 定义了一个二级指针 ptrPtr ,并使其指向指针 ptr 。

通过二级指针,可以间接访问和修改其所指向的指针所指向的变量的值。

指针的指针常用于一些复杂的数据结构,如链表、二叉树等,或者在函数中需要修改指针的值时使用。

五、指针的指针数组

指针数组是一个数组,其中的每个元素都是一个指针。

#include <stdio.h>

int main() 
{
    // 定义三个整数变量
    int num1 = 10, num2 = 20, num3 = 30;

    // 定义一个指针数组,每个元素都是指向 int 类型的指针
    int *ptrArray[] = {&num1, &num2, &num3};

    // 通过指针数组访问和打印变量的值
    for (int i = 0; i < 3; i++) 
    {
        printf("%d ", *(ptrArray[i]));
    }
    printf("\n");

    return 0;
}

        代码中定义了一个指针数组 ptrArray ——int *ptrArray[] = {&num1, &num2, &num3}; 并将三个整数变量的地址分别存储在数组的元素中。在 for 循环中,通过 *(ptrArray[i]) 来获取每个指针所指向的整数的值并进行打印。

六、指针数组与数组指针

指针数组是一个数组,其中的每个元素都是一个指针。

int* arr1[5];  // 定义了一个包含 5 个指向 int 类型的指针的数组

arr1 是一个指针数组,它的每个元素都可以用来指向一个 int 类型的变量。 

数组指针是指向一个数组的指针。

int (*arr2)[5];  // 定义了一个指向包含 5 个 int 类型元素的数组的指针

要区分指针数组和数组指针,可以通过以下方式记忆:

指针数组:先看“数组”,意味着这是个数组,再看“指针”,说明数组中的元素是指针。
数组指针:先看“指针”,表示这是个指针,再看“数组”,表明指针指向的是一个数组。

#include <stdio.h>

int main() 
{
    int num1 = 10, num2 = 20, num3 = 30, num4 = 40, num5 = 50;

    // 指针数组的使用
    int* ptrArray[5] = {&num1, &num2, &num3, &num4, &num5};

    for (int i = 0; i < 5; i++) 
    {
        printf("指针数组中第 %d 个元素指向的值: %d\n", i, *ptrArray[i]);
    }

    // 数组指针的使用
    int arr[3][5] = 
    {
        {1, 2, 3, 4, 5},
        {6, 7, 8, 9, 10},
        {11, 12, 13, 14, 15}
    };

    int (*ptr)[5] = arr;

    for (int i = 0; i < 3; i++) 
    {
        for (int j = 0; j < 5; j++) 
        {
            printf("数组指针指向的数组中第 %d 行第 %d 列的值: %d\n", i, j, (*ptr)[j]);
        }
        ptr++;  // 移动指针指向下一行
    }

    return 0;
}

七、main函数的参数

#include <stdio.h>

// 主函数,接收命令行参数
int main(int argc, const char *argv[]) 
{
    // 打印命令行参数的数量
    printf("argc = %d\n", argc);  

    int i = 0;
    // 遍历所有的命令行参数
    for (i = 0; i < argc; i++)  
    {
        // 打印每个参数的索引和参数值
        printf("argv[%d] = %s\n", i, argv[i]);  
    }

    return 0;
}

        这段代码主要用于处理命令行参数。 在 `main` 函数中,`argc` 表示命令行参数的数量,包括程序名本身。 `argv` 是一个字符指针数组,其中每个元素指向一个命令行参数的字符串。 例如,在命令行中运行 `./a.out arg1 arg2 arg3` 时: - `argc` 的值为 `4`,因为有程序名 `./a.out` 以及三个参数 `arg1`、`arg2`、`arg3`。

  • 21
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值