深入理解指针(2)

前言
本章主要讲解数组名用法,二级指针及指针数组


文章目录

  • 一、数组
    • 1.1 数组名的用法
    • 1.2 使用指针访问数组
  • 二、二级指针
  • 三、指针数组和数组指针
    • 3.1指针数组
    • 3.2 数组指针
  • 四、二维数组与指针
  • 五、函数指针
    • 5.1 函数指针变量的创建
    • 5.2 函数指针变量的使用
    • 5.3 typedef关键字
  • 六、 函数指针数组
    • 6.1 函数指针数组的定义及使用
    • 6.2 实例(转换表)

一、数组

1.1 数组名的用法

在使用数组名时,很多时候数组名表示首元素地址,但也有特例。
以下是数组名表示整个数组的用法

  1. 在使用操作符sizeof时
int arr[]={1,2,3,4,5,6,7,8,9};
int sz=sizeof(arr)/sizeof(arr[0]);//求数组长度(数组元素个数),sz=9,在这里arr就表示整个数组

2.在使用取地址符号&时

int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
printf("arr = %p\n", arr);
 printf("arr+1 = %p\n", arr+1);
 printf("&arr = %p\n", &arr);
 printf("&arr+1 = %p\n", &arr+1)

结果如图所示(16进制地址)
arr代表首地址,所以arr+1后地址+4,&arr代表整个数组,加+1后,地址+4*数组大小
深入理解指针(2)_第1张图片

深入理解指针(2)_第2张图片

1.2 使用指针访问数组

#include 
int main(){
 int arr[] = {123456};
 int i = 0;
 int sz = sizeof(arr)/sizeof(arr[0]);//计算数组长度
 int* p = arr;//指针p指向arr数组首元素
 for(i=0; i<sz; i++){
 printf("%d ", *(p+i));//实际上*(p+i)=p[i],就像数组一样
 }
 return 0;
}

实际上*(p+i)=p[i],同理arr[i] 应该等价于 *(arr+i),数组元素的访问在编译器处理的时候,也是转换成⾸元素的地址+偏移量求出元素的地址,然后解引⽤来访问的。

二、二级指针

二级指针是指向指针的指针。

int a = 10;
int *p = &a; // p 是一个指向 int 类型的指针,存储了变量 a 的地址
int **pp = &p; // pp 是一个指向 int* 类型的指针,存储了指针 p 的地址

深入理解指针(2)_第3张图片

三、指针数组和数组指针

指针数组和数组指针经常会被混淆,分清它们是很重要的。

3.1指针数组

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

    int num1 = 10;
    int num2 = 20;
    int num3 = 30;

    int* arr[3]={ &num1 , &num2 , &num3 }; // 声明一个指针数组,数组元素是指向 int 的指针

    printf("Value at arr[0]: %d\n", *arr[0]); // 输出 num1 的值
    printf("Value at arr[1]: %d\n", *arr[1]); // 输出 num2 的值
    printf("Value at arr[2]: %d\n", *arr[2]); // 输出 num3 的值

3.2 数组指针

数组指针是一个指针,它指向一个数组。数组指针可以用于指向一维或多维数组。数组指针的类型与所指向数组的类型有关。

int (*arr)[3] = {10, 20, 30};

//[]优先级比*高,*arr加括号再与[],或者这种写法
int *arr[3] = {10, 20, 30}; // 声明一个指向 int 的指针,指向数组 arr 的第一个元素
int *ptr = arr;//在这个示例中,ptr 是一个指向整数的指针,它指向数组 arr 的第一个元素。你可以通过递增指针来遍历数组中的元素。

指针数组和数组指针比较:
深入理解指针(2)_第4张图片

四、二维数组与指针

指针和二维数组的关系: 一个指向二维数组的指针实际上是一个指向一维数组的指针。这是因为在内存中,二维数组的行在连续的内存位置上,因此可以通过一个指向一维数组的指针来访问不同的行。

例如,考虑以下的二维数组和指针:

int arr[3][3] = { {1, 2, 3}, {4, 5, 6}, {7, 8, 9} };
	int(*ptr)[3] = arr; // 指向二维数组arr的第一行
	int i = 0;
	for (i = 0; i < 3; i++) {
		int j = 0;
		for (j = 0; j < 3; j++) {
			printf("%d ", ptr[i][j]);//等价于printf("%d ",*(*(ptr+i)+j));
		}
		printf("\n");
	}

运行结果:
深入理解指针(2)_第5张图片

ptr+i指向行,对其解引用*(ptr+i)得到那一行的首元素地址
深入理解指针(2)_第6张图片

五、函数指针

5.1 函数指针变量的创建

函数在内存中也有一个地址,这个地址标识了函数在程序中的位置,允许程序能够通过地址来调用函数。函数的地址实际上是指向函数代码所在内存位置的指针,也被称为函数指针。

#include

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

int main() {
	int x = 5;
	int y = 6;
	int ret=Add(x,y);
	printf("%p\n",Add);//函数名本身就是地址,可以不使用取地址符号
	printf("%p", &Add);
	return 0;
}

运行结果:
深入理解指针(2)_第7张图片
既然是有地址的,我们就可以创建指针将其地址存起来。
函数指针的语法如下

//其中,return_type 是函数的返回类型,function_pointer_name 是函数指针的名称,parameter_list 是函数的参数列表。
return_type (*function_pointer_name)(parameter_list);

函数指针类型解析:
深入理解指针(2)_第8张图片

5.2 函数指针变量的使用

实例:

#include 

// 函数原型
int add(int a, int b) {
    return a + b;
}

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

int main() {
    // 声明函数指针,指向带有两个 int 参数并返回 int 的函数
    int (*pf)(int, int);

    // 赋值函数指针,使其指向 add 函数
    pf = add;

    // 使用函数指针调用函数
    int result = pf(5, 3);
    printf("Result: %d\n", result); // 输出:Result: 8

    // 改变函数指针的指向,使其指向 subtract 函数
    pf = subtract;

    result = pf(10, 4);
    printf("Result: %d\n", result); // 输出:Result: 6

    return 0;
}

在示例中,我们声明了一个函数指针 pf,使其可以指向带有两个 int 参数并返回 int 的函数。然后,我们赋值给这个函数指针,使其分别指向 add 函数和 subtract 函数。通过函数指针,我们可以调用不同的函数,实现了在运行时动态选择调用的函数。

5.3 typedef关键字

typedef 是中的一个关键字,用于定义新的数据类型名称。它可以用来为已有的数据类型(如基本数据类型、结构体、指针等)创建别名,以便更方便地使用这些类型。

typedef 的语法形式如下:

//existing_data_type 是已有的数据类型,new_data_type_name 是你为其创建的新类型名称
typedef existing_data_type new_data_type_name;

为函数指针类型创建别名:

typedef int (*pfun_t)(int, int); // 将函数指针类型[int (*)(int, int)]定义为 pfun_t 类型的别名
pfun_t funcPtr = add; // 使用 pfun_t 类型来声明函数指针变量,funcPtr是函数指针变量名

六、 函数指针数组

6.1 函数指针数组的定义及使用

函数指针数组是一个数组,其元素都是函数指针。这意味着每个数组元素存储了一个指向特定函数的地址,使得可以通过数组来调用不同的函数。

以下是创建和使用函数指针数组的一般步骤:

  1. 定义函数指针类型: 首先,需要定义一个函数指针类型,描述了函数的返回类型和参数列表。这个类型将用于声明函数指针数组的元素类型。
  2. 声明函数指针数组: 使用定义的函数指针类型,声明一个函数指针数组。数组的大小可以根据需要定义,每个数组元素存储一个函数指针。
  3. 将函数地址赋值给数组元素: 将不同函数的地址分别赋值给函数指针数组的各个元素。
  4. 使用函数指针数组: 可以通过数组索引来访问特定的函数指针,然后使用函数指针来调用相应的函数。
    为了方便理解,我们可以将函数指针数组看作数组与函数指针的结合
typedef int (*pfun_t)(int, int); // 定义 pfun_t 为函数指针类型

int main() {
    pfun_t arr[3]; // 声明函数指针数组,数组元素为 pfun_t 类型的函数指针

    // 可以将不同函数的地址赋值给数组元素
    arr[0] = add;
    arr[1] = subtract;
    // ...

    // 使用函数指针数组调用函数
    int result = arr[0](5, 3); // 调用 add 函数
    printf("Result: %d\n", result);

    // ...
    
    return 0;
}

6.2 实例(转换表)

转移表(也称为查找表、跳转表、分支表)是一种在编程中用于优化条件分支操作的技术。它通常用于替代一系列的 if-else 或 switch 语句,从而提高代码的执行效率和可读性。

计算器的⼀般实现:

 #define _CRT_SECURE_NO_WARNINGS 1
#include 
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 x, y;
	int input = 1;
	int ret = 0;
	int(*p[5])(int x, int y) = { 0, add, sub, mul, div }; //转移表
	do{
		printf("*************************\n");
		printf("****** 1:add 2:sub ****** \n");
		printf("****** 3:mul 4:div ******\n");
		printf("******** 0:exit *********\n");
		printf("*************************\n");
		printf("请选择:");
		scanf("%d", &input);
		if ((input <= 4 && input >= 1)){//选择计算方式
			printf("输入操作数:");
			scanf("%d %d", &x, &y);
			ret = (p[input])(x, y);
			printf("ret = %d\n", ret);
		}
		else if (input == 0){
			printf("退出计算器\n");
		}
		else{
			printf("输入有误\n");
		}

	} while (input);
	return 0;
}

深入理解指针(2)_第9张图片
如果你喜欢这篇文章,点赞+评论+关注⭐️哦!
欢迎大家提出疑问,以及不同的见解。

你可能感兴趣的:(算法,数据结构,排序算法,c语言)