白话编程--数据篇(2)指针

发布时间:2024年01月24日

前言

? ? ? ? 数据中最重要的部分,没有之一.?

? ? ? ? 和数据结构紧密相连.建立符合逻辑结构的数据集合必备.

? ? ? ? 指针的内容比较多,尽可能用场景说明,更多需要自己发掘.

引入

? ? ? ? 计算机中所有数据都在内存中存放,他们都拥有自己的"地址",也就是指针.底层有一种机制,可以用访问地址来访问地址中的数据,这就是指针的意义所在.

? ? ? ? 指针的意义是间接访问数据

指针写法

? ? ? ? 数据类型 * 指针变量名=数据地址?

? ? ? ? 说明:数据地址通常用"&+数据变量名"来表示,数组名自动表示为数组首元素地址

? ? ? ? 当把某个数据的地址赋给指针,也称作指针指向了这个数据

????????和前面声明某数据类型的变量做对比: "类型名 变量名=值",二者含义基本一致.

? ? ? ? 举例
int a=10;
int *p=&a; /*整型指针p指向整型变量a*/

int b[]={10,20,30};
int *pa=b;  /*整型指针pa指向整型数组b*/

? ? ? ? 指针可以指向单个数据,但不常用;? ? ? ? ? ? ? ? ? ? ????????? ?????????????????//前两行

? ? ? ? 指针更多的用途是指向数据集合(包括数组,链表)内元素? ? ? ???????//后两行

指针基本用法

? ? ? ? 当使用指针指向了某个数据之后,例如int *pa=b,即可以用*pa来间接访问该地址内数据;同时指针支持加减法,*(pa+1)或者pa[1]表示偏移一个地址后数据的取用

? ? ? ? 比如:已知某整型数组内有n个元素,打印出来

 void print_int(int *p,n){ //形参里的"int*"表示这是一个整型指针类型
    for(;p<p+n;p++){
        printf("%d\n",*p); //"*p"表示指针指向地址的值
    }
}

?调用print_int(b,3)或者print_int(pa,3),将上面的数组b[]和元素数量3传入,得到打印结果.

????????---因为C语言是按值传递的,所以传入的是变量或者常量都可以,为形参传入变量时会查找变量? ?对应的常量(值)

注意:

? ? ? 1>? 因为C语言的独特设计,在声明指针类型,以及函数形参类型时,用到的" * "表示这是一种指针类型,其他地方的"*+指针变量名"表示指针指向地址里的值.这是初学者比较容易迷惑的地方,当然我们也没法去质疑语言的设计者为什么要这样设计,我们只需要记住这个规则即可.

? ? ? ?2> 指针的边界需要程序员自己去掌握.

? ? ? ? 按照上面定义的指针pa指向数组b[],以下几种情况成立:

? ? ? ? *pa==10;? *(pa+1)==20; *(pa+2)==30;

? ? ? ? 如果把上述式子都看成条件表达式,他们的值都为true.? ? ?if( *pa==10)相当于if(true)

? ? ? ? 但是:*(pa+3)等于多少? 这个是未定义的,结果是不确定的.所以指针不允许这样使用,称为指针越界访问内存.

? ? ? ? 同样道理,在调用print_int()函数时,事先知道数组里有3个元素,所以传入3没有问题,但是如果传入其他数字,比如4,则会出现意想不到的结果,这也是被禁止的.

指针的使用禁忌(注意事项)

? ? ? ? 1>指针必须指明其数据类型,即属于什么类型的指针.

? ? ? ? ?int *p=a;//假设存在a[]这样的数组

? ? ? ? ?指明了指针是属于整型,然后可以间接访问整型数组里的元素. 这一点很好理解,如果脱离了数据元素类型,采用了 *p=&a;? 来定义估计没人看得懂.

当然C语言里有void *这样的指针,但是建议不要用,因为这可能超出了程序员对数据的控制范围

? ? ? ? 2>指针必须指向已存在的数据.

? ? ? ? 书上有一句话:指针在使用前必须初始化.可能有点难以理解,换成这个说法可能会好一些.

? ? ? ? 前面讲到指针的意义在于间接访问数据.如果不把指针指向已存在的数据(已开辟的内存空间),指针不仅没有意义,而且是错误的.

? ? ? ? 3>指针不能指向具体的内存地址(非绝对)

? ? ? ? 指针表示地址,他的值就是指针指向数据在内存中的地址值.类似于0x0036FE96(假设32位机)这样的值.在C中这样的写法是禁止的:

? ? ? ? int *p=(int *)0x0036FE96;? ?//错误写法!禁止!

????? ? 这表示程序员想直接访问内存地址.但内存地址使用是要经过操作系统允许的.芯片把这个权限交给了操作系统.操作系统有自己的区间划分.即使程序员想要查询自己定义的变量放在哪个地址上,返回的也是虚拟内存地址,而非实际物理地址.

????????????int *p=a;//假设存在a[]这样的数组,允许!

? ? ? ? 话说回来,如果要操作硬件,需要将硬件寄存器映射到某一片内存空间上,这时候可以在允许的内存空间上操作.这不在软件程序员讨论范畴.

????????一般情况下,不能直接操作内存地址.特殊情况下可以,所以说不是绝对的

? ? ? ? 4>在函数内部建立指针的临时变量时,小心内存泄漏

? ? ? ? 举例:写一个函数,建立一个整型数组,整数个数由自定义输入,数组内所有元素等于0;

 void createArray(int n){

   
    int* p = (int*)malloc(sizeof(int) * n);

    for(int i=0;i<n;i++){

        p[i]=0;

    }
}

? ? ? ? 乍一看这段代码没有问题.但是因为是在函数中临时申请了内存空间,在函数调用完毕后,没有释放相应的内存空间(free),所以造成了内存空间被占用,而且不被发现.这叫做内存空间泄露.

? ? ? ? 但我们可以解决这个问题:既然申请了内存空间,得到了指针.那么返回的指针由定义指针接收,则保持对这部分数据的访问权限.

 int * createArray(int n){

   
    int* p = (int*)malloc(sizeof(int) * n);

    for(int i=0;i<n;i++){

        p[i]=0;

    }
    return p;
}

? ? ? ? 5>指针可以指空(NULL)?

? ? ? ? 这点和2>并不矛盾,指针可以指空,常用于链表末尾.

指针的多样性?

? ? ? ? 从前面内容已经看出来,指针和数组(当然也包括链表),指针和数据集合是紧密联系在一起的.一个必须有的概念应该形成:只要有数据集合,必然有指针去读取或修改.?简单梳理一下逻辑:数据集合必然需要遍历(访问其中的单个元素),遍历必然要用到指针.

? ? ? ? 指针和数据集合存在以下关系:

? ? ? ? 1>可以定义多个指针指向同一个地址.

? ? ? ? 例如:

? ? ? ? int a[]={10,20,30};

????????int *p=a;????????//定义指针p指向数组a

? ? ? ? int *q=a; ????????//定义指针q指向数组a

? ? ? ? ?2>可以定义集合内某具体位置的指针.

? ? ? ? int *first=&a[0];//定义指向数组首元素

? ? ? ? int *last=&a[2];//定义指向数组末元素

文章来源:https://blog.csdn.net/jllws1/article/details/135775707
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。