C语言中的指针以及二级指针代码详解

很多初学者都对C中的指针很迷糊,希望这篇blog能帮助到大家:

1.什么是“指针”:

在执行C程序的时候,由于我们的数据是存储在内存中的。所以对于C程序本身来说,如果想找到相应被调用的数据,就要知道存储该数据的内存地址是多少,换言之,C程序通过已知的内存地址到相应的内存位置存储数据。

这里简单说一下内存管理(对于初学者来说。为了避免专业术语引发的理解问题,下面的叙述尽量避免专业定义:),对于现代计算机系统来说,内存空间分为两个区域,一个是“数据区”,一个是“地址区”,“数据区”存储的是用户数据,比如我们要把一个数字“5”存储到计算机(因为一个单纯的自然数“5”,是没有任何意义的,然后对于计算机来说它需要知道你要把什么定义为“5”,你就不得不定义“x=5")对于计算机而言,这个过程分为以下几个部分:

1.在”栈区(stack)(这个定义实在不能避免,初学者的话就请暂时记住这个名字)“开辟一个空间,用来存放”5“

2.另存存放”5“的内存的地址。

3.将步骤2中的内存地址存在另一个区域(专门用来存放地址的指针区),并记下当前存放步骤2中的内存地址的内存地址(好拗口,这里其实是二级指针的概念)

3.建立一个”索引“将x与步骤3中的内存地址关联,存放在”索引区“(请注意,x和5不是存在一起的,而是有一个“映射表”,并且 指向 x的指针不会直接指向5,而是直接指向x,再通过“映射表”找到x的值‘5',这个概念非常重要,后面例子会讲到利用指针交换两个变量的值,就是基于“x和5不是存在一起的”这个基本概念)。

这里再多说一嘴:为什么要以这种方式存放数据?

内存的存储区就像一池湖水,数据就像池水里面的鱼,如果不用内存寻址的方式,那么当你找某个特定数据的时候,就相当于在一池湖水里找某一条叫做“张三”的鱼一样--你得一条一条捞出来辨认。

如果有内存寻址,就像把一池湖水用渔网分成若干网格,每个网格里面放一两条鱼并且把每个网格都编号(编号和鱼的对应关系假如你用一个小本子记起来),这样当你想找某条叫“张三“的鱼时,你只要打开小本子(指针地址)找相应的网格就可以了。

那么,存储数据的内存地址(有点拗口)或者说是上面例子里面记载编号和鱼的对应关系的小本子就叫指针。

举个实例吧,如下图所示,我们将内存存储空间实体化:假设途中两条平行线夹的空间是内存可以存储数据的空间,途中C的位置存储的是数据,那么P的位置存储的就是指针。

如何定义指针?

int *p;

注意:

1.这里的int,指的是指针p对应的存储区的数据格式,并不是指针p的数据格式。你可以理解为指针的数据格式只有一种。

2.*不仅仅是单纯的运算符,它还是声明符。可以把“*”理解为像“int,float,double”等等这样的格式声明。

3.在使用指针p的时候,经常会用到地址运算符“&”,请注意“&”是运算符,运算操作是取地址,可以把p直接赋上一个地址值:

int i=5,*p;
p=&i; 

于是 *p 的值就是5了。

上面还可以这么写

int i=5;
int *p=&i; 

从这两个例子的区别可以看出“*”具有类型声明的作用。

再写一个交换两个变量的值的代码:

#include <stdio.h> 

void swap(int *a, int *b)
{
 int temp;//创建一个中间变量用于交换位置。
 temp = *a;
 *a = *b;
 *b = temp;
} 

int main()
{
 int m=10,n=22;
 swap(&m,&n);//这里对于理解内存管理原则非常重要,正如前面所说,变量m和其值10不是存在一个内存存储区,而是两个,它们通过一个映射表映射起来,所以在这里交换m和n的地址值可以理解为交换了m和n的映射表指向位置。
 printf("m=%d,n=%d\n",m,n); 

 return 1; 

} 

上面是通过改变两个变量地址的方式交换了变量m,n的值。在这个过程用到了内存地址和内存以及指针的定义,如果没有看懂请回头再仔细研究指针的定义。

二级指针:

如果你熟悉了指针的定义,那么二级指针应该很好理解,所谓的二级指针,就是指针的指针。

具体解释一下:因为任何一个变量值(包括指针地址)最后都是要放入到内存中去的,回到之前举的“池子里的鱼”那个例子,所谓的二级指针就是存放那个写着网格和编号的小本子的位置信息(比如你把这个本子放到某个抽屉里了,那么二级指针记载的内容就是“如何找到这个抽屉”)。

二级指针的定义也很简单粗暴,一个指针变量 *p存放这个指针变量地址的二级指针就是 *(*p),你可以直接简单粗暴地简写为**p(编译器是认这个的)。

其他问题:

1.定义一个指针变量*p,那么p到底是什么?

你可以简单粗暴地把的值p理解为 这个指针变量存储的地址。切记千万不要写成:

int *p=5; 

原因就是我之前说过的,这里再重复一次:*p 只是声明变量p存储的内容是地址。*p并不是一个可以赋值的变量,而是一个”特殊的“ 类型定义+变量。

2.该如何在指针中赋值?

下面说几个合法的赋值:

int *temp = *p;//这是二级指针常用的操作,作用是将指针P的值(指针p的值是地址值,指向的是另外一个地址空间)赋给指针temp指向的值,等价于 int *temp;temp=*p; 
int i=115,j=116,*r=&i,*s=&j;//将i的地址赋给 r</span> 

3.对指针的定义迷糊?

你是不是很奇怪:

int i=5,j=6, *a=&i,*b=&j; 

在这里 *a=&i,*b=j是可以的,但是如果你这么写:

int i=5,j=6,*a,*b;
*a=&i,*b=j; 

这个时候 *a=&i,*b=j就要报错。

为什么?

原因就是我之前说的:你不可以把在 ”int float这样的格式声明后的“”*“理解成为运算符,而是要理解成为一个像”int“这样的格式声明。”int *a,double * n,float *c“这样的搭配含义是”a/b/c是一个指向int/double/float的指针,诸如 int *a=&i这样的声明实际上是 (int *)a=&i。请一定记住这个特殊情况,这样你就不会再迷糊。

4.谨记*a中的*是取(指针a指向的)值运算,&b是取(b所在的内存的)地址运算。在这里字符a存储的是地址,而b存储的是数据,这里再次声明,一定不要被3中提出的“特殊情况”搞混,那只是一个特例,其他情况不可以那样用。

总结

以上就是本文关于C语言中的指针以及二级指针代码详解的全部内容,希望对大家有所帮助。感兴趣的朋友可以继续参阅本站其他相关专题,如有不足之处,欢迎留言指出。感谢朋友们对本站的支持!

(0)

相关推荐

  • C语言中const与指针使用方法总结

    C语言中const与指针使用方法总结 在这里分享一下自己的心得,希望和大家一起分享技术,如果有什么不足,还请大家指正.写出这篇目的,就是希望大家一起成长,我也相信技术之间没有高低,只有互补,只有分享,才能使彼此更加成长. 总结: * const 值不能改变,指向可改变 const * 值能改变,指向不可改变 const * const 都不能改变 实例代码: #include <stdio.h> int main(int argc, const char * argv[]) { // 1 可改

  • 详解Swift中对C语言接口缓存的使用以及数组与字符串转为指针类型的方法

    详解Swift中对C语言接口缓存的使用以及数组与字符串转为指针类型的方法 由于Swift编程语言属于上层编程语言,而Swift中由于为了低层的高性能计算接口,所以往往需要C语言中的指针类型,由此,在Swift编程语言刚诞生的时候就有了UnsafePointer与UnsafeMutablePointer类型,分别对应为const Type*类型与Type *类型. 而在Swift编程语言中,由于一般数组(Array)对象都无法直接用于C语言中含有指针类型的函数参数(比如:void*),所以往往需要

  • C语言指针应用简单实例

    C语言指针应用简单实例 这次来说交换函数的实现: 1. #include <stdio.h> #include <stdlib.h> void swap(int x, int y) { int temp; temp = x; x = y; y = temp; } int main() { int a = 10, b = 20; printf("交换前:\n a = %d, b = %d\n", a, b); swap(a, b); printf("交换

  • C语言指针详解及用法示例

    新手在C语言的学习过程中遇到的最头疼的知识点应该就是指针了,指针在C语言中有非常大的用处.下面我就带着问题来写下我对于指针的一些理解. 指针是什么? 指针本身是一个变量,它存储的是数据在内存中的地址而不是数据本身的值.它的定义如下: int a=10,*p; p=&a int a=10; int *p=&a; 首先我们可以理解 int* 这个是要定义一个指针p,然后因为这个指针存储的是地址所以要对a取地址(&)将值赋给指针p,也就是说这个指针p指向a. 很多新手都会对这两种定义方法

  • C语言中二级指针的实例详解

    C语言中二级指针的实例详解 用图说明 示例代码: #include <stdio.h> int main(int argc, const char * argv[]) { // int a = 5; int *p1 = &a; //-打印地址-----地址相同--------------- printf("&a = %p\n", &a);// printf("p1 = %p\n", p1);// int **p2 = &p

  • C语言中函数指针的三种使用方法总结

     C语言中函数指针的三种使用方法总结 在这里分享一下自己的心得,希望和大家一起分享技术,如果有什么不足,还请大家指正.写出这篇目的,就是希望大家一起成长,我也相信技术之间没有高低,只有互补,只有分享,才能使彼此更加成长. 定义方式:int (*p)(int x, int y); 实现代码: #include <stdio.h> int sum(int x, int y){ return x + y; } int reduce(int x, int y){ return x - y; } int

  • C语言中枚举与指针的实例详解

     C语言中枚举与指针的实例详解 总结一下, 定义枚举,用typedef enum关键字, 比如 typedef enum{Red,Green,Blue} Color3; 枚举到数值的转换,如果没有指定代表数值就是从0开始算, 比如 Color3 c=Red; printf("%d",c);会显示0, 除非指定 如typedef enum{Red=3,Green=5,Blue=10} Color3; 关于类型指针的定义, 定义的时候在变量名左边加*代表此变量只是一个空指针而已, 若需要赋

  • 详解C语言中Char型指针数组与字符数组的区别

    详解C语言中Char型指针数组与字符数组的区别 1.char 类型的指针数组:每个元素都指向一个字符串,指向可以改变 char *name[3] = { "abc", "def", "gbk" }; for(int i = 0 ; i < strlen(name); i ++){ printf("%s\n", *(name+i)); //printf("%s\n", name[i]); } //指向改

  • C语言中带头双向循环链表基本操作的实现详解

    目录 一.概念与结构 二.基本操作的实现 1.创建结点 2.初始化链表 3.打印链表 4.尾插 5.尾删 6.头插 7.头删 8.查找某个数并返回其指针 9.在某个位置之前插入 10.删除某个位置 11.判断链表是否为空 12.计算链表中有效值的个数 13.销毁链表 三.测试代码 一.概念与结构 无头单向非循环链表结构简单,一般不会单独用来存数据.实际中更多的是作为其他数据结构的子结构,如哈希桶.图的邻接表等等.而带头双向循环链表的结构较为复杂,一般用在单独存储数据.实际中使用的链表数据结构,都

  • C语言中函数参数的入栈顺序详解及实例

    C语言中函数参数的入栈顺序详解及实例 对技术执着的人,比如说我,往往对一些问题,不仅想做到"知其然",还想做到"知其所以然".C语言可谓博大精深,即使我已经有多年的开发经验,可还是有许多问题不知其所以然.某天某地某人问我,C语言中函数参数的入栈顺序如何?从右至左,我随口回答.为什么是从右至左呢?我终究没有给出合理的解释.于是,只好做了个作业,于是有了这篇小博文. #include void foo(int x, int y, int z) { printf(&quo

  • JS中实现浅拷贝和深拷贝的代码详解

    (一)JS中基本类型和引用类型 JavaScript的变量中包含两种类型的值:基本类型值 和 引用类型值,在内存中的表现形式在于:前者是存储在栈中的一些简单的数据段,后者则是保存在堆内存中的一个对象. 基本类型值 在JavaScript中基本数据类型有 String , Number , Undefined , Null , Boolean ,在ES6中,又定义了一种新的基本数据类型 Symbol ,所以一共有6种. 基本类型是按值访问的,从一个变量复制基本类型的值到另一个变量后,这两个变量的值

  • C语言实现三子棋的步骤和代码详解

    一.问题描述 用c语言实现三子棋. 二.基本流程 在写三子棋的代码之前,我们来看看实现这个游戏的逻辑: 1.菜单界面选择开始或者退出游戏. 2.创建棋盘并初始化. 3.打印棋盘. 4.玩家落子(玩家输入行列坐标的方式来落子),'x'表示玩家落子. 5.判定胜负关系(输,赢,和棋),'q'表示和棋. 6.电脑落子(随机位置落子) ,'o'表示电脑落子. 7.判定胜负关系. 8.回到 步骤2 继续执行. 三.步骤 1.菜单界面 1.开始游戏 0.退出游戏 int menu(){ printf("--

  • Hibernate中Session增删改查操作代码详解

    把三状态转换图放在这,方便分析方法的作用: 1.Session的save()方法 Session是Hibernate所有接口中最重要的接口,提供了对数据保存,更新,查询和删除的方法. Session的save()方法可以使临时态或游离态转换为持久态.例如,保存一个Customer对象: SessionFactory sessionFactory; Configuration configuration = new Configuration().configure(); sessionFacto

  • vue中使用mxgraph的方法实例代码详解

    1.npm 引入 npm install mxgraph --save 2.这个模块可以使用require()方法进行加载.它将返回一个接受对象作为选项的工厂函数.必须将mxBasePath选项提供给工厂函数,而不是将其定义为一个全局变量. var mxgraph = require("mxgraph")( { // 以下地址不需要修改 mxImageBasePath: "./src/images", mxBasePath: "./src" })

  • vue中datepicker的使用教程实例代码详解

    写这个文章主要是记录下用法,官网已经说的很详细了 npm install vue-datepicker --save html代码 <myDatepicker :date="startTime" :option="multiOption" :limit="limit"></myDatepicker> <myDatepicker :date="endtime" :option="timeo

  • java使用FFmpeg合成视频和音频并获取视频中的音频等操作(实例代码详解)

    FFmpeg是一套可以用来记录.转换数字音频.视频,并能将其转化为流的开源计算机程序. ffmpeg命令参数如下: 通用选项 -L license -h 帮助 -fromats 显示可用的格式,编解码的,协议的... -f fmt 强迫采用格式fmt -I filename 输入文件 -y 覆盖输出文件 -t duration 设置纪录时间 hh:mm:ss[.xxx]格式的记录时间也支持 -ss position 搜索到指定的时间 [-]hh:mm:ss[.xxx]的格式也支持 -title

  • 微信小程序中的列表切换功能实例代码详解

    感觉这列表切换有点类似于轮播图,而且感觉这代码直接可以拿来用,稍微改一改样式什么的就OK了,列表切换也是用到的地方也很多 wxml中的代码如下: <!-- 标签页面标题 --> <view class="tab"> <view class="tab-item {{tab==0?'active':''}}" bindtap="changeItem" data-item="0">音乐推荐<

  • c++语言中虚函数实现多态的原理详解

    前言 自上一个帖子之间跳过了一篇总结性的帖子,之后再发,今天主要研究了c++语言当中虚函数对多态的实现,感叹于c++设计者的精妙绝伦 c++中虚函数表的作用主要是实现了多态的机制.首先先解释一下多态的概念,多态是c++的特点之一,关于多态,简而言之就是 用父类的指针指向其子类的实例,然后通过父类的指针调用实际子类的成员函数,这种方法呢,可以让父类的指针具有多种形态,也就是说不需要改动很多的代码就可以让父类这一种指针,干一些很多子类指针的事情,这里是从虚函数的实现机制层面进行研究 在写这篇帖子之前

随机推荐