当前位置:首页 » 《休闲阅读》 » 正文

指针详解(从基础到入门)

16 人参与  2024年03月28日 09:40  分类 : 《休闲阅读》  评论

点击全文阅读


一、什么是指针

在计算机科学中,指针是编程语言中的一个对象,利用地址,它直接指向存在电脑储存器中另一个地方的值。由于通过地址能找到所需的变量单元,可以说,指针指向该变量单元。因此,将地址形象化地称为指针。意思是通过它能找到以它为地址的内存单元。从另外一个方面来讲,指针就是一个变量,用来存放内存单元的地址(编码),地址唯一标识一块内存空间。

可通过下面C代码来加以说明:

#include<stdio.h>int main(){    int a=10;//在内存中开辟一块空间    int *p=&a;//取出a的地址,将a的地址存放在p变量中,p就是一个指针变量    return 0;}

ps:存放在指针中的值都被当做地址来处理,在32位平台上一个指针变量占4个字节,在64位平台上一个指针变量占8给字节

二、指针类型的意义

既然指针变量的大小是固定的,那么为什么还有各种指针类型呢?

1、指针类型决定了指针解进行引用操作时,能够访问空间的大小
下面举例说明:

当指针类型为字符型时,有以下C代码
在这里插入图片描述
通过观察a的地址变化可知,对char类型的指针进行解引用操作并改变其对应的值时,可以改变两个字节内容(只能访问两个字节的空间)。

当指针类型为整形时,有以下C代码
在这里插入图片描述
通过观察a的地址在内存中的变化可知,对int类型的指针进行解引用操作并改变其对应的值时,可以改变四个字节内容(可以访问四个字节的空间)。

总结:指针的类型决定了对指针进行解引用操作时有多大权限。比如,char *的指针解引用就只能访问一个字节,而int *的指针解引用能访问四个字节,double *的指针解引用就能访问八个字节。

2、指针的类型决定了指针加减整数的步长

观察以下C代码及其运行结果:
在这里插入图片描述
我们不难发现,整形指针变量(pa)+1后,地址由C4跳跃到C8,向后跳跃了4个字节(一个整形);字符型指针变量(pc)+1后,地址由C4跳跃到C5,向后跳跃了1个字节(一个字符);类似的,double类型的指针变量+1后,地址应向后跳跃8个字节。

总结:指针类型决定了指针向前或向后走一步有多大(距离)。

可通过以下例题加深我们对其的理解

例题:已知数组int arr[10]={0},利用指针将数组元素全置为1

#include<stdio.h>int main()    {    int arr[10]={0};    int* p=arr;//数组名表示首元素地址    int i=0;    for(i=0;i<10;i++)//将各数组元素全部置为1    {        *(p+i)=1;//p为int类型的指针,每加一向后跳跃一个整形(4个字节)    }    for(i=0;i<10;i++)//打印输出    {        printf("%d ",arr[i]);    }    return 0;    }

若p为char类型的指针,还可通过以下代码进行实现

#include<stdio.h>int main()    {    int arr[10]={0};    char* p=arr;//数组名表示首元素地址    int i=0;    for(i=0;i<10;i++)//将各数组元素全部置为1    {        *(p+4*i)=1;//p为char类型的指针,每加一向后跳跃一个字符(1个字节),题设数组元素为整形(4个字节),应该加上4*i    }    for(i=0;i<10;i++)//打印输出    {        printf("%d ",arr[i]);    }    return 0;    }

三、野指针

**

(一)概念

**

野指针就是指针指向位置是不可知的(随机的,不正确的,没有明确限制的)。

**

(二)野指针的成因

**

1、指针为初始化

如以下C代码

#include<stdio.h>int main(){    int* p;//局部指针变量未初始化,默认为随机值    *p=10;    return 0;}

2、指针越界访问

如以下C代码

#include<stdio.h>int main(){   int arr[10]={ 0 };   int* p=arr;//数组名代表首元素地址   int i=0;   for(i=0;i<12;i++)   {       *(p++)=i;       //数组元素个数为10,当i>9时,指针指向的范围超过了数组arr的范围,此时p就是野指针   }   return 0;}

3、指针指向被释放的空间

如以下C代码

#include<stdio.h>int* test(){    int a = 10;    //a为局部变量,函数开始时创建,函数结束时销毁    return &a;}int main(){    int* p = test();    *p = 20;    //此时p指向的空间已销毁(被释放),不属于当前程序,此时指针p就为野指针    return 0;}

(三)如何避免野指针的出现

1、指针初始化
2、注意指针是否越界
3、指针指向被释放空间时置为NULL(空指针)
4、指针使用之前应检查其有效性

四、指针与数组

(一)指针与数组

数组名表示首元素地址

在这里插入图片描述
注:以下两种情况例外
1、&数组名(如&arr)
数组名不是首元素地址,数组名(arr)表示整个数组,&数组名(&arr)表示取出整个数组的地址
在这里插入图片描述
arr—>arr+1,向后跳跃了4个字节
&arr—>&arr+1,向后跳跃了40个字节(十六进制A20减9F8得28,换算成十进制为40)

2、sizeof(数组名),如sizeof(arr)
数组名(arr)表示整个数组,sizeof(arr)计算的是整个数组的大小

(二)指针数组

指针数组实质上为数组,与整形数组(存放整形)、字符型数组(存放字符型)类比可知,指针数组用于存放指针。
在这里插入图片描述

(三)数组指针

具体解释见如下代码

在这里插入图片描述

数组指针的用法如下

#include<stdio.h>void print2(int (*p)[5], int x, int y)//第一个形参int (*p)[5]为数组指针,存放的是第一行元素构成的一维数组的地址{    int i = 0;    int j = 0;    for (i = 0; i < x; i++)    {        for (j = 0; j < y; j++)        {            printf("%d ", *(*(p + i)+j));            /*             p的类型为数组指针,指向为一个一维数组的地址,p+1表示向后跳过一个数组大小,p + i表示向            后跳过i行,假设二维数组每一行分别代表一个一维数组,则 *(p + i)表示对应的一维数组的数            组名,而数组名代表该一维数组首元素的地址。该数组为整形数组,+j表示向后跳跃j个整形的大            小,得到该一维数组各个元素的地址,即*(p + i)+j)表示二维数组中各个元素的地址,再通过解            引用操作得到各个元素的值,即*(*(p + i)+j)表示二维数组每个元素的值            */                    }        printf("\n");    }}int main(){    int arr[3][5] = { {0,1,2,3,4},{1,2,3,4,5},{2,3,4,5,6} };    print2(arr, 3, 5);    //在二维数组中,数组名表示第一行数组的地址(可看成一个一维数组的地址)    return 0;}

通过指针数组与数组指针的学习,我们应该掌握下列代码的含义
int arr[5]
(一个五个元素的整形数组)
int *arr[5]
(指针数组,数组包含五个元素,每个元素类型为int *)
int ( * arr)[5]
(数组指针,指向一个有五个元素的数组)
int( * arr[10] )[5]
(是一个数组,数组有十个元素,每个元素为一个数组指针,该数组指针指向的数组有五个元素,每个元素的类型为int)

五、函数指针

顾名思义,函数指针就是指向函数的指针,用来存放函数地址的指针

函数指针的表示方法
在这里插入图片描述
可通过以下代码加以理解

#include<stdio.h>int Add(int x, int y){    return x + y;}int main(){    int a = 0;    int b = 0;    int (*p)(int, int) = Add;//定义一个函数指针    printf("%d\n", (*p)(10, 20));//通过函数指针调用函数    //输出结果为30    return 0;}
#include<stdio.h>void Print(char* str){    printf("%s\n", str);}int main(){    void (*p)(char*) = Print;//定义一个函数指针    (*p)("Hello World");//通过函数指针调用函数    return 0;}

例:
解释如下代码

//解释以下代码void(*fun(int, void(*)(int)))(int);        //fun是一个函数声明        //fun函数的参数有两个,第一个为int类型,第二个为函数指针类型,该函数指针指向的函数参数类型为int,返回类型为void        //fun函数的返回类型也是一个指针函数,该指针函数指向的函数的参数为int,返回类型为void(空)//上述代码可通过typedef关键字进行简化,简化代码如下:    typedef void(*fun_t)(int);//将该函数指针类型创建别名fun_t    fun_t(fun(int, fun_t));

六、函数指针数组

函数指针数组实质上为一个数组,数组的每个元素为一个函数的地址

#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;}int main(){    int(*p[4])(int, int) = { Add,Sub,Mul,Div };//定义一个函数指针数组    int i = 0;    for (i = 0; i < 4; i++)    {        printf("%d\n", p[i](10, 5));//循环逐个调用函数    }    return 0;}

在这里插入图片描述

函数指针数组用途:转移表
例:计算器的实现

#include<stdio.h>void menu(){    printf("**********************\n");    printf("******   1.Add  ******\n");    printf("******   2.Sub  ******\n");    printf("******   3.Mul  ******\n");    printf("******   4.Div  ******\n");    printf("******   0.exit ******\n");    printf("**********************\n");}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;}int main(){    int input = 0;    int x = 0;    int y = 0;    int (*pfarr[])(int, int) = {0, Add,Sub,Mul,Div };    //pfarr为一个函数指针数组—转移表,通过函数指针数组下标转到对应的函数    do    {        printf("请输入你的选择:\n");        scanf_s("%d", &input);        if (input >= 1 && input <= 4)        {            printf("请输入两个数:\n");            scanf_s("%d %d", &x,&y);            printf("%d\n", pfarr[input](x, y));        }        else if (input == 0)        {            printf("退出!\n");        }        else        {            printf("输入错误,请重新输入\n");        }    } while (input);    return 0;}

七、函数指针数组指针

【函数指针数组】指针,为指针,指向一个函数指针数组

    int (*pf)(int, int);//函数指针    int(*pfarr[5])(int, int);//函数指针数组    int(*(*pparr)[5])(int, int)=&pfarr;//函数指针数组指针,指向【函数指针数组】的指针    //所指向的函数指针数组有5个元素,每个元素为一个函数指针,类型为int(*)(int,int)

点击全文阅读


本文链接:http://zhangshiyu.com/post/86927.html

<< 上一篇 下一篇 >>

  • 评论(0)
  • 赞助本站

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

关于我们 | 我要投稿 | 免责申明

Copyright © 2020-2022 ZhangShiYu.com Rights Reserved.豫ICP备2022013469号-1