0
点赞
收藏
分享

微信扫一扫

【深入理解指针】指针的进阶

目录

  1. 📌一些常见指针的用法和意义

  2. 📌指针数组

  3. 📌数组指针

  4. 📌函数指针

  5. 📌函数指针数组


1.📌一些常见指针的用法和意义

常见的指针有以下几种

指针的初阶用法就是认识并能熟练使用像char*,int*等一些用法,

比如说实现一个交换两个整数的函数,你要知道实参和形参的区别,也就是为什么要传指针。

这些就是一些初阶用法。

而指针的意义是什么呢?

其实指针类型的意义就是:决定了每一次访问内存的的大小

我们经常在定义指针的时候是不是都是按照它的类型进行定义的,比如是int类型,就定义一个int*,char类型就定义一个char*,其实你可以想象一下,地址全都是占4或8个字节的空间,哪它为什么要存在指针类型呢?

我把int*类型的指针放在char*行不行?你是否有这样的疑问?*

​ 来我给你举个例子

//下面输出什么
#include<stdio.h>
int main()
{
	int a = 0x00001234;
	int b = 0x00001234;
	int* pa = &a;
	*pa = 1;

	char* pb = (char*)&b;
	*pb = 1;

	printf("a=%x,b=%x\n", a, b);
	return 0;
}

OK,你再回想一下,地址全都是占4或8个字节的空间,哪它为什么要存在指针类型呢?当你这样想之后,再回想一下上面的例子,就会更近一步想到,哦!原来是每次访问内存的大小!

那可能会有人问我把上面int类型的b的指针放在char*之后,能不能把它也改成1?


接下来给大家提升一点难度:字符char*指针的另一种用法!

上面的字符串是存在p里面吗??

首先我们知道char*类型是指针吧,那指针的大小是4或8个字节吧,所以上面的字符串是肯定放不下的,那可能有人学过或了解过数据截断的讲法,但是这里是不可能截断的,因为在后面已经完完整整的把字符串的内存打印出来了。

我给你举个和上面这个类似的例子

#include<stdio.h>
int main()
{
	int arr[10] = { 1,2,3,4,5,7,8,9,10 };
	int* pa = arr;
	return 0;
}

上面这个代码pa是存放整个数组的元素吗? 不是了吧,大家都知道是存放数组首元素地址。

所以我们上面的字符串是存在p里面吗??

显然不是,其实它也是存放首字符的地址,然后通过首字符的地址找到后面的字符!大家可以跟数组这一块类别一下就明白了!

而且呢!它指向的内容是无法被修改的,(不是因为我在char*p前加了const才不被修改的,是它本身就无法被修改,如果你修改了,程序会崩溃,加const就是为防止自己去修改,这样如果不小心修改了,编译器会报错提醒我们)

这样的字符也称“常量字符串”

下面有一个这样的题目

//下面输出什么?
#include <stdio.h>
int main()
{
    char arr1[] = "hello,My friend!";
    char arr2[] = "hello,My friend!";
    const char* arr3 = "hello,My friend!";
    const char* arr4 = "hello,My friend!";
    if (arr1 == arr2)
        printf("arr1 =arr2\n");
    else
        printf("arr1 != arr2 \n");

    if (arr3 == arr4)
        printf("arr3 = arr4 \n");
    else
        printf("arr3 != arr4 \n");

    return 0;
}
为什么会是这样的结果呢,其实呢!数组arr1和arr2它们在开辟空间的时候是分别开不一样的空间,所以它们两的地址是不可能相同的,而arr3和arr4,就是我们上面讲的,它们是不可被修改的,也就是说它们在开辟空间的时候,没必要开两个不一样的空间了,反正它们的内容也不会被修改,那为什么还要浪费空间开新的呢,这时候编译器就很聪明了,它就把两个指针都指向同一块内存空间。因此arr3=arr4!

2.📌指针数组

我们常用的有整型数组,字符数组等,整型数组就是存放整型的数组,而字符数组就是存放字符的数组,

所以指针数组,就是存放指针的数组!!

那它是如何在代码中定义的呢?

3.📌数组指针

数组指针 主语:是指针!

我们知道int*是指向int型的指针,char*是指向char型的指针

所以数组指针是指向数组的指针!

p1, p2分别是什么?

p1上面我们讲过是指针数组

而p2就是我们的数组指针了!

为什么呢?

那数组指针怎么用呢?它有什么作用呢?

我给大家写一个数组指针错误的使用方式!

#include <stdio.h>
int main()
{
    int arr[10] = { 1,2,3,4,5,6,7,8,9,0 };
    int(*p)[10] = &arr;//把数组arr的地址赋值给数组指针变量p

    //但是我们一般很少这样写代码 一个数组指针的比较笨的使用方法
    int i = 0;
    for (i; i < 10; i++)
    {
        printf("%d ", *((*p) + i)  );
    }
    printf("\n");
    //上面的写法我们完全可以用一个整型指针来完成
    int* parr = arr;
    for (i = 0; i < 10; i++)
    {
        printf("%d ", *(parr + i));
    }
    return 0;
}

上面这个列子就是数组指针错误的使用方式,为什么说它错误呢!因为它是的用一个数组指针来实现数组打印,而这个功能完完全全可以用整型指针来实现,可它偏偏用了数组指针,这就将它复杂化,简直就是脱裤子放屁,多此一举!所以我们学数组指针可不是用来实现这样的功能的。

相对正确的使用方式是用于二维数组传参的时候使用的

举个例子

#include <stdio.h>
//以二维数组的形式接受地址
void print_arr1(int arr[3][5], int row, int col)
{
    int i = 0;
    int j = 0;
    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 j = 0;
    int i = 0;
    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 };
    print_arr1(arr, 3, 5);
    //数组名arr,表示首元素的地址
    //但是二维数组的首元素是二维数组的第一行
    //所以这里传递的arr,其实相当于第一行的地址,是一维数组的地址
    //可以数组指针来接收
    print_arr2(arr, 3, 5);
    return 0;
}

学了指针数组和数组指针我们来一起回顾并看看下面代码的意思:

int arr[5];
int *parr1[10];
int (*parr2)[10];
int (*parr3[10])[5];
//思考一下上面的代码是表示数组呢,还是数组指针呢?还是其他什么呢?
int arr[5];//一个整型数组,数组有5个元素,每个元素的类型是整型
int *parr1[10];//一个指针数组,数组里有10个元素,每个元素的类型是一个整型指针(int*)
int (*parr2)[10];//数组指针,指向的对象是一个拥有10个元素的数组,每个元素的类型是整型(int)
int (*parr3[10])[5];//一个数组指针数组,数组有10个元素,每个元素的类型是数组指针(int(*)[5])

4.📌函数指针

“函数指针”我想大家首先得知道它是一个指针吧!

而且是一个指向函数的指针

可能有人会问—>什么鬼?函数也有地址吗?

有没有呢我给你测试一下就知道了,看下面一段代码

#include <stdio.h>
void test()
{
	printf("hehe\n");
}
int main()
{
	printf("%p\n", test);
	printf("%p\n", &test);
	return 0;
}

那问题来了,整型数,字符,数组等的地址我们都已经学会如何保存在相对应的指针中了。

那我们的函数的地址要想保存起来,怎么保存?

我有个选项想让大家选出正确的函数指针:

看下面代码

void test()
{
 printf("hehe\n");
}
//下面pfun1和pfun2哪个有能力存放test函数的地址?
void (*pfun1)();
void *pfun2();

首先,能给存储地址,就要求pfun1或者pfun2是指针,那哪个是指针?

其实我想大家应该都能选出来,答案就是第一个pfun1,因为类似上面的数组指针嘛!*符号一般都要括号括起来的,因为它的优先级比较低

那怎么解释这个(void (*pfun1)())呢?

举个例子

//写出下面函数的函数指针
int add(int x, int y)
{
	return x + y;
}

接下来给大家阅读两段有趣的代码:

//代码1
(*(void (*)())0)();
//代码2
void (*signal(int , void(*)(int)))(int);

知道上面的代码是什么意思吗?这可不是我胡编乱造出来的,上面的代码来自于《C陷阱和缺陷》这本书。

解释一下这两行代码的意思

有没有什么函数指针的使用场景呢?

有,比如c语言自带的标准库中有一个可以排序任何类型的排序算法qsort,它就涉及了函数指针的参数和使用!

而像qsort这样通过调用函数指针的函数就被称为回调函数

感兴趣的可以翻一下我之前的文章有模拟实现过qsort函数,和qsort函数的介绍!


5.📌函数指针数组

为了防止大家学懵了,给大家回忆一下什么是数组!

数组是一个存放相同类型数据的存储空间

int *arr[10];//数组的每个元素是int*
int arr1[5];//数组每个元素是int
char* arr[6];//数组每个元素是char*

那我们已经学习了指针数组,现在学这个函数指针数组我相信是不难理解的!

那要把函数的地址存到一个数组中,那这个数组就叫函数指针数组

那函数指针的数组如何定义呢?

还是给几个选项吧!

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

答案是:parr1

parr1 先和 [] 结合,说明 parr1是数组,数组的内容是什么呢? 是 int (*)() 类型的函数指针。*

教一下大家如何正确的写好一个函数指针数组

#include<stdio.h>

int add(int x, int y)
{
	return x + y;
}
int sum(int x, int y)
{
	return x - y;
}
int main()
{
	//先写函数指针
	int (*pf)(int, int) = &add;

	//再把函数指针copy过来,在变量名后加个[]
	int (*pf1[5])(int, int) = { &add,&sum };

	printf("pf=%p\npf1[0]=%p\npf1[1]=%p \n", pf, pf1[0], pf1[1]);

	return 0;
}

举报

相关推荐

0 条评论