2第二讲 指针文档格式.docx
《2第二讲 指针文档格式.docx》由会员分享,可在线阅读,更多相关《2第二讲 指针文档格式.docx(43页珍藏版)》请在冰豆网上搜索。
![2第二讲 指针文档格式.docx](https://file1.bdocx.com/fileroot1/2023-2/4/002aca08-e969-4cd1-9937-43a7fb4f7ca3/002aca08-e969-4cd1-9937-43a7fb4f7ca31.gif)
func(inta[],intn);
等同于func(int*a,intn);
◆函数声明的几种变通形式。
◆所以指针a在函数中值可以改变,如*a++,不同于函数内定义的数组如:
intb[5];
这里b是指针常量。
8.指针与字符串:
char*p,s[50]=“fdjfjdfj”;
p=s;
◆可用p间接访问字符串,如指针法:
for(;
*p!
=‘\0’;
p++)
或下标法:
for(i=0;
p[i]!
i++)
9.指针形参在函数间传递字符串:
voidcopy(char*s1,char*s2){while(*s1++=*s2++);
}
◆调用时用字符数组名或指向某字符串的指针做实参,
char*p=“sdgygkh”;
charss[50];
func(ss,p);
◆函数调用的灵活形式:
func(ss+2,p)或func(ss,p+2)
◆注意:
输入字符串常用gets函数,但参数应是字符数组名,或已指向某字符数组的指针,如:
chars[50],*p;
gets(s);
是正确的,而gets(p)是错误的,因为p没被赋地址值,无任何指向。
改正:
p=s;
gets(p);
10.数组指针与多维数组:
inta[3][4];
int(*p)[4];
p=a;
◆p指向a的一整行,此时*p就代表a[0];
如果执行p++,则p指向下一行,即*p代表a[1],p跳过单元数为2*4=8个字节。
◆引用元素的两种形式:
p[i][j]或(*p)[j]
◆几个等价关系式:
由x[i]等价于*(x+i)和&
x[i]等价于x+i得出:
a[i][j]等价(*(a+i))[j]等价*(a[i]+j)等价*(*(a+i)+j)
a[i][j]等价*(a+i)+j等价a[i]+j
◆指针形参在函数间传递多维数组:
intfunc(int(*b)[4],intn);
等价于intfunc(intb[][4],intn);
◆调用时用二维数组名做实参,如:
func(a,3);
11.指针数组:
char*s[3];
表示数组s有3个元素,每一个元素都是一个指针变量,都可以指向一个字符串。
◆赋初值:
char*s[3]={“sdgg”,”ww”,”q”};
或单独赋值:
s[0]=“sdgg”;
s[1]=“ww”;
s[2]=“q”;
◆用于指向多个字符串。
◆也可用字符数组存储多个字符串,只是占用内存空间大。
11.指向指针的指针:
二级指针,char**p;
◆用于访问指针数组,或在函数间传递指针数组,通常就是传递多个字符串。
12.char*s[3]={“sdgg”,”ww”,”q”};
char**p;
◆p指向数组s的首地址,此时*p代表s[0],即指向第0个字符串,*p和s[0]都是第0个字符串的首地址,
◆可用gets(*p)或gets(p[i])来输入字符串,用puts(*p)或puts(p[i])来输出字符串。
◆在函数间传递多个字符串的参数形式:
func(char**s,intn)或func(char*s[],intn)或func(chars[][],intn)
◆总结:
1、通常在主调函数中定义指针数组指向多个字符串。
2、函数:
func(char**s,intn)或func(char*s[],intn)传递多个字符串首地址;
3、在函数内的使用形式:
s[i]或*(s+i),都代表第i个串首地址,或*s++;
4、一般都是整串操作,循环一般使用字符串个数进行控制,如:
i<
n;
i++){s[i]……}
◆字符串操作函数:
strlen()、strcpy()、strcmp()
13.返回指针值的函数:
char*func(char*s,charch)
◆切记:
返回的是地址,在主调函数中应使用指针类型变量接收函数返回值,
char*p,a[]=“asdgg”;
p=func(a,’d’);
14.动态内存分配:
掌握malloc函数的使用方法。
知识点详解及习题
一、
指针概念
1、指针
⏹指针:
一个变量的地址。
⏹指针变量:
是一个变量,存放另一个变量的地址。
⏹任何变量都在计算机内存中占有一块内存区域,变量的值就存放在这块内存区域之中
⏹一个变量的访问(访问是指取出其值或向它赋值)
(1)直接访问,通过变量名访问,如通过变量名i直接访问。
(2)间接访问,通过该的变量指针来访问,如通过i_pointer访问变量i。
2、指针变量
●指针变量定义的一般形式:
类型标识符*标识符
如:
int*pointer_1,*pointer_2;
(1)该指针变量指向的变量的类型。
如i_pointer指向的变量i是整型。
(2)该指针变量指向哪一个变量,即该指针变量的值是多少。
如i_pointer的值是2000。
例1
inti,j;
/*定义两个整型变量*/
int*pointer_1,*pointer_2;
float*pointer_3;
char*pointer_4;
void*pointer_5;
指针变量的赋值:
pointer_1=&
i;
pointer_2=&
j;
pointer_3=&
(?
)
⏹为什么在定义指针时要指定基类型?
⏹注意,指针变量中只能存放地址,不能将一个非地址类型的数据(如常数等)赋给一个指针变量,如:
pointer_1=100;
⏹也可以在定义指针变量的同时指定其初值,
inta;
int*p=&
a;
3指针变量的引用
有两个运算符可以引用指针变量:
(1)&
:
取地址运算符。
如pointer_1=&
(2)*:
指针运算符。
用于访问指针变量所指向的变量。
(取其指向的内容)
例;
i=3;
直接访问
ptr=&
*ptr=3;
间接访问
(3)*和&
是互逆运算如*&
i,i,*ptr三者表示内容相同,都是3
说明:
●指针变量必须定义且赋值后,才能用*运算符访问所指向的变量。
int*p;
(未规定指向哪个变量)
*p=100;
这种错误称为访问悬挂指针(suspededpointer)。
正确的引用:
p=&
(规定指向a)
●区分:
*运算符在不同场合的作用,编译器能够根据上下文环境判别*的作用。
inta,b,c;
int*p;
(*表示定义指针)
(*表示指针运算符)
c=a*b;
(*表示乘法运算符)
例2输入a和b两个整数,按先大后小的顺序输出a和b。
#include<
stdio.h>
main()
{
int*p1,*p2,*p,a,b;
scanf("
%d,%d"
&
a,&
b);
p1=&
p2=&
b;
if(a<
b)
{p=p1;
p1=p2;
p2=p;
}/*p1指向较大值,p2指向较小值.(交换的是ab地址)*/
printf("
a=%d,b=%d\n"
a,b);
max=%d,min=%d\n"
*p1,*p2);
4指针变量作为函数的参数
作用:
将一个变量的地址传递到另一个函数中(常用方法)
例3题目要求输入a和b两个整数,按先大后小的顺序输出a和b。
voidswap(int*p1,int*p2)
{intp;
p=*p1;
/*是否可以定义int*p;
*p=*p1;
为什么?
*/
*p1=*p2;
*p2=p;
{inta,b;
scanf("
pointer_2=&
if(a<
b)
swap(pointer_1,pointer_2);
/*另一种调用swap()的形式是:
b)swap(&
*/
printf("
\n%d,%d\n"
例4输入a、b、c三个整数,按大小顺序输出。
intswap(int*pt1,int*pt2)
p=*pt1;
*pt1=*pt2;
*pt2=p;
}
intexchange(int*q1,int*q2,int*q3)
{if(*q1<
*q2)swap(q1,q2);
if(*q1<
*q3)swap(q1,q3);
if(*q2<
*q3)swap(q2,q3);
{inta,b,c,*p1,*p2,*p3;
%d,%d,%d"
&
a,&
b,&
c);
p1=&
p3=&
c;
exchange(p1,p2,p3);
\n%d,%d,%d\n"
a,b,c);
5数组的指针和指向数组的指针变量
指针可以指向数组和数组元素,当一个指针指向数组后,对数组元素的访问,既可以使用数组下标,也可以使用指针。
并且,用指针访问数组元素,程序的效率更高。
●指向数组元素的指针变量
指向数组元素的指针变量,其类型应与数组元素相同。
例
inta[10];
/*元素为整型*/
floatb[10];
/*元素为实型*/
/*可以指向数组a的元素*/
float*pf;
/*可以指向数组b的元素*/
为了让指针p指向数组a,应把数组a的地址赋给指针变量p。
或p=&
a[0];
或int*p=a;
●通过指针引用数组元素
当使指针p指向数组a后,可以用指针p访问数组的各个元素。
如果指针p指向数组a(指向数组的第一个元素a[0]),则:
p+1指向下一个元素a[1],注意不是将p值简单加1。
(前面提到的为什么定义指针时需要指定类型)
•如果数组元素是整型,p+1表示p的地址加2;
•如果数组元素是实型,p+1表示p的地址加4;
•如果数组元素是字符型,p+1表示p的地址加1。
p+i指向元素a[i]。
可以使用*(p+i)访问元素a[i]。
另外:
1、p+i也可以记作a+i,指向元素a[i]。
2、指向数组的指针变量也可以带下标,如,p[i]与*(p+i)等价,表示元素a[i]。
{inta[]={1,2,3,4,5,6},*p;
p=a;
*(p+3)+=2;
printf(“%d,%d\n”,*p,*(p+3));
A0,5B1,5
C0,6D1,6
例5输出数组的全部元素。
(设10个元素,整型)。
●下标法(常用,很直观)
{inta[10];
inti;
for(i=0;
10;
i++)
%d"
a[i]);
\n"
);
for(i=0;
i++)
%d"
a[i]);
●用数组名计算数组元素的地址。
(效率与下标法相同,不常用,why?
{inta[10];
inti;
*(a+i));
●用指针访问各元素。
(常用,效率高)main()
int*p,i;
scanf(“%d”,&
printf(“\n”);
for(p=a;
p<
(a+10);
p++)
/*a++?
?
*/
*p);
⏹若指针p指向数组a,虽然p+i与a+i、*(p+i)与*(a+i)意义相同,但仍应注意p与a的区别,a是地址常量,而p是指针变量。
例、
a<
(p+10);
a++)
a代表数组的首地址,是不变的,a++不合法
⏹指针变量可以指向数组中的任何元素,注意指针变量的当前值。
例5输出数组a的10个元素。
(检查代码,判断输出?
{int*p,i,a[10];
p=a;
p++);
i++,p++)
正确代码
{int*p,i,a[10];
i++,p++)
⏹使用指针时,应特别注意避免指针访问越界。
在上例中,第二次for循环,p已经越过数组的范围,但编译器不能发现该问题。
⏹指针使用的几个细节。
设指针p指向数组a(p=a),则:
①p++(或p+=1),p指向下一个元素。
②*p++,相当于*(p++)。
因为,*和++同优先级,++是右结合运算符。
③*(p++)与*(++p)的作用不同。
*(p++):
先取*p,再使p加1。
*(++p):
先使p加1,再取*p。
④(*p)++表示,p指向的元素值加1。
⑤如果p当前指向数组a的第i个元素,则:
*(p--)相当于a[i--],先取*p,再使p减1。
*(++p)相当于a[++i],先使p加1,再取*p。
*(--p)相当于a[--i],先使p减1,再取*p。
6数组名作函数参数
数组名代表数组首地址,因此,它作实参在函数调用时,是把数组首地址传送给形参。
这样,实参数组和形参数组共占同一段内存区域。
从而在函数调用后,实参数组的元素值可能会发生变化。
例7将数组a中n个元素按相反顺序存放。
算法:
a[0]与a[n-1]交换,a[1]与a[n-2]交换,.....,a[(n-1)/2]与a[n-int((n-1)/2)]交换。
实现:
用i,j作元素位置变量,开始i=0,j=n-1。
将a[i]与a[j]交换,然后i加1,j减1,直到i=(n-1)/2。
voidinv(intx[],intn)
{intt,i,j,m=(n-1)/2;
i<
=m;
i++)
{j=n-1-i;
t=x[i];
x[i]=x[j];
x[j]=t;
return;
{staticinti,a[10]={3,7,9,11,0,6,7,5,4,2};
theoriginalarray:
a[i]);
inv(a,10);
thearrayhansbeeninverted:
i++)
函数inv()可以用指针作形参,运行情况与用数组作形参相同。
voidinv(int*x,intn)
{int*p,t,*i,*j,m=(n-1)/2;
i=x;
j=x+n-1;
p=x+m;
for(;
=p;
i++,j--)
{t=*i;
*i=*j;
*j=t;
}
return;
例8从10个数中找出其中最大值和最小值。
只找出其中最大值和最小值,不能改变元素的排列顺序)。
方法1、实参和形参均用数组变量。
方法2、形参和实参均使用指针变量。
小结:
数组作函数的参数,实参和形参之间传送数组的首地址,首地址可以用指针表示,也可以用数组名表示,因此,实参和形参有以下四种组合情况。
若有以下调用语句,则不正确的fun函数的首部是
A)voidfun(intm,intx[])
B)voidfun(ints,inth[41])
C)voidfun(intp,int*s)
D)voidfun(intn,inta)
main()
{…
inta[50],n;
…
fun(n,&
a[9]);
7多维数组的指针
●二维数组staticinta[3][4]={{1,3,5,7},{9,11,13,15},{17,19,21,23}};
理解为:
有三个元素a[0]、a[1]、a[2],每一个元素代表一行,每一个元素是一个包含4个元素的数组
⏹数组名a代表:
整个二维数组的首地址,也是元素a[0][0]的地址,同时代表第一行元素的首地址。
⏹a+1表示第二行元素的首地址,也是元素a[1][0]的地址。
⏹a+2表示第三行元素的首地址,也是元素a[2][0]的地址。
设数组的首地址是2000,则有a等于2000。
第一行4个元素,占8字节,因此第二行的首地址是2008,即a+1等于2008。
第二行4个元素,占8字节,因此第三行的首地址是2016,即a+2等于2016。
由于把a[0]、a[1]、a[2]看成一维数组(一维数组名),它们代表各自数组的首地址,即:
a[0]~&
a[0][0]等价于*(a+0),
a[1]~&
a[1][0]等价于*(a+1)
a[2]~&
a[2][0]等价于*(a+2)
根据一维数组的表示方法,有
a[0]+1:
~&
a[0][1]一维数组中第二个元素的地址
a[0]+2:
~&
a[0][2]
a[1]+1:
a[1][1];
等价于*(a+1)
综上所述,二维数组a的地址用下图说明
已知某元素的指针后,可以用*运算符访问该元素。
例9
*(a[1]+2)=a[1][2]=13
二维数组元素a[i][j]的值可用以下方式表示:
*(a[i]+j),*(*(a+i)+j),a[i][j]
二维数组元素a[i][j]的地址可用以下方式表示:
a[i]+j,*(a+i)+j,&
a[i][j]
P243:
表10-2数组a的性质
练习:
若有以下定义和语句:
ints[4][5],(*ps)[5];
(一维数组的指针,数组有5个元素,区别指针数组,*ps[5],存放5个指针)
ps=s;
则对s数组元素的正确引用形式是()
A)ps+1
B)*(ps+3)
C)ps[0][2]
D)*(ps+1)+3
●指向多维数组的指针变量
(1)指向数组元素的指针变量(用列指针对二维数组进行操作)。
例10用指针变量输出数组元素的值。
main()
{staticinta[3][4]={1,3,5,7,9,11,13,15,17,19,21,23};
for(p=a[0];
p<
a[0]+12;
p++)
{
if((p-a[0])%4==0)printf("
%4d"
*p);
注意:
本例用指针顺序访问二维数组的元素。
若需访问二维数组a[n][m](n行m列)的某个元素a[i][j],计算该元素的相对位置公式为:
i*m+j(i,j=0,1,2,...)
这种方法相当于把二维数组转化为一维数组来使用。
(2)指向m个元素组成的一维数组的指针变量(用行指针对二维数组进行操作)
int(*p)[4];
例11输出二维数组任一行任一列元素的值。
{inta[3][4]={1,3,5,7,9,11,13,15,17,19,21,23};
int(*p)[4],i,j;
p=a;
scanf(“%d,%d”,&
i,&
j);
printf(“a[%d][%d]=%d\n”,i,j,*(*(p+i)+j);
(*p)[4]表示p为单独一个指针变量,行指针,指向含有4个元素的一维数组,即*p有4个元素,每个元素为整型。
*p[4]为指针数组,*p数组有4个元素,每个元素中存放的是地址,即每个元素都为指针变量。
●多维数组的指针作函数参数,两种情况:
(1)用指向变量的指针变量
(2)用指向一维数组的指针变量
8字符串的指针
●字符串的表现形式
C语言中,有两种方式可以实现字符串:
字符数组
字符指针
(1)字符数组
{staticcharstring[]="
IloveChina!
"
;
printf("