有关一级指针和二级指针的归纳总结

C语言中的二级指针(2) -- 链表操作 一个链表中所有元素通过指针串联起来,链表的插入、删除等操作都是对指针的操作。 使用指针操作链表 例如,下面的代码段实现了一个删除链表节点的函数,很多人都会采用类似的代码删除链表节点。 /* 结构体定义 */ struct testdata { struct testdata * next; //指向链表下一个节点的指针 int id; }; /* 链表头 */ struct testd 阅读详情

           计导课讲到指针和动态内存分配,这里真是一个难点,很多概念容易混淆,有时候一个概念要查阅好多网站,看过很多博文才能理解,不过学习编程不就是这样嘛~~一定要想办法理解才行,所以今天归纳了关于一级指针和二级指针的知识供大家学习(中间有一些摘录,不太记录来源,如果有涉及到哪位仁兄的文章,请跟我说,我会加上来源链接哒~)(http://blog.youkuaiyun.com/bearray123/article/details/7209356  这个博友写的二级指针理解很不错)

       

一级指针与二级指针的含义:

首先任何值都有地址,一级指针的值虽然是地址,但这个地址做为一个值亦需要空间来存放,是空间就具有地址 ,这就是存放地址这一值的空间所具有的地址,二级指针就是为了获取这个地址,一级指针所关联的是其值(一个地址)名下空间里的数据,这个数据可以是任意类型并做任意用途,但二级指针所关联的数据只有一个类型一个用途,就是地址,指针就是两个用途提供目标的读取或改写, 那么二级指针就是为了提供对于内存地址的读取或改写。指针的表现形式是地址,核心是指向关系指针运算符“*”的作用是按照指向关系访问所指向的对象.如果存在A指向B的指向关系,则A是B的地址,“*A”表示通过这个指向关系间接访问B.如果B的值也是一个指针,它指向C,则B是C的地址,“*B”表示间接访问C如果C是整型、实型或者结构体等类型的变量或者是存放这些类型的数据的数组元素,则B(即C的地址)是普通的指针,称为一级指针,用于存放一级指针的变量称为一级指针变量。A(即B的地址)是指向指针的指针,称为二级指针,用于存放二级指针的变量称为二级指针变量.根据B的不同情况,二级指针又分为指向指针变量的指针和指向数组的指针.

 

 

*****总结:指针也是传值传递,当我们要在被调函数里面改变调用函数一级指针的值时,就需要以二级指针作为参数。这种情况是经常碰到的,比如在链表(无头结点)操作时是通过链表第一个元素来找到其他所有链表中的元素,如果删除操作时删除的正好是第一个 元素,那么这时就要改变链表头指针的指向了。当然还有在二叉树操作时当删除的刚好是树根结点,此时也要改变一级指针的指向。

 

为什么要用二级指针(见上传的图片)



怎么理解利用二级指针申请动态内存

以前在学习数据结构的时候一直没弄懂函数参数里面传递 ** p  以及使用 &的含义,这里摘抄了一小段文章方便理解。不懂的时候再看看这几段代码。

指针参数是如何传递内存的?

    如果函数的参数是一个指针,不要指望用该指针去申请动态内存。见如下例子:

void GetMemory(char *ip, int num)

{

 ip= (char *)malloc(sizeof(char) * num);

}

void Test(void)

{

 char*str = NULL;

 GetMemory(str,100); // str 仍然为 NULL

 strcpy(str,"hello"); // 运行错误

}

 

试图用指针参数申请动态内存

    毛病出在函数GetMemory中。编译器总是要为函数的每个参数制作临时副本,指针参数ip的副本是 _ip,编译器使 _ip= ip.如果函数体内的程序修改了_ip的内容,就导致参数ip的内容作相应的修改。这就是指针可以用作输出参数的原因。在本例中,_ip申请了新的内存, 只是把_ip所指的内存地址改变了,但是ip丝毫未变。所以函数GetMemory并不能输出任何东西。事实上,每执行一次GetMemory就会泄露一 块内存,因为没有用free释放内存。

    如果非得要用指针参数去申请内存,那么应该改用“指向指针的指针”,见如下示例:

 void GetMemory(char **p, int num)

{

 *p = (char *)malloc(sizeof(char) * num);

}

void Test(void)

{

 char *str = NULL;

 GetMemory(&str, 100); // 注意参数是 &str,而不是str

 strcpy(str, "hello");

 std::cout<< str << std::endl;

 free(str);

}

 

这里写一段自己的理解:这个程序是想通过函数给str指针分配一点内存 --malloc,然后再给str指针赋值。。。注意这是在另一个函数内部改变str'指针的指向(str原来是指向NULL的,现在要使之指向函数 GetMemory内分配的内存区域)所以要使用二级指针来实现,其实质就是改变str指针的指向(比如从指向A 改为指向B),而不是改变str指针指向地址的内容哦。

 

 

   用指向指针的指针申请动态内存

    当然,我们也可以用函数返回值来传递动态内存。这种方法更加简单,见如下示例:

 char *GetMemory(int num)

{

 char *ip = (char *)malloc(sizeof(char) * num);

 return ip;

}

void Test(void)

{

 char *str = NULL;

 str = GetMemory(100);

 strcpy(str, "hello");

 std::cout<< str << std::endl;

 free(str);

}

 

我们在做链表的时候,我们肯定希望在用一个函数creatLink(...)函数来增加链表节点。那么我们可以有2种方法来实现
    第一种,用一级指针

[cpp] view plaincopy

  1. typedef struct node{    
  2.     ...    
  3.     ...    
  4. }list;    
  5. node *create(list *l){    
  6.    list *head;    
  7.    head = l;    
  8.    malloc...//为节点申请内存空间     
  9.    ...    
  10.    ...    
  11.    //操作     
  12.    return head;    
  13. }    
  14. int main(...){    
  15.     ...    
  16.     ...    
  17.     list *listhead    
  18.     createList(listhead);    
  19.     ....    
  20.     //以后的任何操作,我们都要考虑,我们是否拿到的是链表头指针,到底哪个是链表波的头指针,我们是否要renturn下来返回链表头指针??等。。。。     
  21. }    

 

这样做可以达到删除增加 节点的目的,但是,在任何情况下,我们的操作都得死死地抓住头指针,也即是我们增加删除节点后,任何对链表长度的修改,我们都要链表头指针返回,即 return head;所以,我们要通过这个函数最后获得头指针,抓住他,死死地抓住他,然后操作。
   
    第二种方法:用双指针,也即是二级指针。

[cpp] view plaincopy

  1. typedef struct node{    
  2.     ...    
  3.     ...    
  4. }list;    
  5. void   create(list **l){    
  6.    list *head;    
  7.    head = *l; //这里不知道写成 *l 对不对,原文是写的l,我自己觉得应该是 *l    
  8.    malloc...//为节点申请内存空间     
  9.    ...    
  10.    ...    
  11.    //操作     
  12.        
  13. }    
  14. int  main(...){    
  15.     ...    
  16.    ...    
  17.     list *st    
  18.     createList(st)    
  19.     ....    
  20.     ..    
  21. // 以后的任何操作,不管是删除还 是插入,我们不需要考虑,我们是否已经return head了,不需要,我们在任何情况下,对链表的操作都只需要使用 st来完成,因为,st就是链表的头指针,不变,因为在申明st的时候,已经为st分配 一个地址空间,它是存在的,一直存在,直到main函数结 束     
  22. }    


 

 

 

总结:  如果函数参数中传递的是指针,而且想改变传入指针的指向的话,则可以使用 二级指针来实现。(当然如果不在函数中的情况则可以直接给一级指针重新赋值使之指向另一个数据)

 

 

为什么改变主函数里指针的内容,即所指变量的地址时,要用二级指针

先来说说函数的传参机制

当数组作为参数传入时,是能够实现双向传递的

数组名作函数参数与基本数据类型作函数参数相比,具有完全不同的特点。c语言规定,数组名代表数组的首地址,所以,数组名作函数参数时,是将数组的首地址由实参传递给形参,即实参数组与形参数组会公用一个相同的数组首地址和一段相同的存储单元。所以,当形参数组元素发生变化时,实参数组元素的值也会随着发生改变。
当形参数组元素发生变化时,实参数组元素的值也会随着发生改变。
 
数组作为参数是按地址传递的
数组名就是数组的首地址。因此在数组名作函数参数时所进行的传送只是地址的传送, 也就是说把实参数组的首地址赋予形参数组名。

 

函数调用时基本的参数传递方式有传值和传地址两种,在传值方式上是将实参的值传递给形参,因此实参可以是表达式或常量,也可以是变量(数组元素),在传址方式下,要将实参的地址传递给形参,因此,实参必须是变量(数组名或数组元素,不能是表达式或常量),在这种情况下,被调函数对形参进行修改其实就是对实参进行修改,因此客观上能实现数据的双向传递。

也解释了当要改变主函数里指针的内容,即所指变量的地址时,要用二级指针。

.指针作为形参时,如果只需要改变指针指向的值,可以使用一级指针,如果需要改变指针本身的地址

,则需要使用二级指针,相当于改变的是一级指针指向的值。
2.指针作为形参时,指针指向的内容变化是可以带回的,指针地址的变化是不可带回的,即指针作为参数,其地址不可改变,否则形参就无法传回实参的值。

即指针作为形参,指针本身的地址改变,函数返回时这种变化将无效(注意!!!是当形参是指针时!!!!)





数组指针与指针数组(一些notes)

数组指针:int(*p)[4] 指向数组p的指针 a pointer to an array

指针数组:int *p[4] orint *(p[4]) 数组p中元素都为int型指针array of pointers

[ ]的优先级高于*


Linux下.tar.gz文件的解压及libpcap的安装 Linux下.tar.gz文件的解压及libpcap的安装初步了解libpcap环境libpcap下载解压.tar.gz文件配置生成makefile文件Make安装查看帮助 初步了解libpcap libpcap(Packet Capture Library)即数据包捕获函数库,是Unix/Linux平台下的网络数据包捕获函数库。 它是一个独立于系统的用户层包捕获的API接口,为底层网络监测提供了... 阅读详情

相关推荐

Win/Linux系统如何离线安装python第三方库文件whl | tar.gz

在联网的状态下,通常使用 pip install XX 即可安装相应第三方库,但是有时候服务器会考虑安全性问题,服务器是处于离线断网状态。所以需要一些操作步骤进行第三方库的离线安装。 PyPI(Python Package Index)是python官方的第三方库的仓库,所有人都可以下载第三方库或上传自己开发的库到PyPI。PyPI推荐使用pip包管理器来下载第三方库。 pip可正常工作在Win...

天真不无邪,hello和world 2万+

【数据结构】深入浅出理解链表中二级指针的应用

本文通过对单链表结构的分析,以及大量逻辑图示,物理图示详细解析了什么是二级指针?为什么链表要用到二级指针?为什么同样是链表的函数,有的要用二级指针而有的只要用一级指针?为什么同样是链表,有的链表中使用了二级指针?而有的函数却只需要使用一级指针?等问题.

C/C++领域,数据结构,linux,MySQl学习者 3790

linux下mysql的tar包离线安装

linux下mysql的tar包离线安装 1.确保mysql安装之前有libaio依赖 yum search libaio yum install libaio 2.下载mysql的tar离线包 下载页面选择系统,linux-Generic https://cdn.mysql.com//Downloads/MySQL-8.0/mysql-8.0.19-linux-glibc2.12-x86_6...

饶一熊的博客 1545

单链表中二级指针的使用以及单链表的增删查改操作

本文主要介绍了链表带头节点和不带头节点的两种方式的基本介绍和实现及二级指针的用法原理

Lee贤的博客 1671

kali linux虚拟机VmTool的安装

对于半路出家的疯子来说,这个vmtool的安装是个比较麻烦的事情,其中涉及到tar命令的使用,个人感觉这些压缩/解压文件命令对于压缩文件、解压文件名、解压目录的安放比较杂乱,今天就借安装vmtool梳理下tar的相关知识。 【tar】 【常用选项】 -c 建立新的压缩文件 -x 从压缩的文件中提取文件 -v 显示操作过程 -f 指定压缩文件 -z 支持gzip解压文件 -j 支持bzip2解压文件...

weixin_45365048的博客 611

Linux实战指南:Zotero从tar.bz2安装到桌面快捷方式全解析

本文详细解析了在Linux系统(特别是Ubuntu)上通过tar.bz2压缩包安装Zotero文献管理器的完整流程。从下载解压、将软件移至/opt目录、赋予执行权限,到创建并配置桌面快捷方式,提供了每一步的终端命令与原理说明,帮助用户掌握Linux下绿色软件的通用安装方法,并涵盖后续更新维护指南。

rust6ferris的博客 817

链表,二级指针的第一个实际应用

链表中的数据是不连续存储的,以节点的方式存在,现在我们要把这些数据连接起来,方式是让上一个节点存储下一个节点的地址。

2301_79682841的博客 935

ubuntu、linux安装redis(使用tar包的方式)

linux安装redis ubuntu 按照步骤一步步来,你是可以的,每天给自己打个气,加油!所有的困难都是一步步解决的,即使当前你干的工作比较难,只要用心,最后都能解决,加油,对面的你!学习之所以会想睡觉,是因为那是梦开始的地方。ଘ(੭ˊᵕˋ)੭ (开心) ଘ(੭ˊᵕˋ)੭ (开心)ଘ(੭ˊᵕˋ)੭ (开心)ଘ(੭ˊᵕˋ)੭ (开心)ଘ(੭ˊᵕˋ)੭ (开心)------不写代码不会凸的小刘。

qq_40834643的博客 1941

C语言:结构体中一级指针和二级指针的创建与释放示例

结构体结构体中一级指针和二级指针的创建与释放,以一个例子来说明,笔者这里自己敲得代码,虽然用C语言几年了,但是有的东西还是会遗忘。笔者敲代码过程中也是有bug出现的,经过调试也找到了bug所在,不得不说,巩固也是很重要的。而结构体作为C语言一部分也是很重要的,指针作为C语言的半壁江山也保持着其重要地位。学过C++或Java的都知道class(类),个人觉得结构体就像是class的一部分,不过里面不能放实现class的相关属性函数。代码不是很长,但里面有很多细节的考虑,特别是一级指针和二级指针的内存的申请和释

Bixiwen_liu的博客 5028

linux系统在离线环境下如何选择合适的安装包

版权声明:本文为神州灵云作者的原创文章,未经神州灵云允许不得转载。 本文作者:陈正林(志云),高级测试工程师,神州灵云首席测试。 linux离线安装包的选择: 常用的安装包有.rpm(.deb或者其他linux发行版本)格式安装包, 二进制文件,tar压缩包或者源码; 我们如何快速找到适合自己的安装文件呢? 如我们安装kompose软件,能联网,在线安装再简单不过了...

神州灵云的博客 2717

Linux 在VMWARE中安装JAVA以及配置jdk环境(已使用SSH把jdk-7u71-linux-i586.tar.gz传输到Linux的文件夹demo名下)

目录 一、使用SSH传输jdk到Linux的demo文件夹下 二、打开SSH窗口或者(Linux的终端窗口)操作 : 2.1打开Linux终端 2.2输入命令su root 开启root权限 ​2.3成功root :~变成# 三、Linux的JDK安装 3.1查看当前Linux系统是否已经安装java,没有则继续下一步,有则删除jdk 输入 rpm -qa | grep java 3.2卸载两个openJDK 四、解压jdk 4.1看到jdk解压完毕,usr/local文件夹下已...

HelloGuoYing的博客 1161

Linux(Alibaba Cloud Linux release 3) 安装JDK21.0.5

【代码】Linux(Alibaba Cloud Linux release 3) 安装JDK21.0.5。

AllowM的博客 777

Linux安装&卸载mysql5.7

Linux系统【tar包】方式安装mysql5.7。**查询并卸载 Mariadb **移除残留的的文件和相关依赖。查询残留的文件和相关依赖。

glenn_programmer的博客 636

Linux Tar软件包管理

4、Tar 软件包管理   Linux 操作系统除了使用 RPM 管理工具对软件包管理之外,还可以通过 tar、zip、jar等工具进行源码包的管理,实现对文件的压缩解压4.1、Tar 命令参数详解-A, --catenate, --concatenate 将存档与已有的存档合并-c, --create 建立新的存档-d, --diff, --compare 比较存档...

whx827058798的博客 511

Linux压缩工具tar与unzip实战:C/C++开发者必备的环境搭建与项目部署指南

在Linux系统管理与软件开发中,文件归档与压缩是基础且核心的操作。其原理在于通过特定算法将多个文件或目录集合成单个包,并减少存储空间占用,从而实现高效的数据组织、传输与备份。这一技术对于提升工程效率、保障数据完整性具有重要价值,尤其在软件源码分发、依赖库管理和项目部署等场景中不可或缺。本文聚焦于Linux环境下最常用的tar与unzip工具,深入解析其命令参数、工作流程及权限处理机制,并结合C/C++开发中常见的第三方库安装、源码编译等实际场景,提供从解压、权限修复到环境配置的完整解决方案,帮助开发者规避

hideto 872

Linux解压下载安装命令

常用命令:tar、yum、rpm 1、tar命令 tar命令解包:tar -zxvf xxx.tar.gz(-xvf xxx.tar) tar命令压包:tar -zcvf xxx xxx.tar.gz 2、yum命令 搜索命令:yum search命令 下载并安装:yum install -y 应用名 卸载:yum remove 应用名 3、rpm命令:需要先有rpm包 安装:rpm -ivh 安装包 卸载:rpm -e 应用[--nodeps]

ITjob1102的博客 4618

Linux离线部署VSCode:tar.gz手动安装与配置全攻略

在Linux系统管理中,软件部署是基础且关键的环节。传统包管理器虽便捷,但在离线环境、版本锁定或自定义路径等场景下存在局限。通过理解Linux软件包的组织原理,掌握通用归档文件(如.tar.gz)的解压与配置方法,能显著提升环境控制的灵活性与独立性。这种技术方案尤其适用于内网服务器、多版本共存及标准化开发环境构建等工程实践。本文以Visual Studio Code为例,详细解析其tar.gz包的下载、解压、环境变量配置及启动器创建全过程,并针对离线安装、文件监视器限制等常见问题提供解决方案,帮助开发者实现

weixin_34189116的博客 377

Linux下安装jdk-7u76-linux-x64.tar.gz

1、新建一个jdk的安装目录,我这里是在/usr/local/下新建了java目录,我是使用WinSCP创建的文件夹,把 jdk-7u76-linux-x64.tar.gz压缩包从本地Windows系统中拖到Linux的/usr/local/java目录下,然后进行解压,命令如下: tar xvf jdk-7u76-linux-x64.tar.gz 2、解压完成后,进入到

徐凤竹 程序人生 8977
上一篇: 字符串数组与字符串指针
下一篇: 计导课指针 数组部分笔记大杂烩
Peggy_Chang
博客等级 码龄11年 21粉丝 44原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值