C语言——善于利用指针

1 指针是什么

由于通过地址能找到所需的单元变量,可以说地址指向该变量单位,因此,将地址形象化地称为“指针”。意思是通过它能找到以它为地址的内存单元,指针的本质是变量。

对变量的访问可以分为直接访问和间接访问

如果有一个变量专门用来存放另一变量的地址(即指针),则它称为“指针变量”。指针变量就是地址变量,用来存放地址,指针变量的值是地址
注:
区分“指针”和“指针变量”这两个概念。例如,可以说变量i的指针是2000,而不能说i的指针变量是2000.指针是一个地址 ,而指针变量是存放地址的变量

2 指针变量

定义指针变量的一般形式为: 类型名 * 指针变量名; 如:int * pointer_1, * pointer_2;

左端的int是在定义指针变量时必须指定的“基类型”。指针变量的基类型用来指定此指针变量可以指向的变量的类型,指针变量是基本数据类型派生出来的类型,它不能离开基本类型而独立存在。
注:
<1>指针变量前面的“ * ”表示该变量为指针型变量

<2>在定义指针变量时必须指定基类型。一个变量的指针的含义包含两个方面:一是一存储单元编号表示的纯地址,一是它指向的存储单元的数据类型;

<3>指向整型数据的指针类型表示为“int *”,读作“指向int的指针或简称“int指针”

<4>指针变量中只能存放地址(指针),不要将一个整数赋给一个指针变量

3 怎样引用指针变量

<1>给指针变量赋值。 如: p=&a; 指针变量p的值时变量a的地址,p指向a。

<2>引用指针变量指向的变量。 如果已执行“p=&a;”,即指针变量p指向了整型变量a,则 prinft(“%d”,* p);其作用是以整数形式输出指针变量p所指向的变量的值,即变量a的值

<3>引用指针变量的值。如:printf(“%o”,p);其作用是以八进制数形式输出指针变量p的值,如果p指向了a,就是输出了a的地址,即&a

4 通过指针引用数组

4.1 数组元素的指针

一个变量有地址,一个数组包含着若干元素,每个数组元素都在内存中占用存储单元,它们都有对应的地址。指针变量既可以指向变量,也可以指向数组元素。所谓数组元素的指针就是数组元素的地址

引用数组元素可以使用下标法如(a[3]),也可以用指针法,即通过指向数组元素的指针找到所需的元素
注:程序中的数组名不代表整个数组,只代表数组首元素的地址。如"p=a"的作用是把a数组的首元素的地址赋给指针变量p,而不是把数组a中的各元素的值给p

4.2 在引用数组元素时指针的运算

在指针已指向一个数组元素时,可以对指针进行以下运算:
加一个整数(用+或+=),如p+1
减一个整数(用-或-=),如p-1
自加运算,如p++,++p
自减运算,如p–,–p
两指针相减,如p1-p2(只有p1和p2都指向同意数组中的元素时才有意义)
注:
<1>如果指针p已经指向数组中的一个元素,则p+1指向同一个数组中的下一个元素,p-1指向同一数组中的上一个元素
<2>如果p的初值为&a[0],则p+i和a+i就是数组元素a[i]的地址
<3>(p+i)或(a+i)是p+i或a+i所指向的数组元素,即a[i]。在编译时对数组元素a[i]就是按*(a+i)处理的。按数组首元素的地址加上相对位移量得到要找的元素的地址,然后找出该单元的内容
<4>如果指针变量P1和P2都指向同一数组中的元素,如执行p2-p1,结果是p2-p1的值除以数组元素的长度,即:两个指针所指元素之间的距离,两指针不能相加,相加毫无意义

4.3 通过指针引用数组元素

引用一个数组元素,可以用下面两种方法
<1>下指法,如a[i]形式

<2>指针法,如a(a+i)或(p+i)。其中a是数组名,p是指向数组元素的指针变量,其中初值p=a

分析:
1 p++使p指向下一元素。然后若再执行*p,则得到下一元素的值

2 p++,由于++和同优先级,结合方向为自右而左,因此它等价于*(p++)。先引用p的值,实现*p的运算,然后再使p自增1。如:
for(i=0;i<10;i++,p++)
printf(“%d”,*p);
可以改写为
for(i=0;i<10;i++)
printf(“%d”,*p++);

3 (p++)与(++p)作用不相同。前者是先取p的值然后使p+1。后者是先使p+1,再取p。如:p的初值为a(即&a[0]),若输出*(p++),得到的使a[0]的值,而输出*(++p),得到的是a[1]的值

4 ++(*p)表示p所指向的元素值加1,如果p=a,则++(*p)相当于++a[0],若a[0]的值为3,则在执行++(*p)后a[0]的值为4。注:是元素a[0]的值加一,而不是指针p的值加1

5 如果p的值指向a数组中第i的元素a[i],则:
(p–)相当于a[i–],先对p进行""运算(求p所指向的元素的值),再使p自减
(++p)相当于a[++i],先使p自加,再进行""运算
(–p)相当于a[–i],先使p自减,再进行""运算

4.4 用数组名做函数参数
用数组元素作函数实参可以向形参传递一个数组元素的值,实参数组名代表该数组首元素的地址,而形参是用来接受从实参传递过来的数组首元素地址的。因此,形参是一个指针变量
注:
1 实参数组名代表的是一个固定的地址,或者说的使指针常量,但形参数组名并不是一个固定的地址,而是按指针变量处理

2 在函数调用进行虚实结合后,形参的值就是实参数组首元素的地址。在函数执行的期间,它可以再被赋值

3 如果一个实参数组,要想在函数 中改变此数组中的元素的值,实参与形参的对应关系有以下4种情况
<1>形参和实参都用数组名

<2>实参用数组名,形参用指针变量

<3>实参形参都用指针变量

<4>实参为指针变量,形参为数组名

4 如果用指针变量做实参,必须先使指针变量有确定值,指向一个已定义的对象

5实参与形参如果使指针类型,应当注意它们的基类型必须一致

在这里插入图片描述
在这里插入图片描述

4.5 通过指针引用多维数组
多维指针根据声明的维数需要进行多次地址转换才能够取到目标数据。但指针作为数据变量,可以多次赋值

由于分配内存情况不同,所显示的地址可能是不同的,但是显示的地址是有共同规律的

二维数组名是指向行的,“&二维数组名”就指向了整个二维数组

“二维数组名[i]” 是指向i行的首元素的。“&二维数组名[i]”就指向了一行

5 通过指针引用字符串

5.1 字符串的引用方式

在c程序中引用一个字符串,可以用以下两种方法
1 在字符数组存放一个字符串,可以通过数组名和下标引用字符串中的一个字符,也可以通过数组名和格式声明"%s"输出该字符串

2 用字符指针变量指向一个字符串常量,通过字符指针变量引用字符串常量
如:
#include<stdio.h>
int main()
{
char * string=“I love China “;
printf(”%s\n”,string);
return 0;
}
通过字符数组或字符指针变量可以输出一个字符串,而对一个数值型数组,是不能用数组名输出它的全部元素的,它输出是数组首元素的地址。对于数值型数组元素只能逐个输出

5.2 字符指针作函数参数

如果想把一个字符串从一个函数"传递"到另一个函数,可以用地址传递的办法,即用字符数组名作参数,也可以用字符指针变量作参数。在被调用的函数中可以改变字符串的内容,在主调函数中可以引用改变后的字符串

5.3 使用字符指针变量和字符数组的比较

1、字符数组由若干个元素组成,每个元素中放一个字符,而字符指针变量中存放的是地址(字符串第1个字符的地址),绝不是将字符串放到字符指针变量中;

2、可以对字符指针变量赋值,但不能对数组名赋值;

3、可以在定义时对各元素赋初值,但不能用赋值语句对字符数组中全部元素整体赋值;

4、编译时为字符数组分配若干存储单元,以存放各元素的值,而对字符指针变量,只分配一个存储单元;

5、指针变量的值是可以改变的,而字符数组名代表一个固定的值,不能改变;

6、字符数组中各元素的值是可以改变的(可以对它们再赋值),但字符指针变量指向的字符串常量中的内容是不可以被取代的;

7、如果定义了字符指针变量,并使它指向了数组a的首元素,则可以用指针变量带下标的形式引用数组元素,同样,可以用地址发引用数组元素;但若未指向数组,则无法用此类形式引用数组中的元素;

8、用指针指向格式字符串,可用它代替printf函数中的格式字符串。

6 指向函数的指针

6.1 什么是函数的指针?
函数名代表函数的起始地址,函数名就是函数的指针

6.2 用函数指针变量调用函数
如果想调用一个函数,除了可以通过函数名调用以外,还可以通过指向函数的指针变量来调用该函数
1 通过函数名调用函数
#include<stdio.h>
int main()
{
int max(int,int); //函数声明
int a,b,c;
printf(“please enter a and b:”);
scanf(“%d,%d”,&a,&b);
c=max(a,b); //通过函数名调用max函数
printf(“a=%d\nb=%d\nmax=%d\n”,a,b,c);
return 0;
}

int max(int x,int y)
{
int z;
if(x>y) z=x;
else z=y;
return(z);
}

2 通过指针变量调用它所指向的函数
#include<stdio.h>
int main()
{
int max(int,int); //函数声明
int (* p)(int,int); //定义指向函数的指针变量p
int a,b,c;
p=max; //是p指向max函数
printf(“please enter a and b:”) ;
scanf(“%d,%d”,&a,&b);
c=(* p)(a,b); //通过指针变量调用max函数
printf(“a=%d\nb=%d\nmax=%d\n”,a,b,c);
return 0;
}

int max(int x,int y)
{
int z;
if(x>y) z=x;
else z=y;
return(z);
}

6.3 怎样定义和使用指向函数的指针变量
定义指向函数的指针变量的一般形式为: 类型名 (* 指针变量名) (函数参数表列);
注:
1 定义指向函数的指针变量,只能指向在定义时指定的类型的函数,且一个指针变量可以先后指向同类型的不同函数

2 如果要用指针调用函数,必须先使指针变量指向该函数

3 再给函数指针变量赋值是,只给出函数名而不给参数

4 用函数指针变量调用函数时,将(*p)代替函数名即可

5 函数指针是不能够进行算术运算的

6 函数名只能调用所定义的一个函数,而通过指针变量则比较灵活

6.4 用指向函数的指针作函数参数
指针函数的指针变量的一个重要用途是把函数的入口地址作为函数传递到其他函数

指向函数的指针可以作为函数参数,把函数的入口地址传递给形参,这样就能够在被调用的函数中使用实参函数

7 返回指针值的函数

一个函数可以返回一个整型值,字符值,实型值,也可以返回指针型的数据,即地址。

定义返回指针值的原型的一般形式为:
类型名 * 函数名(参数表列);

8 指针数组和多重指针

8.1 什么是指针数组?
一个数组,若其元素均为指针类型数据,称为指针数组。也就是说,指针数组中的每一个元素都存放一个地址,相当一个指针变量

定义一维指针数组的一般形式为:
类型名 * 数组名[数组长度];

什么情况下要用到指针数组?
指针数组比较适合用来指向若干个字符串,使字符串处理更加方便灵活

8.2 指向指针数据的指针变量
指向指针数据的指针变量,简称指向指针的指针,它的每一个元素是一个指针型的变量,其值为地址

定义一个指向指针数据的指针变量:char p;
p的前面有两个
,从附录c可以知道,*运算符的结合性是从右到左,因此
*p相当于*(*p)。char 和( p),后面的(*p)表示p是指针变量,前面的char * 表示p指向的是char 型的类型,也就是说,p指向一个字符指针变量(这个字符指针变量指向一个字符型数据)。如果引用p就得到p所指向的字符指针变量的值

数组元素只能存放地址,不能存放整数

8.3 指针数组作main函数的形参

指针数组的一个重要应用是作为main函数的形参。在以往的程序中,main函数的第1行一般写成以下形式:
int main() 或 int main(void)
括号中是空的或有void,表示main函数没有参数,调用main函数不必给出实参。这是一般程序常采用的形式。实际上,在某些情况下,main函数可以有参数。如:
int main(int argc,char * argv[]),其中,argc和argv就是main函数的形参。

注:如果用带参数的main函数,其中第一个形参必须是int型,用来接收形参个数,第二个形参必须是字符指针数组,用来接收从操作系统命令行传来的字符串中的收字符的地址

命令行的 一般形式为:
命令名 参数1 参数2…参数n

9 动态内存分配与指向它的指针变量

9.1 什么是内存的动态分配?
全局变量是分配在内存中的静态存储区的,非静态的局部变量是分配在内存中的动态存储区的,这个存储区是一个称为栈的区域。存储动态分配区域临时存储的数据存放在一个特别的自由存储空间称为堆区

9.2 怎样建立内存的动态分配
1 用malloc函数开辟动态存储区
其函数的原型为:void * malloc(unsigned int size);
其作用是在内存的动态存储区中分配一个长度为size的连续空间。形参size的类型定为无符号整型(不允许为负数)。此函数的值是所分配区域的第一个字节的地址,或者说,此函数是一个指针型函数,返回的指针指向该分配域的第一个字节。如:
malloc(100); //开辟100字节的临时分配域,函数值为其第一个字节的地址
注意指针的基类型为void,即不指向任何类型的数据,只提供一个纯地址。如果此函数未能成功地执行(例如空间不足),则返回空指针(NULL)

2 用calloc函数开辟动态存储区
其函数地原型为:void * calloc(unsigned n,unsigned size);
其作用是在内存的动态存储区中分配n个长度为size的连续空间,这个空间一般比较大,足以保存一个数组
用calloc函数可以为一维数组开辟动态存储空间,n为数组元素,每个元素长度为size。这就是动态数组。函数返回指向所分配域的第一个字节的指针;如果分配不成功,返回空指针。如:
p=calloc(50,4); //开辟50x4个字节的临时分配域,把首地址赋给指针变量p

3 用realloc函数重新分配动态存储区
其函数的原型为:void * realloc(void * p,unsigned int size);
如果已经通过malloc函数或calloc函数获得了动态空间 ,想改变其大小,可以用recalloc函数重新分配
用realloc函数将p所指向的动态空间的大小改编为size。p的值不变。如果重分配不成功,返回空指针。如:
realloc(50,4); //将p所指向的已分配的动态空间改为50字节

4 用free函数释放动态存储区
其函数原型为:void free(void * p);
其作用是释放指针变量p所指向的动态空间,使这部分空间能重新被其他变量使用。p应是最近一次调用calloc或malloc函数时得到的函数返回值,free函数无返回值。
free§; //释放指针变量p所指向的已分配的动态空间

注意:以上4个函数的声明在stdlib.h的头文件中,在用到这些函数时应当用"#include<stdlib.h>"指令把stdlib.h头文件包含到程序文件中

9.3 void 指针类型
C99使用基类型为void的指针类型,可以定义一个基类型为void的指针变量(void*型变量)它不指向任何类型的数据
void * p3; //p3为无类型指针变量(基类型为void型)

void*型纸质恩代表“无指向的地址”,这种指针不指向任何类型的数据。不能企图通过它存取数据,在程序中它只是过渡性的,只有转换为有指向的地址,才能存取数据。

10 指针小结

10.1 "指针"是C语言中一个形象化的名词,形象地表示"指向"的关系,其在物理上的实现是通过地址来完成的
1 &a是变量a的地址,也可称为变量a的指针;
2 指针变量是存放地址的变量,也可说,指针变量是存放指针的变量;
3 指针变量也可称为地址变量,它的值是地址;
4 &是取地址运算符,也可说是去指针运算符,&a是a的地址,也是变量a的指针;
5 数组名是一个地址,是数组首元素的地址,也可说是一个指针,是首元素的指针;
6 函数名是一个指针(即函数代码区的首字节),也可以说函数名是一个地址;
7 函数的实参如果是数组名,传递给形参的是一个地址,也可说传递给形参的是一个指针;

10.2 在C语言中,所有的数据都是有类型的。可以说C语言中的数据都是"有类型的数据",或称"带类型的数据"。对地址而言,也同样有类型,地址不是一个数值型数据,它是按指针型数据存储的方式存储的,指针型存储单元是专门用来存放地址的,指针型数据的存储形式就是地址的存储形式;
一个地址型的数据实际上包含3个信息:
1 表示内存编号的纯地址;
2 它本身的类型,即指针类型;
3 以它为标识的存储单元中存放的是什么类型的数据,即基类型。

10.3 要区别指针和指针变量。指针就是地址,而指针变量是用来存放地址的变量

10.4 什么叫“指向”地址就意味着指向,因为通过地址能找到具有该地址的对象。但并不是任何类型的数据的地址都可以存放在同一个指针变量中的,只有与指针变量的基类型相同的数据的地址才能存放在相应的指针变量中;

10.5 要深入掌握在对数组的操作中正确地使用指针,搞清指针地指向。一维数组名代表数组首元素的地址

10.6 指针运算
1 指针变量加或者减一个整数;如:p++,p–
2 指针变量赋值;如:p=&a;p=array; //将数组array首元素地址赋给p
3 两个指针变量可以相减;(相加则无意义)
4 两个指针变量比较;(两个指针指向同一个数组的元素)
5 指针变量可以有空值;(该指针变量不指向任何变量,在引用指针变量之前应对它赋值)

10.7 指针的优点
1 提高程序效率;
2 在调用函数时当指针指向的变量的值改变时,这些值能够为主调函数使用,即可以从函数调用得到多个可以改变的值;
3 可以实现动态存储分配。

在这里插入图片描述

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值