0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

C语言指针详解

GReq_mcu168 来源:玩转单片机 2020-08-05 10:17 次阅读

1为什么使用指针

假如我们定义了 char a=’A’ ,当需要使用 ‘A’ 时,除了直接调用变量 a ,还可以定义 char *p=&a ,调用 a 的地址,即指向 a 的指针 p ,变量 a( char 类型)只占了一个字节,指针本身的大小由可寻址的字长来决定,指针 p 占用 4 个字节。

但如果要引用的是占用内存空间比较大东西,用指针也还是 4 个字节即可。

使用指针型变量在很多时候占用更小的内存空间。

变量为了表示数据,指针可以更好的传递数据,举个例子:

第一节课是 1 班语文, 2 班数学,第二节课颠倒过来, 1 班要上数学, 2 班要上语文,那么第一节课下课后需要怎样作调整呢?方案一:课间 1 班学生全都去 2 班, 2 班学生全都来 1 班,当然,走的时候要携带上书本、笔纸、零食……场面一片狼藉;方案二:两位老师课间互换教室。

显然,方案二更好一些,方案二类似使用指针传递地址,方案一将内存中的内容重新“复制”了一份,效率比较低。

在数据传递时,如果数据块较大,可以使用指针传递地址而不是实际数据,即提高传输速度,又节省大量内存。

一个数据缓冲区 char buf[100] ,如果其中 buf[0,1] 为命令号, buf[2,3] 为数据类型, buf[4~7] 为该类型的数值,类型为 int ,使用如下语句进行赋值:

*(short*)&buf[0]=DataId; *(short*)&buf[2]=DataType; *(int*)&buf[4]=DataValue;

数据转换,利用指针的灵活的类型转换,可以用来做数据类型转换,比较常用于通讯缓冲区的填充。

指针的机制比较简单,其功能可以被集中重新实现成更抽象化的引用数据形式

函数指针,形如: #define PMYFUN (void*)(int,int) ,可以用在大量分支处理的实例当中,如某通讯根据不同的命令号执行不同类型的命令,则可以建立一个函数指针数组,进行散转。

在数据结构中,链表、树、图等大量的应用都离不开指针。

2 指针是什么?

操作系统硬件和软件结合起来,给程序员提供的一种对内存使用的抽象,这种抽象机制使得程序使用的是虚拟存储器,而不是直接操作和使用真实存在的物理存储器。所有的虚拟地址形成的集合就是虚拟地址空间。

内存是一个很大的线性的字节数组,每个字节固定由 8 个二进制位组成,每个字节都有唯一的编号,如下图,这是一个 4G 的内存,他一共有 4x1024x1024x1024 = 4294967296 个字节,那么它的地址范围就是 0 ~ 4294967296 ,十六进制表示就是 0x00000000~0xffffffff ,当程序使用的数据载入内存时,都有自己唯一的一个编号,这个编号就是这个数据的地址。指针就是这样形成的。

1

#include intmain(void) { charch='a'; intnum=97; printf("ch的地址:%p ",&ch); //ch的地址:00BEFDF7 printf("num的地址:%p ",&num); //num的地址:00BEFDF8 return0; }

指针不仅可以表示变量的地址,还可以存储各种类型数据的地址,指针变量是用来保存这些地址的变量,与数组类似,依据地址存放的数据类型,指针也分为 int 指针类型, double 指针类型, char 指针类型等等。

综上,指针的实质就是数据在内存中的地址,而指针变量是用来保存这些地址的变量。

指针变量 和 指向关系

用来保存 指针 的变量,就是指针变量。如果指针变量p保存了变量 num的地址,则就说:p指向了变量num,也可以说p指向了num所在的内存块,指针变量pp指向了p所在的内存块,以下面为例:

#include intmain(void) { intnum=97; charch='a'; int*p=# int**pp=&p; char*p1=&ch; printf("num的地址:%p ",&num); printf("指针p的值:%p ",p); printf("指针p的地址:%p ",&p); printf("指针pp的值:%p ",pp); printf("ch的地址:%p ",&ch); return0; }

运行结果

int型的num值为97占4个字节,内存地址为:0113F924,char 型的ch('a')值为97占1个字节,内存地址为:0113F91B。

int型占4个字节

char型占1个字节

num的地址为:0113F924,num的值为 97 ,指针 p 指向 num 的内存块,指针 p 地址为:0113F90C,p的内存保存的值就是num的地址0113F924。

0x0113F90C存储的内容为地址0113F924

指针变量 pp 指向 指针 p,指针 pp 内存值为 指针 p 的地址:0113F90C,形成了只想指针的指针。

指针pp为指向指针p的指针

定义指针变量

C语言中,定义变量时,在变量名 前 写一个 * 星号,这个变量就变成了对应变量类型的指针变量。必要时要加( ) 来避免优先级的问题。

引申:C语言中,定义变量时,在定义的最前面写上typedef ,那么这个变量名就成了一种类型,即这个类型的同义词。

inta;//int类型变量a int*a;//int*变量a intarr[3];//arr是包含3个int元素的数组 int(*arr)[3];//arr是一个指向包含3个int元素的数组的指针变量 int*p_int;//指向int类型变量的指针 double*p_double;//指向idouble类型变量的指针 structStudent*p_struct;//结构体类型的指针 int(*p_func)(int,int);//指向返回类型为int,有2个int形参的函数的指针 int(*p_arr)[3];//指向含有3个int元素的数组的指针 int**p_pointer;//指向一个整形变量指针的指针

取地址

既然有了指针变量,那就得让他保存其它变量的地址,使用& 运算符取得一个变量的地址。

intadd(inta,intb) { returna+b; } intmain(void) { intnum=97; floatscore=10.00F; intarr[3]={1,2,3}; int*p_num=# float*p_score=&score; int(*p_arr)[3]=&arr; int(*fp_add)(int,int)=add;//p_add是指向函数add的函数指针 return0; }

特殊的情况,他们并不一定需要使用&取地址:

数组名的值就是这个数组的第一个元素的地址。

函数名的值就是这个函数的地址。

字符串字面值常量作为右值时,就是这个字符串对应的字符数组的名称,也就是这个字符串在内存中的地址。

intadd(inta,intb){ returna+b; } intmain(void) { intarr[3]={1,2,3}; int*p_first=arr; int(*fp_add)(int,int)=add; constchar*msg="Helloworld"; return0; }

解地址

对一个指针解地址,就可以取到这个内存数据,解地址 的写法,就是在指针的前面加一个 * 号。

解指针的实质是:从指针指向的内存块中取出这个内存数据。

intmain(void) { intage=19; int*p_age=&age; *p_age=20;//通过指针修改指向的内存数据 printf("age=%d",*p_age);//通过指针读取指向的内存数据 printf("age=%d",age); return0; }

空指针

空指针在概念上不同于未初始化的指针。空指针可以确保不指向任何对象或函数;而未初始化的指针则可能指向任何地方。空指针不是野指针。

在C语言中,我们让指针变量赋值为NULL表示一个空指针,而C语言中,NULL实质是 ((void*)0) , 在C++中,NULL实质是0。

#ifdef__cplusplus #defineNULL0 #else #defineNULL((void*)0) #endif

void*类型指针

void是一种特殊的指针类型,可以用来存放任意对象的地址。一个void指针存放着一个地址,这一点和其他指针类似。不同的是,我们对它到底储存的是什么对象的地址并不了解。

doublea=2.3; intb=5; void*p=&a; cout<

由于void是空类型,只保存了指针的值,而丢失了类型信息,我们不知道他指向的数据是什么类型的,只指定这个数据在内存中的起始地址,如果想要完整的提取指向的数据,程序员就必须对这个指针做出正确的类型转换,然后再解指针。

数组和指针

同类型指针变量可以相互赋值,数组不行,只能一个一个元素的赋值或拷贝

数组在内存中是连续存放的,开辟一块连续的内存空间。数组是根据数组的下进行访问的。指针很灵活,它可以指向任意类型的数据。指针的类型说明了它所指向地址空间的内存。

数组所占存储空间的内存:sizeof(数组名) 数组的大小:sizeof(数组名)/sizeof(数据类型),在32位平台下,无论指针的类型是什么,sizeof(指针名)都是 4 ,在 64 位平台下,无论指针的类型是什么,sizeof(指针名)都是 8 。

数组名作为右值的时候,就是第一个元素的地址

intmain(void) { intarr[5]={1,2,3,4,5}; int*p_first=arr; printf("%d",*p_first);//1 return0; }

指向数组元素的指针 支持 递增 递减 运算。p= p+1意思是,让p指向原来指向的内存块的下一个相邻的相同类型的内存块。在数组中相邻内存就是相邻下标元素。

函数与指针

函数的参数和指针

C语言中,实参传递给形参,是按值传递的,也就是说,函数中的形参是实参的拷贝份,形参和实参只是在值上面一样,而不是同一个内存数据对象。这就意味着:这种数据传递是单向的,即从调用者传递给被调函数,而被调函数无法修改传递的参数达到回传的效果。

voidchange(inta) { a++;//在函数中改变的只是这个函数的局部变量a,而随着函数执行结束,a被销毁。age还是原来的age,纹丝不动。 } intmain(void) { intage=60; change(age); printf("age=%d",age);//age=60 return0; }

有时候我们可以使用函数的返回值来回传数据,在简单的情况下是可以的,但是如果返回值有其它用途(例如返回函数的执行状态量),或者要回传的数据不止一个,返回值就解决不了了。

传递变量的指针可以轻松解决上述问题。

voidchange(int*pa) { (*pa)++;//因为传递的是age的地址,因此pa指向内存数据age。当在函数中对指针pa解地址时, //会直接去内存中找到age这个数据,然后把它增1。 } intmain(void) { intage=160; change(&age); printf("age=%d",age);//age=61 return0; }

比如指针的一个常见的使用例子:

#include #include #include voidswap(int*,int*); intmain() { inta=5,b=10; printf("a=%d,b=%d ",a,b); swap(&a,&b); printf("a=%d,b=%d ",a,b); return0; } voidswap(int*pa,int*pb) { intt=*pa;*pa=*pb;*pb=t; }

在以上的例子中,swap函数的两个形参pa和pb可以接收两个整型变量的地址,并通过间接访问的方式修改了它指向变量的值。在main函数中调用swap时,提供的实参分别为&a,&b,这样就实现了pa=&a,pb=&b的赋值过程,这样在swap函数中就通过*pa修改了 a 的值,通过*pb修改了 b 的值。因此,如果需要在被调函数中修改主调函数中变量的值,就需要经过以下几个步骤:

定义函数的形参必须为指针类型,以接收主调函数中传来的变量的地址;

调用函数时实参为变量的地址;

在被调函数中使用*间接访问形参指向的内存空间,实现修改主调函数中变量值的功能。

指针作为函数的形参的另一个典型应用是当函数有多个返回值的情形。比如,需要在一个函数中统计一个数组的最大值、最小值和平均值。当然你可以编写三个函数分别完成统计三个值的功能。但比较啰嗦,如:

intGetMax(inta[],intn) { intmax=a[0],i; for(i=1;ia[i])min=a[i]; } returnmin; } doubleGetAvg(inta[],intn) { doubleavg=0; inti; for(i=0;i

其实我们完全可以在一个函数中完成这个功能,由于函数只能有一个返回值,可以返回平均值,最大值和最小值可以通过指针类型的形参来进行实现:

doubleStat(inta[],intn,int*pmax,int*pmin) { doubleavg=a[0]; inti; *pmax=*pmin=a[0]; for(i=1;ia[i])*pmin=a[i]; } returnavg/n; }

函数的指针

一个函数总是占用一段连续的内存区域,函数名在表达式中有时也会被转换为该函数所在内存区域的首地址。我们可以把函数的这个首地址赋予一个指针变量,使指针变量指向函数所在的内存区域,然后通过指针变量就可以找到并调用该函数。这种指针就是函数指针。

函数指针的定义形式为:

returnType (*pointerName)(param list);

returnType 为函数返回值类型,pointerNmae 为指针名称,param list 为函数参数列表。参数列表中可以同时给出参数的类型和名称,也可以只给出参数的类型,省略参数的名称,这一点和函数原型非常类似。

用指针来实现对函数的调用:

#include //返回两个数中较大的一个 intmax(inta,intb) { returna>b?a:b; } intmain() { intx,y,maxval; //定义函数指针 int(*pmax)(int,int)=max;//也可以写作int(*pmax)(inta,intb) printf("Inputtwonumbers:"); scanf("%d%d",&x,&y); maxval=(*pmax)(x,y); printf("Maxvalue:%d ",maxval); return0; }

结构体和指针

结构体指针有特殊的语法:-> 符号

如果p是一个结构体指针,则可以使用 p ->【成员】 的方法访问结构体的成员

typedefstruct { charname[31]; intage; floatscore; }Student; intmain(void) { Studentstu={"Bob",19,98.0}; Student*ps=&stu; ps->age=20; ps->score=99.0; printf("name:%sage:%d ",ps->name,ps->age); return0; }

const 和 指针

指向常量的指针,值不能改变,指向可改变

常指针值能改变,指向不可改变

指向常量的常指针,都不能改变

#include intmain() { //1可改变指针 constinta=10; int*p=&a; *p=1000; printf("*p=%d ",*p); //2可改变指针 constb=10; int*pb=&b; pb=p; printf("*pb=%d ",*pb); //3 constc=10; int*constpc=&c; *pc=1000; //pc=pb;不能改变 //4 constd=10; const*intconstpd=&d; //*pd=1000;不能改变 printf(" "); return0; }

深拷贝和浅拷贝

如果2个程序单元(例如2个函数)是通过拷贝 他们所共享的数据的 指针来工作的,这就是浅拷贝,因为真正要访问的数据并没有被拷贝。如果被访问的数据被拷贝了,在每个单元中都有自己的一份,对目标数据的操作相互 不受影响,则叫做深拷贝。

#include usingnamespacestd; classCopyDemo { public: CopyDemo(intpa,char*cstr)//构造函数,两个参数 { this->a=pa; this->str=newchar[1024];//指针数组,动态的用new在堆上分配存储空间 strcpy(this->str,cstr);//拷贝过来 } //没写,C++会自动帮忙写一个复制构造函数,浅拷贝只复制指针,如下注释部分 //CopyDemo(CopyDemo&obj) //{ //this->a=obj.a; //this->str=obj.str;//这里是浅复制会出问题,要深复制 //} CopyDemo(CopyDemo&obj)//一般数据成员有指针要自己写复制构造函数,如下 { this->a=obj.a; //this->str=obj.str;//这里是浅复制会出问题,要深复制 this->str=newchar[1024];//应该这样写 if(str!=0) strcpy(this->str,obj.str);//如果成功,把内容复制过来 } ~CopyDemo()//析构函数 { deletestr; } public: inta;//定义一个整型的数据成员 char*str;//字符串指针 }; intmain() { CopyDemoA(100,"hello!!!"); CopyDemoB=A;//复制构造函数,把A的10和hello!!!复制给B cout<<"A:"<< A.a << "," <

根据上面实例可以看到,浅复制仅复制对象本身(其中包括是指针的成员),这样不同被复制对象的成员中的对应非空指针会指向同一对象,被成员指针引用的对象成为共享的,无法直接通过指针成员安全地删除(因为若直接删除,另外对象中的指针就会无效,形成所谓的野指针,而访问无效指针是危险的;

除非这些指针有引用计数或者其它手段确保被指对象的所有权);而深复制在浅复制的基础上,连同指针指向的对象也一起复制,代价比较高,但是相对容易管理。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 数据
    +关注

    关注

    8

    文章

    6511

    浏览量

    87590
  • C语言
    +关注

    关注

    180

    文章

    7528

    浏览量

    128438
  • 变量
    +关注

    关注

    0

    文章

    595

    浏览量

    28112

原文标题:C语言指针详解

文章出处:【微信号:mcu168,微信公众号:硬件攻城狮】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    C语言函数指针六大应用场景详解

    函数指针是一种非常强大的编程工具,它可以让我们以更加灵活的方式编写程序。在本文中,我们将介绍 6 个函数指针的高级应用场景,并贴出相应的代码案例和解释。
    的头像 发表于 04-23 18:19 406次阅读

    C语言中数字怎么转成指针

    C语言中数字怎么转成指针,比如我要去内存地址为0x100的内容,用一句话表达,不如uchar a=?我要问号等于0x100地址的内容
    发表于 10-31 06:29

    单片机C语言指针有什么用处呢?

    单片机C语言指针有什么用处呢
    发表于 10-23 07:18

    C语言中的结构体指针在访问的时候怎么读取成员变量的数据?

    C语言中的结构体指针在访问的时候怎么读取成员变量的数据
    发表于 10-10 07:07

    c语言指针用法详解:如何使用指针变量做函数参数

    指针是C语言中一个比较重要的东西,有人说指针是C语言的灵魂这句话说的一点也没错。 正确灵活地运用它,可以有效地表达一些复杂的数据结构,比如系统的动态分配内存、消息机制、任务调度、灵活矩
    发表于 09-21 12:32 828次阅读
    c<b class='flag-5'>语言</b><b class='flag-5'>指针</b>用法<b class='flag-5'>详解</b>:如何使用<b class='flag-5'>指针</b>变量做函数参数

    一文详解C语言指针变量

    指针变量也是一个变量,对应一块内存空间,对应一个内存地址,指针名就是己址。这空内存空间多大?一个机器字长(machine word),32位的CPU和操作系统就是32个位,4个字节,其值域
    发表于 09-08 12:24 300次阅读
    一文<b class='flag-5'>详解</b>C<b class='flag-5'>语言</b><b class='flag-5'>指针</b>变量

    为什么说指针是C语言的灵魂呢?

    指针是C语言的灵魂,这句话并不夸张。
    发表于 09-05 17:03 431次阅读
    为什么说<b class='flag-5'>指针</b>是C<b class='flag-5'>语言</b>的灵魂呢?

    C语言中指针的基本概念和用法

    在C语言中,指针是一项重要的概念,它允许我们直接访问和操作内存地址。
    发表于 08-17 15:30 513次阅读

    C语言中空指针和野指针的概念及产生原因

    在C语言中,指针是一种非常强大和灵活的工具,但同时也容易引发一些问题,其中包括空指针和野指针
    发表于 08-16 16:18 398次阅读

    C语言中多级指针的概念和使用方法

    多级指针在C语言中是一种特殊的指针类型,它可以指向其他指针指针
    发表于 08-16 16:16 306次阅读

    C语言中指针函数和函数指针的概念及应用示例

    在C语言中,指针函数和函数指针是强大且常用的工具。它们允许我们以更灵活的方式处理函数和数据,进而扩展程序的功能。
    发表于 08-16 16:14 342次阅读

    C语言数组和指针的区别

    大家好,我是嵌入式老林,从事嵌入式软件开发多年,今天分享的内容是C语言数组和指针的区别,希望能对你有所帮助。
    发表于 07-11 12:40 275次阅读
    C<b class='flag-5'>语言</b>数组和<b class='flag-5'>指针</b>的区别

    用图文和代码讲透C语言指针

    如果问C语言中最重要、威力最大的概念是什么,答案必将是指针!威力大,意味着使用方便、高效,同时也意味着语法复杂、容易出错。指针用的好,可以极大的提高代码执行效率、节约系统资源;如果用的不好,程序中将会充满陷阱、漏洞。
    发表于 06-08 09:42 156次阅读
    用图文和代码讲透C<b class='flag-5'>语言</b><b class='flag-5'>指针</b>

    C语言指针p、*p、&amp;p、*&amp;p、&amp;*p符号分别代表什么意思?

    在C语言中,指针是非常重要的概念。指针是一个变量,其值为另一个变量的地址。使用指针可以直接访问内存中的数据,这使得C语言非常灵活和强大。
    的头像 发表于 05-29 15:05 2152次阅读

    C语言中一级指针、二级指针和三级指针

    一级指针的用法其实是取数据的地址,以此类推,二级指针就是取一级指针的地址,也可以表示一级指针的指向的内容。
    发表于 05-19 17:30 947次阅读
    C<b class='flag-5'>语言</b>中一级<b class='flag-5'>指针</b>、二级<b class='flag-5'>指针</b>和三级<b class='flag-5'>指针</b>