IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> 关于指针的若干思考 -> 正文阅读

[数据结构与算法]关于指针的若干思考

字节

本文我们操作的最小的数据存储单位是字节

变量和变量名

1.当我们声明一个变量的时候,内存中就为我们开辟了一块空间,用于储存之后赋予变量的值,此时内存要解决两个问题:1. 存在哪里,2.开辟多大的空间,其实这两个问题在我们的语句中都有:其中变量名告诉我们把内存空间开辟到哪里,但是其实这个说法也不准确,准确来说是经历了以下步骤

  • 我们告诉了操作系统我们需要一块内存,这块内存的大小是int
  • 操作系统根据实际情况为我们分配了一块空间,这块空间可能是一个字节,也可能是很多个字节,具体的看我们的数据类型。这种分配可以如下图的自动分配,也可以是使用malloc函数手动分配
  • 当分配完成之后,操作系统会把分配空间的首地址和我们的变量名建立一个唯一映射,我们使用变量名就可以访问到这片内存空间中的值。
int i 

各种变量类型所占据的空间(按字节)

我的操作系统是64位的,各种数据类型所占据的空间(字节)如下

size of int is 4
size of long is 8
size of char is 1
size of float is 4
size of double is 8
size of int* is 8
size of char* is 8
size of int** is 8

注意所有不同数据类型的指针的大小都一样,无论是字符指针,整形指针,不同级数的指针大小也都一样,比如一级指针和二级指针,还有多级指针。但是为什么呢?因为我们说变量名是地址,而所有的变量,不管其内存多大,变量名都是等于该变量占据所有空间的首地址。所以所有指针的大小都是一样的

内存

内存是线性的,但是为了方便,我们通常把内存画成一个矩形,矩形的宽为一个字节,每一行都赋予一个唯一的地址,如图,每个小格子就是数据的最小存储单位,比特。每一行是一个字节。
在这里插入图片描述

定义变量和调用函数的内存行为

void swap(int* a,int* b){
	int temp = *a;
	*a = *b;
	*b = temp;
}

int main(){
	int a = 1234 ;
	int b = 5678 ;
	swap(&a,&b);
}

在这里插入图片描述
以上是一个经典的交换函数,我在这里画了内存中的行为

	int a = 1234 ;
	int b = 5678 ;

这两条语句会在内存中开辟两个int空间(8字节),同时在对应的比特位填充数据。当我们调用swap函数的时候

swap(&a,&b);

我们在内存中开辟两个指针空间(8字节)用于接受传进来的地址。


从访问权限看函数传参

  1. 如果函数是值传递,那么我们对该值的权限只有,我们可以使用printf函数输出值,可以将值作为运算数进行运算
  2. 如果传的的是一个指针p,那么我们对该指针指向的对象*p具有读写的权限,我们可以改变其值,比如上面的swap函数就是地址传递

指针变量的赋值

指针之间的赋值相当于共享某空间的地址,参看下面代码

    int a=10;
    int* p = &a;
    int* q = p;
    printf("[*p] is %d,[&p] is %p\n",*p,p);
    printf("[*q] is %d,[&q] is %p\n",*q,q);

以上代码的输出是

[*p] is 10,[&p] is 0x7ffe0458c994
[*q] is 10,[&q] is 0x7ffe0458c994

如果要修改指针本身所指向的地方,我们需要使用二级指针,比如下面,指针p,q,r可以修改变量a的值,pp,qq,rr可以修改p,q,r指针的内容,换言之修改指针的指向,比如现在我想让指针p指向一个新的变量,那么我们需要操作二级指针。
在这里插入图片描述

    int a= 10;
    int b= 20;
    int* p = &a;
    int** pp = &p;
    printf("%4s %4s  %14s %14s %14s%4s  %14s %14s %14s %14s\n","a","b","&a","&b","p","*p","&p","pp","*pp","&pp");
    
    printf("%4d %4d  %14p %14p %14p%4d  %14p %14p %14p %14p\n",a,b,&a,&b,p,*p,&p,pp,*pp,&pp);
    *p = 19;//change a
    printf("%4d %4d  %14p %14p %14p%4d  %14p %14p %14p %14p\n",a,b,&a,&b,p,*p,&p,pp,*pp,&pp);
    *pp = & b;//now p pointer to b
    printf("%4d %4d  %14p %14p %14p%4d  %14p %14p %14p %14p\n",a,b,&a,&b,p,*p,&p,pp,*pp,&pp);
    *p = 30;
    printf("%4d %4d  %14p %14p %14p%4d  %14p %14p %14p %14p\n",a,b,&a,&b,p,*p,&p,pp,*pp,&pp);

输出内容是

ab&a&b
10200x7ffc9bd71c900x7ffc9bd71c94
19200x7ffc9bd71c900x7ffc9bd71c94
19200x7ffc9bd71c900x7ffc9bd71c94
19300x7ffc9bd71c900x7ffc9bd71c94
p*p&p
0x7ffc9bd71c90100x7ffc9bd71c98
0x7ffc9bd71c90190x7ffc9bd71c98
0x7ffc9bd71c94200x7ffc9bd71c98
0x7ffc9bd71c94300x7ffc9bd71c98
pp*pp&pp
0x7ffc9bd71c980x7ffc9bd71c900x7ffc9bd71ca0
0x7ffc9bd71c980x7ffc9bd71c900x7ffc9bd71ca0
0x7ffc9bd71c980x7ffc9bd71c940x7ffc9bd71ca0
0x7ffc9bd71c980x7ffc9bd71c940x7ffc9bd71ca0

说说链表的malloc

在刚开始写链表的时候,我一直纠结一个问题,那就是为什么我们需要在创建链表的时候传递二级指针,如下,我们定义了一个结构体,然后写了一个创建链表的函数

typedef struct Node{
    int data;
    char name[20];
    char nodeName[5];
    struct Node* next;
    struct Node* pre;
}Node;

void Create(Node** node){
    (*node) = (Node*) malloc(sizeof (Node));
    (*node)->next=NULL;
    (*node)->pre=NULL;
    (*node)->data=0;
    strncpy((*node)->nodeName,"Head",5);
    strncpy((*node)->name,"Head",5);
}

为了解决这个问题,我们需要说说malloc函数,malloc函数会分配一块内存空间,然后返回一个指针,而注意,在这个过程中,我们需要修改的是指针的值,而我们说过,在函数的参数传递中。

如果想要改变变量的值,需要传递指针
如果想要改变指针的值,需要传递指针的指针,也就是二级指针

以下为了说明简便,我使用int**作为传递的指针类型,且看如下代码

#include "stdio.h"
#include "stdlib.h"

void Create(int** p){
    *p = (int*) malloc(sizeof (int ));
    *(*p)=20;
}

int main(){
    int* q;
    Create(&q);
    printf("%d",*q);
    
}

在这里插入图片描述

过程是这样的

  1. int* q,创建一个int指针,但是这个指针指向未知
  2. 通过函数传递二级指针,建立p和q的映射关系(*p=q)
  3. 通过*p来改变q的值,接受malloc的返回值
  4. 通过*(*p)来访问malloc的int空间,并且修改其值

如果把程序稍微改变一下,大家来观察下不同

#include "stdio.h"
#include "stdlib.h"

void Create(int** p){
	int* pointer = *p;
    pointer = (int*) malloc(sizeof (int ));
    *(pointer)=20;
}

int main(){
    int* q;
    Create(&q);
    printf("%d",*q);
    
}

看似小小的改变,但是产生了完全不同的结果

  1. 创建int指针,但是不知道指针指向哪里
  2. 通过函数传参建立了p和q的关系(p=*q)
  3. 创建了一个局部int指针pointer指向*q,也就是未知的空间
  4. 局部变量pointer接受malloc返回值
  5. 访问局部变量pointer指向的地方,赋值。
    在这里插入图片描述

我们发现其实q的值并没有发生变化,问题就出现在我们使用了int* pointer = *p;,我们需要修改的是p的值,所以我们需要传递p的指针,而p本身是一个int指针,所以我们需要传递二级int指针,而我们使用int* pointer = *p;语句,则是直接让我们的操作只能对q指向的位置空间操作,或者改变pointer的值,本操作直接令访问权限发生了变化。而且我们也不能在通过对pointer取地址运算重新操作*(Addrq),我们之后的一切操作都是针对局部变量pointer(见上图)。


降低指针权限,安全操作

看了上面,那时不是就说明int* pointer = *p;语句就一无是处呢?很显然不是,因为指针的可以任意访问,所以指针级数越高,也就越危险。我们传递二级指针是为了可以接受malloc的值,而之后的操作,绝大部分时间,都是读操作,所以我们可以尝试在使用二级指针接受malloc返回值之后,进行指针降级操作。如下:

#include "stdio.h"
#include "stdlib.h"

void Create(int** p){
    *p = (int*) malloc(sizeof (int ));
    int* pointer = *p;
    *(pointer)=20;
}

int main(){
    int* q;
    Create(&q);
    printf("%d",*q);
    
}

在这里插入图片描述


数组名和指针

在学习数组的时候我们知道,数组名其实就是指针,而对数组的下标索引操作,也可以使用指针的偏移来实现,比如

int a[10]={0};
//a[1] 和 *(a+1) 是等价的

函数指针

(未完待续。。。)

  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2022-04-30 08:55:25  更:2022-04-30 08:59:14 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 5:22:24-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码