认识指针的作用和意义

文章正文
发布时间:2024-10-30 04:04

       本篇博客主要是学习C语言指针的作用和一些使用场景,内容基本是自己对于C语言的理解和笔记,如有错误请及时指出,不保证是百分百正确的理解,希望大家互勉。本篇博客大部分是自己收集和整理,如有侵权请联系我删除。

如果觉得有用点赞关注收藏三连,多谢支持

本博客内容原创,创作不易,转载请注明
————————————————
————————————————

一.认识指针 1.指针的基本作用

表示一些复杂的数据结构

快速的传递数据

(1)使用指针传递大容量的参数,主函数和子函数使用的是同一套数据,避免了参数传递过程中的数据复制,提高了运行效率,减少了内存占用

(2)使用指针传递输出参数,利用主函数和子函数使用同一套数据的特性,实现数据的返回,可实现多返回值函数的设计
 

使函数返回一个以上的值

(1)将模块内的公有部分返回,让主函数持有模块的“句柄”,便于程序对指定对象的操作
 

直接访问物理地址下的数据

(1)访问硬件指定内存下的数据,如设备ID号等

(2)将复杂格式的数据转换为字节,方便通信与存储
 

能够方便的处理字符串

是理解面向对象语言中引用的基础

可以有效地表达一些复杂的数据结构,比如系统地动态分配内存、消息机制、任务调度、灵活矩阵定时等等。

传递参数

2.指针的基本概念(理解)

(1)指针就是地址,地址就是指针。地址就是内存单元的编号,是从0开始的非负整数。

        没有内存就没有指针,指针必须指向一块在程序中申请过的内存空间,这个指针才能使用,而不是野指针;

        其实内存空间就相当于一个房间,每个房间都有门牌号,而指针就是房卡,房卡上写着门牌号, 你拿着房卡啥都干不了 必须打开门进去,才能对房间里的东西进行操作

(2)在C语言中,每个变量的最终存放的地方就是芯片的地址,每个地址就相当于一个编号,而指针的作用就是直接指向该地址,获取地址上的值。对于一个内存单元来说,单元的地址即为指针,其中存放的数据才是该单元的内容。

(3)在C语言中,允许用一个变量来存放指针,这种变量称为指针变量。因此,一个指针变量的值就是某个内存单元的地址或称为某内存单元的指针。

(4)一个指针是一个地址,是一个常量。而一个指针变量却可以被赋予不同的指针值,是变量。但常把指针变量简称为指针。

(5) 既然指针变量的值是一个地址, 那么这个地址不仅可以是变量的地址, 也可以是其它数据结构的地址。指针变量存放一个数组或者函数的首地址,就可以找到改数组或者函数,因为内存存储都是连续存放的。

(6)一种数据类型或数据结构往往都占有一组连续的内存单元。 用“地址”这个概念并不能很好地描述一种数据类型或数据结构,而“指针”虽然实际上也是一个地址,但它却是一个数据结构的首地址,它是“指向”一个数据结构的。

3.指针的定义

        指针即指针变量,用于存放其他数据单元(变量/数组/结构体/函数等)的首地址。若指针存放了某个数据单元的首地址,则这个指针指向了这个数据单元,若指针存放的值是0,则这个指针为空指针 定义一个指针变量:

4.指针类型说明  int p; //这是一个普通的整型变量 int *p; //首先从P 处开始,先与*结合,所以说明P 是一个指针,然后再与int 结合,说明指针所指向的内容的类型为int 型.所以P是一个返回整型数据的指针 int p[3]; //首先从P 处开始,先与[]结合,说明P 是一个数组,然后与int 结合,说明数组里的元素是整型的,所以P 是一个由整型数据组成的数组 int *p[3]; //首先从P 处开始,先与[]结合,因为其优先级比*高,所以P 是一个数组,然后再与*结合,说明数组里的元素是指针类型,然后再与int 结合,说明指针所指向的内容的类型是整型的,所以P 是一个由返回整型数据的指针所组成的数组 int (*p)[3]; //首先从P 处开始,先与*结合,说明P 是一个指针然后再与[]结合(与"()"这步可以忽略,只是为了改变优先级),说明指针所指向的内容是一个数组,然后再与int 结合,说明数组里的元素是整型的.所以P 是一个指向由整型数据组成的数组的指针 int **p; //首先从P 开始,先与*结合,说是P 是一个指针,然后再与*结合,说明指针所指向的元素是指针,然后再与int 结合,说明该指针所指向的元素是整型数据.由于二级指针以及更高级的指针极少用在复杂的类型中,所以后面更复杂的类型我们就不考虑多级指针了,最多只考虑一级指针. int p(int); //从P 处起,先与()结合,说明P 是一个函数,然后进入()里分析,说明该函数有一个整型变量的参数,然后再与外面的int 结合,说明函数的返回值是一个整型数据 Int (*p)(int); //从P 处开始,先与指针结合,说明P 是一个指针,然后与()结合,说明指针指向的是一个函数,然后再与()里的int 结合,说明函数有一个int 型的参数,再与最外层的int 结合,说明函数的返回类型是整型,所以P 是一个指向有一个整型参数且返回类型为整型的函数的指针 int *(*p(int))[3]; //可以先跳过,不看这个类型,过于复杂从P 开始,先与()结合,说明P 是一个函数,然后进入()里面,与int 结合,说明函数有一个整型变量参数,然后再与外面的*结合,说明函数返回的是一个指针,,然后到最外面一层,先与[]结合,说明返回的指针指向的是一个数组,然后再与*结合,说明数组里的元素是指针,然后再与int 结合,说明指针指向的内容是整型数据.所以P 是一个参数为一个整数据且返回一个指向由整型指针变量组成的数组的指针变量的函数. 二.变量的指针和指向变量的指针变量 1.指针变量的说明

一个指针变量 无论它指向的数据类型占几个字节,它自己本身只占四个字节(32位机)

指针变量是存放地址的变量

指针和指针变量是两个不同的概念

(1)变量的指针就是变量的地址。

(2)存放变量地址的变量是指针变量。

        在C语言中,允许用一个变量来存放指针,这种变量称为指针变量。因此,一个指针变量的值就是某个变量的地址或称为某变量的指针。

但是要注意:通常我们叙述时会把指针变量简称为指针,实际它们含义并不一样。

        指针的本质就是一个操作受限的非负整数,它不能进行乘和除,只能进行加减。(相加在两个独立的指针没有意义,一般只用于数组间)。

2.定义一个指针变量

对指针变量的定义包括三个内容:

(1)指针类型说明,即定义变量为一个指针变量;

(2)指针变量名;

(3)变量值(指针)所指向的变量的数据类型。

指针变量的一般形式为:

类型说明符 *变量名;

其中, *表示这是一个指针变量,变量名即为定义的指针变量名,类型说明符表示本指

针变量所指向的变量的数据类型。

例如: int *p1;

表示p1是一个指针变量, 它的值是某个整型变量的地址。 或者说p1指向一个整型变量。

至于p1究竟指向哪一个整型变量,应由向p1赋予的地址来决定。

再如: int *p2; /*p2是指向整型变量的指针变量*/ float *p3; /*p3是指向浮点变量的指针变量*/ char *p4; /*p4是指向字符变量的指针变量*/ 应该注意的是,一个指针变量只能指向同类型的变量,如P3 只能指向浮点变量,不能 时而指向一个浮点变量,时而又指向一个字符变量。

示例:

int i; //这就相当于申请了一个int类型房间, int *p; //这就相当与得到了一张能打开int类型房间的房卡 p = &i; //这相当于把房间的门牌号写到房卡上 *p = 1; //相当于先打开门进去了 再把1放入房间 p--;// 这相当于你去了这个房间的下一个房间

int* p;//p是变量的名字,int*表示p变量存放的是int类型变量的地址

//        int* p; 不表示定义了一个名字叫做*p的变量

//        int* p; 应该这样理解:p是变量名,p变量的数据类型是int * 类型

//         所谓int * 类型 就是存放int变量地址的类型

        int i=1;        p=&i;        前一个成立,后一个才生效,才能进行指针之间的赋值。

/****************************************************************************

1.p保存了i的地址,因此p指向i;

2.p不是i,i也不是p,更准确的说:修改p的值不影响i的值,修改i的值也不会影响p的值,

3.如果一个指针变量指向了某个普通变量,则

*指针变量 就完全等同于 普通变量

例子: 如果p是个指针变量,并且p存放了普通变量i的地址 则p指向了普通变量i *p就完全等同于i 或者说: 在所有出现*p的地方都可以替换成i 在所有出现i的地方都可以替换成*p *p 就是以p的内容为地址的变量

 

 

3.指针变量的引用

(1)指针变量同普通变量一样,使用之前不仅要定义说明,而且必须赋予具体的值。

(2)未经赋值的指针变量不能使用,否则将造成系统混乱,甚至死机。(俗称野指针)

(3)指针变量的赋值只能赋予地址, 决不能赋予任何其它数据,否则将引起错误。在C语言中,变量的地址是由编译系统分配的,对用户完全透明,用户不知道变量的具体地址。

两个有关的运算符:

1) &:取地址运算符。

2) *:指针运算符(或称“间接访问” 运算符) 。

C语言中提供了地址运算符&来表示变量的地址。

运算符的一般形式为:

&变量名;*变量名;

如&a表示变量a的地址, &b表示变量b的地址。变量本身必须预先说明。

指针的赋值方式:

指针只有赋值地址之后,才可以进行对于的运算操作,否则无效

(1)指针变量初始化的方法 int a; int *p=&a; (2)赋值语句的方法 int a; int *p; p=&a; 不允许把一个数赋予指针变量,故下面的赋值是错误的: int *p; p=1000; 被赋值的指针变量前不能再加“ *”说明符,如写为*p=&a 也是错误的。

        在对指针取内容之前,一定要确保指针指在了合法的位置,否则将会导致程序出现不可预知的错误 同级指针之间才能相互赋值,跨级赋值将会导致编译器报错或警告

 4.指针的间接访问

        通过指针访问它所指向的一个变量是以间接访问的形式进行的,所以比直接访问一个变量要费时间,而且不直观,因为通过指针要访问哪一个变量,取决于指针的值(即指向)

例如:"*p2=*p1;"实际上就是"j=i;",前者不仅速度慢而且目的不明。

 但由于指针是变量,我们可以通过改变它们的指向,以间接访问不同的变量

指针变量可出现在表达式中, 设 int x,y, *px=&x; 指针变量px指向整数x,则*px可出现在x能出现的任何地方。例如: y=*px+5; /*表示把x的内容加5并赋给y*/ y=++*px; /*px的内容加上1之后赋给y, ++*px相当于++(*px)*/ y=*px++; /*相当于y=*px; px++ 或 y=*(px++) */ 直接上例子说明: main() { int a,b; int *pointer_1, *pointer_2; a=100;b=10; pointer_1=&a; pointer_2=&b; printf("%d,%d\n",a,b); printf("%d,%d\n",*pointer_1, *pointer_2); }

对程序的说明:

1) 在开头处虽然定义了两个指针变量pointer_1和pointer_2, 担它们并未指向任何一个

整型变量。只是提供两个指针变量,规定它们可以指向整型变量。程序第6、 7行的作

用就是使pointer_1指向a, pointer_2指向b。

2) 最后一行的*pointer_1和*pointer_2就是变量a和b。 最后两个printf函数作用是相

同的。

3) 程序中有两处出现*pointer_1和*pointer_2,请区分它们的不同含义。

4) 程序第6、 7行的“pointer_1=&a”和 “pointer_2=&b”不能写成“*pointer_1=&a”和

“*pointer_2=&b”。

问题考虑:

请对下面再的关于“ &”和“*”的问题进行考虑:

1) 如果已经执行了“ pointer_1=&a; ”语句,则&*pointer_1是什么含义?
        *pointer_1 已经取过地址,则就相当于变量a
        &*pointer_1 = &a  地址,地址下有赋值,所以等于100

2) *&a含义是什么?
        &a = pointer_1
; 反过来就是 *pointer_1 的值即为变量a的值

3) (pointer_1)++和pointer_1++的区别?

1) &*pointer_1 其实就是取pointer_1 指向的地址,即a的地址,等价于 &a. 2) *&a 取a地址上的值,即a的值,为100; 3) 这两个是一样的,都是取pointer_1指向的地址,然后地址加1 int main() { int a=100; int *pointer_1; pointer_1=&a; printf("%d\n",&a); printf("%d\n",&*pointer_1); printf("%d\n",*&a); printf("%d %d\n",(pointer_1)++,pointer_1++); printf("%d\n",pointer_1); //地址执行了加2,int占4字节,所以地址加8 } 5.指针变量作为函数形参

        函数的参数不仅可以是整型、实型、字符型等数据,还可以是指针类型。它的作用是将

一个变量的地址传送到另一个函数中。        

void swap(int *p1,int *p2) { int temp; temp=*p1; *p1=*p2; *p2=temp; } main() { int a,b; int *pointer_1,*pointer_2; scanf("%d,%d",&a,&b);//输入两个数的时候注意要输入,隔开 pointer_1=&a;pointer_2=&b; if(a<b) swap(pointer_1,pointer_2); printf("\n%d,%d\n",a,b); } 对程序的说明:

        swap是用户定义的函数, 它的作用是交换两个变量 ( a和b) 的值。swap函数的形参p1、p2是指针变量。程序运行时,先执行main函数,输入a和b的值。然后将a和b的地址分别赋给指针变量pointer_1和pointer_2,使pointer_1指向a, pointer_2指向b。

接着执行if语句,由于a<b,因此执行swap函数。注意实参pointer_1和pointer_2是

指针变量,在函数调用时,将实参变量的值传递给形参变量。采取的依然是“值传递”方式

因此虚实结合后形参p1的值为&a, p2的值为&b。这时p1和pointer_1指向变量a, p2和pointer_2指向变量b。

接着执行执行swap函数的函数体使*p1和*p2的值互换,也就是使a和b的值互换。

函数调用结束后, p1和p2不复存在(已释放)如图。

6.指针变量几个问题的进一步说明

指针变量可以进行某些运算, 但其运算的种类是有限的。 它只能进行赋值运算和部分算

术运算及关系运算。

1. 指针运算符

1) 取地址运算符&:取地址运算符&是单目运算符,其结合性为自右至左,其功能是取变量

的地址。在scanf函数及前面介绍指针变量赋值中,我们已经了解并使用了&运算符。

2) 取内容运算符*:取内容运算符*是单目运算符,其结合性为自右至左,用来表示指针变

量所指的变量。在*运算符之后跟的变量必须是指针变量。

需要注意的是指针运算符*和指针变量说明中的指针说明符*不是一回事。 在指针变量说明中, “*”是类型说明符,表示其后的变量是指针类型。而表达式中出现的“*”则是一个运算符用以表示指针变量所指的变量。

2. 指针变量的运算

1) 赋值运算:指针变量的赋值运算有以下几种形式。

① 指针变量初始化赋值,前面已作介绍。

② 把一个变量的地址赋予指向相同数据类型的指针变量。

例如: int a,*pa; pa=&a; /*把整型变量a的地址赋予整型指针变量pa*/

③ 把一个指针变量的值赋予指向相同类型变量的另一个指针变量。

例如: int a,*pa=&a,*pb; pb=pa; /*把a的地址赋予指针变量pb*/ 由于pa,pb均为指向整型变量的指针变量,因此可以相互赋值。

④ 把数组的首地址赋予指向数组的指针变量。

例如: int a[5],*pa; pa=a; (数组名表示数组的首地址,故可赋予指向数组的指针变量pa) 也可写为: pa=&a[0]; /*数组第一个元素的地址也是整个数组的首地址,也可赋予pa*/ 当然也可采取初始化赋值的方法: int a[5],*pa=a;

⑤ 把字符串的首地址赋予指向字符类型的指针变量。

例如: char *pc; pc="C Language"; 或用初始化赋值的方法写为: char *pc="C Language"; 这里应说明的是并不是把整个字符串装入指针变量, 而是把存放该字符串的字符数 组的首地址装入指针变量。

⑥ 把函数的入口地址赋予指向函数的指针变量。

例如: int (*pf)(); pf=f; /*f为函数名*/ 3.加减算术运算

        对于指向数组的指针变量,可以加上或减去一个整数n。设pa是指向数组a的指针变

量, 则pa+n,pa-n,pa++,++pa,pa--,--pa运算都是合法的。 指针变量加或减一个整数n的意

义是把指针指向的当前位置(指向某数组元素)向前或向后移动n个位置。

(1)指针变量加1,即向后移动1 个位置表示指针变量指向下一个数据元素的首地址。而不是在原地址基础上加1 例如: int a[5],*pa; pa=a; /*pa指向数组a,也是指向a[0]*/ pa=pa+2; /*pa指向a[2],即pa的值为&pa[2]*/

        指针变量的加减运算只能对数组指针变量进行, 对指向其它类型变量的指针变量作加减

运算是毫无意义的。

(2) 两个指针变量之间的运算

        只有指向同一数组的两个指针变量之间才能进行运算, 否则运算毫无意义。

(3)两指针变量进行关系运算

        指向同一数组的两指针变量进行关系运算可表示它们所指数组元素之间的关系。

例如: pf1==pf2表示pf1和pf2指向同一数组元素; pf1>pf2表示pf1处于高地址位置; pf1<pf2表示pf2处于低地址位置。 指针变量还可以与0比较。 设p为指针变量,则p==0表明p是空指针,它不指向任何变量; p!=0表示p不是空指针。

空指针是由对指针变量赋予0值而得到的。

例如: #define NULL 0 int *p=NULL;

        对指针变量赋0值和不赋值是不同的。指针变量未赋值时,可以是任意值, 是

不能使用的。否则将造成意外错误。而指针变量赋0值后,则可以使用,只是它不

指向具体的变量而已。


7.指针的一些说明

(1)实参必须为普通变量的地址

(2)形参必须为指针变量

(3)在被调函数中通过

*形参名=... 的形式就可以修改实参中的地址 从而达到交换变量的值 数组名就是表示数组中第一个元素的地址, a==&a[0] a[i]等价于*(a+i) 同样&a[i]等价于(a+i) (a+i)就是a数组中序号为i的元素的地址 *(a+i)就是该元素的值 语句p=a;等价于p=&a[0]; int a[5];// a是int*类型 它是地址 a=&a[3];//error 因为a是常量 不能位于等号左边 确定一个数组需要两个参数 一个是数组的开始的地方一个是数组的长度 就是需要两个形参 一个是地址(int*形式) 一个是数字(int形式) 总结:

        关于C语言的指针方面我也属于是探索阶段,本篇博客主要是自己对于一些指针的理解,相当于自己对指针学习的知识总结,可能内容比较冗杂,对于一些细节也可能说不清楚,对于新手来说还是有点吃力,如果内容方面有问题,希望大家可以支出,互勉。

如果觉得有用点赞关注收藏三连,多谢支持

本博客内容原创,创作不易,转载请注明

  点赞收藏关注博主,不定期分享单片机知识,互相学习交流。
————————————————