指针进阶(3)(超详细)

9efbcbc3d25747719da38c01b3fa9b4f.gif

给大家分享一句我很喜欢我话:

知不足而奋进,望远山而前行!!!

铁铁们,成功的路上必然是孤独且艰难的,但是我们不可以放弃,远山就在前方,但我们能力仍然不足,所有我们更要奋进前行!!!

今天我们更新了指针进阶(3)内容,

🎉 欢迎大家关注🔍点赞👍收藏⭐️留言📝

一、数组指针变量

1.1数组指针变量是什么?

之前我们学习的指针数组,数组中存放的是一种数组,数组中存放的是地址(指针)。

数组指针变量是指针变量?还是数组?

答案是:指针!

例如我们前面所学的:

字符指针:char*指向字符的指针

整形指针:int*指向整形的指针

浮点型指针:float*指向浮点型的指针

那么数组指针呢,应该就是指向数组的指针,

#include<stdio.h>
int main()
{
	int a = 10;
	int*  pa=&a;

	char ch = 'w';
	char* pc = &ch;

	int arr[10] = { 0 };
	int* p= &arr;//取出的是数组的地址 

	return 0;
}

所以数组指针变量就是:存放的是数组的地址,能够指向数组的指针变量。

大家看一下下面这两个哪个是数组指针?

int* p1[10];

int (*p2)[10];

我们来分析一下:

第一个是不带(),然后p1首先和方块结合,然后与*结合,所以这个p1就是一个数组,并且是存放指针的数组,即指针数组。

第二个p2与*在一起,所以p2是一个指针变量,指向的是数组。即数组指针。

1.2指针变量的初始化

  • 指针也是一种数据类型,指针变量也是一种变量

  • 指针变量指向谁,就把谁的地址赋值给指针变量

  • “*”操作符操作的是指针变量指向的内存空间

#include <stdio.h>
 
int main()
{
    int a = 0;
    char b = 100;
    printf("%p, %p\n", &a, &b); //打印a, b的地址
 
    //int *代表是一种数据类型,int*指针类型,p才是变量名
    //定义了一个指针类型的变量,可以指向一个int类型变量的地址
    int *p;
    p = &a;//将a的地址赋值给变量p,p也是一个变量,值是一个内存地址编号
    printf("%d\n", *p);//p指向了a的地址,*p就是a的值
 
    char *p1 = &b;
    printf("%c\n", *p1);//*p1指向了b的地址,*p1就是b的值
 
    return 0;
}

注意:&可以取得一个变量在内存中的地址。但是,不能取寄存器变量,因为寄存器变量不在内存里,而在CPU里面,所以是没有地址的。

1.3二维数组的传参

按我们之前所学的,二维数组的传参一般都是按下面这种形式去传参:

#include<stdio.h>

void print(int arr[3][5],int row,int col)
{
	for (int i = 0; i < row; i++) {
		for (int j = 0; j < col; j++) {
			printf("%d ", arr[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} };

	print(arr, 3, 5);

	return 0;
}

这样便实现了数组的传参。

如果我们想要利用数组进行传参的话该怎么去做呢?

其实,二维数组的数组名也是数组首元素的地址,那么到底是谁的地址?

首元素其实就是第一行,首元素的地址就是第一行的地址,第一行的地址就是一维数组的地址!

将新的函数写成这种形式即可:

#include<stdio.h>

int print(int (*arr)[5], int row, int col)
{
	for (int i = 0; i < row; i++) {
		for (int j = 0; j < col; j++) {
			printf("%d ",*(*(arr+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} };

	print(arr, 3, 5);

	return 0;
}

这样也可以得到上面那种效果。

二、函数指针变量

函数指针是什么呢?是指向函数的指针吗?存放的是函数的地址吗?

一个函数总是占用一段连续的内存区,而函数名就是该函数所占内存区的首地址。
我们可以把函数的这个首地址(或称入口地址)赋予一个指针变量,使该指针变量指向该函数。
然后通过指针变量就可以找到并调用这个函数。
我们把这种指向函数的指针变量称为“函数指针变量”。

2.1函数指针变量的创建

函数编译之后,也会占用一定的内存空间,那么也就具有地址。把这个函数的地址,就叫做函数的指针。把函数的指针就叫做函数的入口地址。
定义的一般形式:函数的返回值类型 (*指针变量名)(形式参数1, 形式参数2, …);

int sum(int a, int b)
{
    return a+b;
}
int main(void)
{
    // 定义一个指针变量p,指向sum函数
    int (*p)(int a, int b) = sum;
    // 或者 int (*p)(int, int) = sum;
    // 或者 int (*p)() = sum;
    // 利用指针变量p调用函数
    int result = (*p)(1, 3);
    // 或者 int result = p(1, 3);
    printf("%d\n", result);//4
    
    return 0;
}

2.2函数指针变量的使用

这里我们举一个使用函数指针的例子:

#include<stdio.h>

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

void menu(){
	printf("****************************\n");
    printf("********1.add  2.sub *******\n");
    printf("********3.mul  4.div *******\n");
    printf("********0.exit       *******\n");
}

void calc(int (*pf)(int, int)) {
	int x = 0, y = 0;
	int ret = 0;

	printf("请输入两个操作数:>");
	scanf("%d%d", &x, &y);
	ret = pf(x, y);
	printf("%d\n", ret);
}

int main()
{
	int input = 0;
	
	do {
		menu();
		scanf("%d", &input);
		switch (input) {
		case 1:
			calc(add);
			break;
		case 2:
			calc(sub);
			break;
		case 3:
			calc(mul);
			break;
		case 4:
			calc(div);
			break;
		case 0:
				break;
		default:
			printf("输入错误,请重新选择。");
			break;
		}
	} while (input);
	return 0;
}

这是一个简易的计算机模型,大家自己看一下是怎么实现的。

2.3两段有趣代码

第一段:

int main()
{
(*(void(*)() ) 0 )();

return 0;
}

大家一起来看一下这串代码,先自己分析一下,然后我带大家一起来分析。

我们先来看最里面的一段,void (*p)(),这一段其实就是一段利用指针进行函数传参的过程。

然后( void(*)() )--这是在进行   强制类型转换

(void (*)())0 --将0强制转化为void(*)()的函数指针类型

//这些就意味着我们假设0 地址处放着无参,返回类型是void的函数

//意味着是调用0地址处放的函数

第二段:

void (*signal(int,void(*)(int)))(int);

这段代码是一个函数声明,它声明了一个名为signal的函数。函数的返回类型是一个指向函数指针的指针。参数列表包括一个int类型的参数和一个指向函数的指针,该函数接受一个int类型的参数并返回void

简单来说,这个函数signal允许我们传入一个整数和一个函数指针,并返回一个指向函数指针的指针。这个函数指针可用于处理信号的响应。

通常情况下,signal函数用于设置信号处理函数,以便在接收到特定信号时执行相应的操作。

 

2.4  typedef  关键字

本科学过C语言的朋友都知道数据类型的概念。C语言中有整型,如char、int、short等等;也有浮点类型,如float、double。

1.typedef关键字就是给这些数据类型起一个别的名字,然后用这个自己起的名字来定义变量。比如如下语句就把char类型起了个新的名字叫int8。然后用int8定义了一个变量a1。

typedef char int8;
int8 a1;

2.简化复杂的类型声明:typedef 可以简化复杂的类型声明,使得类型声明更加简洁明了。

例如,下面的语句将一个指向函数的指针类型定义为 FUNC_PTR:

typedef int (*FUNC_PTR)(int, int);

然后就可以在程序中使用 FUNC_PTR 来代替这个复杂的类型声明,例如:

FUNC_PTR fp = add;   // add 是一个函数,与 FUNC_PTR 类型匹配

3.提高代码可移植性:由于不同的编译器对数据类型的定义可能存在差异,使用 typedef 可以提高代码的可移植性。

例如,如果使用 short int 类型来定义变量,可能在不同的编译器上会有不同的长度,导致代码的可移植性受到影响。使用 typedef 定义一个新的类型名称,可以确保该类型在不同的编译器上都具有相同的长度和语义。

总之,typedef 是一个非常有用的关键字,它可以提高代码的可读性、简化复杂的类型声明,以及提高代码的可移植性。

相关推荐

  1. 指针

    2024-02-06 02:38:01       25 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-02-06 02:38:01       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-02-06 02:38:01       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-02-06 02:38:01       18 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-02-06 02:38:01       20 阅读

热门阅读

  1. ES6-let

    ES6-let

    2024-02-06 02:38:01      22 阅读
  2. 记一次Mysql加字段加不上问题排查过程

    2024-02-06 02:38:01       33 阅读
  3. NCCL 源码详解总目录

    2024-02-06 02:38:01       33 阅读
  4. 多线程与socket编程

    2024-02-06 02:38:01       34 阅读
  5. nginx负载均衡

    2024-02-06 02:38:01       33 阅读
  6. Blender 的重拓扑功能中的参数,

    2024-02-06 02:38:01       29 阅读
  7. salesforce flow 如何保存多选列表选中的值

    2024-02-06 02:38:01       28 阅读
  8. 【C语言】深入理解函数指针

    2024-02-06 02:38:01       38 阅读