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
// 定义一个函数,返回输入参数的值
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
int main()
{
int num = 10; // 定义一个整数变量
int *ptr = # // 定义一个指针指向 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 = # 定义了一个指针 ptr ,并使其指向 num 。
int **ptrPtr = &ptr; 定义了一个二级指针 ptrPtr ,并使其指向指针 ptr 。
通过二级指针,可以间接访问和修改其所指向的指针所指向的变量的值。
指针的指针常用于一些复杂的数据结构,如链表、二叉树等,或者在函数中需要修改指针的值时使用。
指针数组是一个数组,其中的每个元素都是一个指针。
#include
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
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;
}
#include
// 主函数,接收命令行参数
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`。