迅闻网
让更多人看到你

c语言指针详解(C语言中的指针是什么)

  c语言指针详解

学C语言一定要把指针、内存搞透彻,否则C语言就发挥不出它强大的威力!
比如泛型编程、OOP等在C语言中全靠函数指针、指针。
很多时候正是指针的存在大大的简化了我们的编码,当然越大的自由意味着使用者也需要付出的大代价,比如防不胜防的缓冲区溢出问题……
在C语言里,变量存放在内存中,而内存其实就是一组有序字节组成的数组,每个字节有唯一的内存地址。
CPU通过内存寻址对存储在内存中的某个指定数据对象的地址进行定位。
这里,数据对象是指存储在内存中的一个指定数据类型的数值或字符串,它们都有一个自己的地址,而指针便是保存这个地址的变量。
也就是说:指针是一种保存变量地址的变量。
指针很简单,说白了就跟快递柜似的
快递柜大家都用过吧,丰巢或者超市储物柜都是这样,每个格子都有一个编号,我们只需要拿到编号,然后就能找到对应的格子,取出里面的东西。
这里的格子就是内存单元,编号就是地址,格子里放的东西就对应存储在内存中的内容。
为什么要使用指针?
在C语言中,指针的使用非常广泛,因为使用指针往往可以生成更高效、更紧凑的代码。总的来说,使用指针有如下好处:
1)指针的使用使得不同区域的代码可以轻易的共享内存数据,这样可以使程序更为快速高效;
2)C语言中一些复杂的数据结构往往需要使用指针来构建,如链表、二叉树等;
3)C语言是传值调用,而有些操作传值调用是无法完成的,如通过被调函数修改调用函数的对象,但是这种操作可以由指针来完成,而且并不违背传值调用。
很多同学不理解指针,那么看完这个回答就会让你解开以前的迷雾了~~
这篇回答的主题是「指针与内存模型」
说到指针,就不可能脱离开内存,学会指针的人分为两种,一种是不了解内存模型,另外一种则是了解。
不了解的对指针的理解就停留在“指针就是变量的地址”这句话,会比较害怕使用指针,特别是各种高级操作。
而了解内存模型的则可以把指针用得炉火纯青,各种byte随意操作,让人直呼666。
一、内存本质
编程的本质其实就是操控数据,数据存放在内存中。
因此,如果能更好地理解内存的模型,以及C如何管理内存,就能对程序的工作原理洞若观火,从而使编程能力更上一层楼。
大家真的别认为这是空话,我大一整年都不敢用C写上千行的程序也很抗拒写C。
因为一旦上千行,经常出现各种莫名其妙的内存错误,一不小心就发生了coredump……而且还无从排查,分析不出原因。
相比之下,那时候最喜欢Java,在Java里随便怎么写都不会发生类似的异常,顶多偶尔来个NullPointerException,也是比较好排查的。
直到后来对内存和指针有了更加深刻的认识,才慢慢会用C写上千行的项目,也很少会再有内存问题了。(过于自信
「指针存储的是变量的内存地址」这句话应该任何讲C语言的书都会提到吧。
所以,要想彻底理解指针,首先要理解C语言中变量的存储本质,也就是内存。
1.1内存编址
计算机的内存是一块用于存储数据的空间,由一系列连续的存储单元组成,就像下面这样,
每一个单元格都表示1个Bit,一个bit在EE专业的同学看来就是高低电位,而在CS同学看来就是0、1两种状态。
由于1个bit只能表示两个状态,所以大佬们规定8个bit为一组,命名为byte。
并且将byte作为内存寻址的最小单元,也就是给每个byte一个编号,这个编号就叫内存的地址。
这就相当于,我们给小区里的每个单元、每个住户都分配一个门牌号:301、302、403、404、501……
在生活中,我们需要保证门牌号唯一,这样就能通过门牌号很精准的定位到一家人。
同样,在计算机中,我们也要保证给每一个byte的编号都是唯一的,这样才能够保证每个编号都能访问到唯一确定的byte。
1.2内存地址空间
上面我们说给内存中每个byte唯一的编号,那么这个编号的范围就决定了计算机可寻址内存的范围。
所有编号连起来就叫做内存的地址空间,这和大家平时常说的电脑是32位还是64位有关。
早期Intel8086、8088的CPU就是只支持16位地址空间,寄存器和地址总线都是16位,这意味着最多对2^16=64Kb的内存编号寻址。
这点内存空间显然不够用,后来,80286在8086的基础上将地址总线和地址寄存器扩展到了20位,也被叫做A20地址总线。
当时在写minios的时候,还需要通过BIOS中断去启动A20地址总线的开关。
但是,现在的计算机一般都是32位起步了,32位意味着可寻址的内存范围是2^32byte=4GB。
所以,如果你的电脑是32位的,那么你装超过4G的内存条也是无法充分利用起来的。
好了,这就是内存和内存编址。
1.3变量的本质
有了内存,接下来我们需要考虑,int、double这些变量是如何存储在0、1单元格的。
在C语言中我们会这样定义变量:
inta=999;
charc=’c’;
当你写下一个变量定义的时候,实际上是向内存申请了一块空间来存放你的变量。
我们都知道int类型占4个字节,并且在计算机中数字都是用补码(不了解补码的记得去百度)表示的。
999换算成补码就是:0000001111100111
这里有4个byte,所以需要四个单元格来存储:
有没有注意到,我们把高位的字节放在了低地址的地方。
那能不能反过来呢?
当然,这就引出了大端和小端。
像上面这种将高位字节放在内存低地址的方式叫做大端
反之,将低位字节放在内存低地址的方式就叫做小端:
上面只说明了int型的变量如何存储在内存,而float、char等类型实际上也是一样的,都需要先转换为补码。
对于多字节的变量类型,还需要按照大端或者小端的格式,依次将字节写入到内存单元。
记住上面这两张图,这就是编程语言中所有变量的在内存中的样子,不管是int、char、指针、数组、结构体、对象…都是这样放在内存的。

 

c
二、指针是什么东西?
2.1变量放在哪?
上面我说,定义一个变量实际就是向计算机申请了一块内存来存放。
那如果我们要想知道变量到底放在哪了呢?
可以通过运算符&来取得变量实际的地址,这个值就是变量所占内存块的起始地址。
(PS:实际上这个地址是虚拟地址,并不是真正物理内存上的地址
我们可以把这个地址打印出来:
printf(“%x”,&a);
大概会是像这样的一串数字:0x7ffcad3b8f3c
2.2指针本质
上面说,我们可以通过&符号获取变量的内存地址,那获取之后如何来表示这是一个地址,而不是一个普通的值呢?
也就是在C语言中如何表示地址这个概念呢?
对,就是指针,你可以这样:
int*pa=&a;
pa中存储的就是变量a的地址,也叫做指向a的指针。
在这里我想谈几个看起来有点无聊的话题:
为什么我们需要指针?直接用变量名不行吗?
当然可以,但是变量名是有局限的。
变量名的本质是什么?
是变量地址的符号化,变量是为了让我们编程时更加方便,对人友好,可计算机可不认识什么变量a,它只知道地址和指令。
所以当你去查看C语言编译后的汇编代码,就会发现变量名消失了,取而代之的是一串串抽象的地址。
你可以认为,编译器会自动维护一个映射,将我们程序中的变量名转换为变量所对应的地址,然后再对这个地址去进行读写。
也就是有这样一个映射表存在,将变量名自动转化为地址:
a|0x7ffcad3b8f3c
c|0x7ffcad3b8f2c
h|0x7ffcad3b8f4c
说的好!
可是我还是不知道指针存在的必要性,那么问题来了,看下面代码:
intfunc(…){

};
intmain(){
inta;
func(…);
};
假设我有一个需求:
要求在func函数里要能够修改main函数里的变量a,这下咋整,在main函数里可以直接通过变量名去读写a所在内存。但是在func函数里是看不见a的呀。
你说可以通过&取地址符号,将a的地址传递进去:
intfunc(intaddress){
….
};
intmain(){
inta;
func(&a);
};
这样在func里就能获取到a的地址,进行读写了。
理论上这是完全没有问题的,但是问题在于:
编译器该如何区分一个int里你存的到底是int类型的值,还是另外一个变量的地址(即指针)。
这如果完全靠我们编程人员去人脑记忆了,会引入复杂性,并且无法通过编译器检测一些语法错误。
而通过int*去定义一个指针变量,会非常明确:这就是另外一个int型变量的地址。
编译器也可以通过类型检查来排除一些编译错误。
这就是指针存在的必要性。
实际上任何语言都有这个需求,只不过很多语言为了安全性,给指针戴上了一层枷锁,将指针包装成了引用。
可能大家学习的时候都是自然而然的接受指针这个东西,但是还是希望这段啰嗦的解释对你有一定启发。
同时,在这里提点小问题:
既然指针的本质都是变量的内存首地址,即一个int类型的整数。
那为什么还要有各种类型呢?比如int指针,float指针,这个类型影响了指针本身存储的信息吗?这个类型会在什么时候发挥作用?
2.3解引用
上面的问题,就是为了引出指针解引用的。
pa中存储的是a变量的内存地址,那如何通过地址去获取a的值呢?
这个操作就叫做解引用,在C语言中通过运算符*就可以拿到一个指针所指地址的内容了。
比如*pa就能获得a的值。
我们说指针存储的是变量内存的首地址,那编译器怎么知道该从首地址开始取多少个字节呢?
这就是指针类型发挥作用的时候,编译器会根据指针的所指元素的类型去判断应该取多少个字节。
如果是int型的指针,那么编译器就会产生提取四个字节的指令,char则只提取一个字节,以此类推。
下面是指针内存示意图:
pa指针首先是一个变量,它本身也占据一块内存,这块内存里存放的就是a变量的首地址。
当解引用的时候,就会从这个首地址连续划出4个byte,然后按照int类型的编码方式解释。
2.4活学活用
别看这个地方很简单,但却是深刻理解指针的关键。
举两个例子来详细说明:
比如:
floatf=1.0;
shortc=*(short*)&f;
你能解释清楚上面过程,对于f变量,在内存层面发生了什么变化吗?
或者c的值是多少?1?
实际上,从内存层面来说,f什么都没变。
如图:
假设这是f在内存中的位模式,这个过程实际上就是把f的前两个byte取出来然后按照short的方式解释,然后赋值给c。
详细过程如下:
&f取得f的首地址
(short*)&f
上面第二步什么都没做,这个表达式只是说:
“噢,我认为f这个地址放的是一个short类型的变量”
最后当去解引用的时候*(short*)&f时,编译器会取出前面两个字节,并且按照short的编码方式去解释,并将解释出的值赋给c变量。
这个过程f的位模式没有发生任何改变,变的只是解释这些位的方式。
当然,这里最后的值肯定不是1,至于是什么,大家可以去真正算一下。
那反过来,这样呢?
shortc=1;
floatf=*(float*)&c;
如图:
具体过程和上述一样,但上面肯定不会报错,这里却不一定。
为什么?
(float*)&c会让我们从c的首地址开始取四个字节,然后按照float的编码方式去解释。
但是c是short类型只占两个字节,那肯定会访问到相邻后面两个字节,这时候就发生了内存访问越界。
当然,如果只是读,大概率是没问题的。
但是,有时候需要向这个区域写入新的值,比如:
*(float*)&c=1.0;
那么就可能发生coredump,也就是访存失败。
另外,就算是不会coredump,这种也会破坏这块内存原有的值,因为很可能这是是其它变量的内存空间,而我们去覆盖了人家的内容,肯定会导致隐藏的bug。
如果你理解了上面这些内容,那么使用指针一定会更加的自如。
2.6看个小问题
讲到这里,我们来看一个问题,这是一位群友问的,这是他的需求:
这是他写的代码:
他把double写进文件再读出来,然后发现打印的值对不上。
而关键的地方就在于这里:
charbuffer[4];

printf(“%f%x\n”,*buffer,*buffer);
他可能认为buffer是一个指针(准确说是数组),对指针解引用就该拿到里面的值,而里面的值他认为是从文件读出来的4个byte,也就是之前的float变量。
注意,这一切都是他认为的,实际上编译器会认为:
“哦,buffer是char类型的指针,那我取第一个字节出来就好了”。
然后把第一个字节的值传递给了printf函数,printf函数会发现,%f要求接收的是一个float浮点数,那就会自动把第一个字节的值转换为一个浮点数打印出来。
这就是整个过程。
错误关键就是,这个同学误认为,任何指针解引用都是拿到里面“我们认为的那个值”,实际上编译器并不知道,编译器只会傻傻的按照指针的类型去解释。
所以这里改成:
printf(“%f%x\n”,*(float*)buffer,*(float*)buffer);
相当于明确的告诉编译器:
“buffer指向的这个地方,我放的是一个float,你给我按照float去解释”
三、结构体和指针
结构体内包含多个成员,这些成员之间在内存中是如何存放的呢?
比如:
structfraction{
intnum;//整数部分
intdenom;//小数部分
};
structfractionfp;
fp.num=10;
fp.denom=2;
这是一个定点小数结构体,它在内存占8个字节(这里不考虑内存对齐),两个成员域是这样存储的:
我们把10放在了结构体中基地址偏移为0的域,2放在了偏移为4的域。
接下来我们做一个正常人永远不会做的操作:
((fraction*)(&fp.denom))->num=5;
((fraction*)(&fp.denom))->denom=12;
printf(“%d\n”,fp.denom);//输出多少?
上面这个究竟会输出多少呢?自己先思考下噢~
接下来我分析下这个过程发生了什么:
首先,&fp.denom表示取结构体fp中denom域的首地址,然后以这个地址为起始地址取8个字节,并且将它们看做一个fraction结构体。
在这个新结构体中,最上面四个字节变成了denom域,而fp的denom域相当于新结构体的num域。
因此:
((fraction*)(&fp.denom))->num=5
实际上改变的是fp.denom,而
((fraction*)(&fp.denom))->denom=12
则是将最上面四个字节赋值为12。
当然,往那四字节内存写入值,结果是无法预测的,可能会造成程序崩溃,因为也许那里恰好存储着函数调用栈帧的关键信息,也可能那里没有写入权限。
大家初学C语言的很多coredump错误都是类似原因造成的。
所以最后输出的是5。
为什么要讲这种看起来莫名其妙的代码?
就是为了说明结构体的本质其实就是一堆的变量打包放在一起,而访问结构体中的域,就是通过结构体的起始地址,也叫基地址,然后加上域的偏移。
其实,C++、Java中的对象也是这样存储的,无非是他们为了实现某些面向对象的特性,会在数据成员以外,添加一些Head信息,比如C++的虚函数表。
实际上,我们是完全可以用C语言去模仿的。
这就是为什么一直说C语言是基础,你真正懂了C指针和内存,对于其它语言你也会很快的理解其对象模型以及内存布局。
四、多级指针
说起多级指针这个东西,我以前大一,最多理解到2级,再多真的会把我绕晕,经常也会写错代码。
你要是给我写个这个:int******p能把我搞崩溃,我估计很多同学现在就是这种情况
其实,多级指针也没那么复杂,就是指针的指针的指针的指针……非常简单。
今天就带大家认识一下多级指针的本质。
首先,我要说一句话,没有多级指针这种东西,指针就是指针,多级指针只是为了我们方便表达而取的逻辑概念。
首先看下生活中的快递柜:
这种大家都用过吧,丰巢或者超市储物柜都是这样,每个格子都有一个编号,我们只需要拿到编号,然后就能找到对应的格子,取出里面的东西。
这里的格子就是内存单元,编号就是地址,格子里放的东西就对应存储在内存中的内容。
假设我把一本书,放在了03号格子,然后把03这个编号告诉你,你就可以根据03去取到里面的书。
那如果我把书放在05号格子,然后在03号格子只放一个小纸条,上面写着:「书放在05号」。
你会怎么做?
当然是打开03号格子,然后取出了纸条,根据上面内容去打开05号格子得到书。
这里的03号格子就叫指针,因为它里面放的是指向其它格子的小纸条(地址)而不是具体的书。
明白了吗?
那我如果把书放在07号格子,然后在05号格子放一个纸条:「书放在07号」,同时在03号格子放一个纸条「书放在05号」
这里的03号格子就叫二级指针,05号格子就叫指针,而07号就是我们平常用的变量。
依次,可类推出N级指针。
所以你明白了吗?同样的一块内存,如果存放的是别的变量的地址,那么就叫指针,存放的是实际内容,就叫变量。
inta;
int*pa=&a;
int**ppa=&pa;
int***pppa=&ppa;
上面这段代码,pa就叫一级指针,也就是平时常说的指针,ppa就是二级指针。
内存示意图如下:
不管几级指针有两个最核心的东西:
指针本身也是一个变量,需要内存去存储,指针也有自己的地址
指针内存存储的是它所指向变量的地址
这就是我为什么多级指针是逻辑上的概念,实际上一块内存要么放实际内容,要么放其它变量地址,就这么简单。
怎么去解读int**a这种表达呢?
int**a可以把它分为两部分看,即int*和*a,后面*a中的*表示a是一个指针变量,前面的int*表示指针变量a
只能存放int*型变量的地址。
对于二级指针甚至多级指针,我们都可以把它拆成两部分。
首先不管是多少级的指针变量,它首先是一个指针变量,指针变量就是一个*,其余的*表示的是这个指针变量只能存放什么类型变量的地址。
比如int****a表示指针变量a只能存放int***型变量的地址。
五、指针与数组
5.1一维数组
数组是C自带的基本数据结构,彻底理解数组及其用法是开发高效应用程序的基础。
数组和指针表示法紧密关联,在合适的上下文中可以互换。
如下:
intarray[10]={10,9,8,7};
printf(“%d\n”,*array);//输出10
printf(“%d\n”,array[0]);//输出10
printf(“%d\n”,array[1]);//输出9
printf(“%d\n”,*(array+1));//输出9
int*pa=array;
printf(“%d\n”,*pa);//输出10
printf(“%d\n”,pa[0]);//输出10
printf(“%d\n”,pa[1]);//输出9
printf(“%d\n”,*(pa+1));//输出9
在内存中,数组是一块连续的内存空间:
第0个元素的地址称为数组的首地址,数组名实际就是指向数组首地址,当我们通过array[1]或者*(array+1)去访问数组元素的时候。
实际上可以看做address[offset],address为起始地址,offset为偏移量,但是注意这里的偏移量offset不是直接和address相加,而是要乘以数组类型所占字节数,也就是:address+sizeof(int)*offset。
学过汇编的同学,一定对这种方式不陌生,这是汇编中寻址方式的一种:基址变址寻址。
看完上面的代码,很多同学可能会认为指针和数组完全一致,可以互换,这是完全错误的。
尽管数组名字有时候可以当做指针来用,但数组的名字不是指针。
最典型的地方就是在sizeof:
printf(“%u”,sizeof(array));
printf(“%u”,sizeof(pa));
第一个将会输出40,因为array包含有10个int类型的元素,而第二个在32位机器上将会输出4,也就是指针的长度。
为什么会这样呢?
站在编译器的角度讲,变量名、数组名都是一种符号,它们都是有类型的,它们最终都要和数据绑定起来。
变量名用来指代一份数据,数组名用来指代一组数据(数据集合),它们都是有类型的,以便推断出所指代的数据的长度。
对,数组也有类型,我们可以将int、float、char等理解为基本类型,将数组理解为由基本类型派生得到的稍微复杂一些的类型,
数组的类型由元素的类型和数组的长度共同构成。而sizeof就是根据变量的类型来计算长度的,并且计算的过程是在编译期,而不会在程序运行时。
编译器在编译过程中会创建一张专门的表格用来保存变量名及其对应的数据类型、地址、作用域等信息。
sizeof是一个操作符,不是函数,使用sizeof时可以从这张表格中查询到符号的长度。
所以,这里对数组名使用sizeof可以查询到数组实际的长度。
pa仅仅是一个指向int类型的指针,编译器根本不知道它指向的是一个整数,还是一堆整数。
虽然在这里它指向的是一个数组,但数组也只是一块连续的内存,没有开始和结束标志,也没有额外的信息来记录数组到底多长。
所以对pa使用sizeof只能求得的是指针变量本身的长度。
也就是说,编译器并没有把pa和数组关联起来,pa仅仅是一个指针变量,不管它指向哪里,sizeof求得的永远是它本身所占用的字节数。
5.2二维数组
大家不要认为二维数组在内存中就是按行、列这样二维存储的,实际上,不管二维、三维数组…都是编译器的语法糖。
存储上和一维数组没有本质区别,举个例子:
intarray[3][3]={{1,2,3},{4,5,6},{7,8,9}};
array[1][1]=5;
或许你以为在内存中array数组会像一个二维矩阵:
123
456
789
可实际上它是这样的:
123456789
和一维数组没有什么区别,都是一维线性排列。
当我们像array[1][1]这样去访问的时候,编译器会怎么去计算我们真正所访问元素的地址呢?
为了更加通用化,假设数组定义是这样的:
intarray[n][m]
访问:array[a][b]
那么被访问元素地址的计算方式就是:array+(m*a+b)
这个就是二维数组在内存中的本质,其实和一维数组是一样的,只是语法糖包装成一个二维的样子。
六、神奇的void指针
想必大家一定看到过void的这些用法:
voidfunc();
intfunc1(void);
在这些情况下,void表达的意思就是没有返回值或者参数为空。
但是对于void型指针却表示通用指针,可以用来存放任何数据类型的引用。
下面的例子就是一个void指针:
void*ptr;
void指针最大的用处就是在C语言中实现泛型编程,因为任何指针都可以被赋给void指针,void指针也可以被转换回原来的指针类型,并且这个过程指针实际所指向的地址并不会发生变化。
比如:
intnum;
int*pi=#
printf(“addressofpi:%p\n”,pi);
void*pv=pi;
pi=(int*)pv;
printf(“addressofpi:%p\n”,pi);
这两次输出的值都会是一样:
平常可能很少会这样去转换,但是当你用C写大型软件或者写一些通用库的时候,一定离不开void指针,这是C泛型的基石,比如std库里的sort函数申明是这样的:
voidqsort(void*base,intnelem,intwidth,int(*fcmp)(constvoid*,constvoid*));
所有关于具体元素类型的地方全部用void代替。
void还可以用来实现C语言中的多态,这是一个挺好玩的东西。
不过也有需要注意的:
不能对void指针解引用
比如:
intnum;
void*pv=(void*)#
*pv=4;//错误
为什么?
因为解引用的本质就是编译器根据指针所指的类型,然后从指针所指向的内存连续取N个字节,然后将这N个字节按照指针的类型去解释。
比如int*型指针,那么这里N就是4,然后按照int的编码方式去解释数字。
但是void,编译器是不知道它到底指向的是int、double、或者是一个结构体,所以编译器没法对void型指针解引用。
七、花式秀技
很多同学认为C就只能面向过程编程,实际上利用指针,我们一样可以在C中模拟出对象、继承、多态等东西。
也可以利用void指针实现泛型编程,也就是Java、C++中的模板。
大家如果对C实现面向对象、模板、继承这些感兴趣的话,可以积极一点,点赞,留言~呼声高的话,我就再写一篇。
实际上也是很有趣的东西,当你知道了如何用C去实现这些东西,那你对C++中的对象、Java中的对象也会理解得更加透彻。
比如为啥有this指针,或者Python中的self究竟是个啥?
关于指针想写的内容还有很多,这其实也只算是开了个头,限于篇幅,以后有机会补齐以下内容:
二维数组和二维指针
数组指针和指针数组
指针运算
函数指针
动态内存分配:malloc和free
堆、栈
函数参数传递方式
内存泄露
数组退化成指针
const修饰指针

C语言中的指针是什么

其实想了好久,很纠结要不要写一篇关于C言语指针的文章,我很担心自己把它讲得不清楚,众所周知C言语编程的魅力正是指针,难度也在指针,许多触摸很久的C言语都不能说通晓他,可是今日我决议尝试一下把C言语的指针尽或许地去讲清楚,篇幅或许有一点长,希望你耐性看完,有缺乏的地方能够一起探讨。
首先咱们先记住一个概念:指针也是一个变量,只是这个变量的值比较特殊,它寄存的是地址;
指针和内存
咱们结合一个比如来讲指针:
int*p;
在这儿咱们界说了一个指针p,可是现在没没有对它进行初始化,在C言语中编译进程中这条句子会给p开辟一块内存空间,空间巨细呢由类型决议在32位系统上int为四个字节,所以现在就为p变量开辟了巨细为4个字节的空间,而且限定了这4个字节里边只能寄存某个内存的地址(什么意思?便是说请求的空间只能寄存地址,而不是具体数值),即便你存入其他任何数据都将会被当作地址处理,而且请求的这个内存地址开端接连的4个字节上只能存某个int类型的数据。
int*p//p只能寄存int类型的地址
char*p//p只能寄存cahr类型的地址
float*p//p只能寄存float类型的地址
这样看理解了么?
也便是说你声明一个指针的时分:请求的类型是什么类型他便是什么类型的指针。
给你举个比如:
inta;
a=10.12345;
printf(“a=%d\n”,a);
你觉得a打印出来的值是多少?最开端声明的类型是整型,你给他一个浮点型的数据,他也只会当作整型去处理,成果输出来的是10.指针变量也是也相同的,请求的时分是什么类型,最好就给他什么样的类型地址,否则或许不会打到你预期的效果。
指针的运用
咱们来看一下面的代码:
inta=10;
int*p;
p=&a;
printf(“a=%dp=%d”,a,*p);
&符号为取地址符:&a便是取a的地址;
这儿咱们把a的地址赋给指针变量p,咱们能够理解现在指针变量p里边寄存的是a的地址(p指针指向a的地址)那咱们现在咱们就有两种办法能够得到咱们的数据10;
一个是变量名:a另外一个便是指针p,星号p(*p)的意思是把指针p里边的值取出来,那么咱们都知道现在p里边寄存的是a的地址程序取值的时分直接根据地址去内存里边把值取出来。
举一个比如:图书馆里边有一排排书架寄存着许多书籍,这时分教师叫你去图书馆拿书,可是怕你忘记了,然后他给你一个写了一张纸条上面写着“2号书架第3本”,然后你就拿着纸条去图书馆找到书而且拿回来给你教师,这个进程叫什么?
这个进程你充任的便是一个指针,手里拿的纸条便是地址,图书馆书架书架上那本书便是你要读取的数据。
什么叫类型不一致,你教师最开端叫你去图书馆取书,可是你拿着纸条去药店,药店也是货架,成果你拿到了2号架子上第3个药,虽然你也是拿到东西了,可是是你教师想要的么?
总结一下:
1、指针也是变量。
2、指针里边放的是地址;(你把哪个变量的地址给他他就存谁的地址)。
3、运用指针时要和声明的类型一致。

未经允许不得转载:迅闻网 » c语言指针详解(C语言中的指针是什么)
分享到: 更多 (0)

评论 抢沙发

评论前必须登录!

 

迅闻网-让更多人看到你

登录/注册返回首页