指针的概念
普通变量必须先定 义后引用,例如定义整型变量x通过“int x;"实现,变量名x就代表变量本身。因此在引用变量时,可以直接访问变量,例如给变量x赋值10,可通过语句“x=10;"实现。此种访问变量的方式被称为“直接访问”。
还有一种访问方式,即“间接访问”。如下图所示,变量p的值是“&x”即变量x的内存地址,因此通过变量p可以找到内存中的变量x(如图中的箭头示意),对变量p进行操作,就可以间接地操作变量X。这里的变量p就是所说的“指针”。
指针指向普通变量示例
指针也是个变量,它和普通变量一样占用一定的存储空间。但不同的是,指针变量内存放的是地址,而不是普通数据。因此,指针是一个地址变量。 如上所示,指针变量p的值是变量&x,等价于语句"p=&x,"。
指针变量的定义
指针变量的定义形式为: 类型 *指针变量名
说明: * 是指针变量的标志:。“类型”限制该指针指向的变量的数据类型。
char *cp; //cp是指向字符型变量的指针变量
定义指针变量后,其值是随机的,是不确定的,只是表明该指针可以指向哪种类型的变量,但此时并没有确切地指向哪个变量。
指针变量的引用
指针变量在使用之前必须先赋值,换言之,指针在使用前必须有确切的指向。
#include <stdio.h>
#include <string.h>
void main()
{
int *p,i;
p=&i;
*P=100;
printf("*p=%d\n",*p); //*p=100
printf("i=%d\n",i); //i=100
}
在引用阶段,首要的工作就是给指针变量赋值(赋地址),让指针有确切的指向,p=&i。指针变量本身就是地址,而*指针变量则是所指向地址空间的内容。
指针变量的初始化
在定义指针变量的同时给它赋地址值,这种做法称为指针变量初始化。
格式:类型 *指针变量名=&变量名;
int x,*p; //定义部分
p=&x; //引用部分
等价于: int x,*p=&x;
注意:如果改写为int *p=&x,x;系统就会报错,因为机器从左至右,系统会报错int *p=&x;中的x没有定义。
指针变量的运算
1、赋值运算
在讲述指针变量的引用时,曾提到指针使用之前,必须有确切的指向,即指针必须指向某一变量的地址。这一操作就是利用“赋值运算”完成的,即将某一地址赋值给指针变量。例如有以下赋值语句:
(1) int *p,i;
p=&i;
(2) int *p1,*p2, a; //定义两个指针变量p1和p2
p2=&a; //变量a的地址赋值给指针变量p2
p1=p2; //将指针P2的值赋值给pl,等价于两指针指向同一内存地址
(3) int *p;
p=2000; //不合法,不允许把一个数赋值给指针变星
说明:如(3)中所示,“2000 不再单纯意味着是整数,它将代表的是内存地址为2000的空间,而且并不知道这块地址中保存的是什么数据,可能是计算机系统数据,一且后续对 指针进行操作,就意味着对内存这块地址的数据进行操作,这是危险的。
2.算术运算
在指针与数组结合使用时,指针的算术运算才是有意义的,对于指针指向其他类型变量时,指针做算术运算是毫无意义的。指针可以参加的算术运算仅限于以下两种情况:
1)指针变量加上或减去一个整数N
其表示的含义是,让指针的指向从当前位置向前或向后移动N个位置。注意,这里的“N个位置”并不是在原地址上加上数字N,因为不同数据类型所占内存的字节数是不同的,到底是多少个字节,将取决于指针变量的数据类型,相当于“NX数据类型所占字节数”个字节。在编程时,我们不用考虑到底移动了多少字节,系统会自动转换, 我们只需关心移动了多少个位置。
例如: int *p,a[10]; //定义指针变量p和一维数组a
p=a; /数组名即是数组的首地址。该语句意味指针P指向数组的首地址,等价于p=&a[0];
p=p+2: //算数运算, 即指针P指向数组元素a[2], 等价于p=&a[2]
2)指针变量的自增和自减运算
表示的含义是,让指针相对手现在的位置向前或向后移动一 个位置。运算后指针地址值的变化量取决于指针的数据类型。
char . *p="hello world!" ; //定义字符指针p,指向字符串的首地址
whlle(*p) // *p表示当前位置内存放的字符,进行判断当前字符不为0,即字符串未结束
putchar(*p++) //输出当前位置的字符,指针自增向后移一个位置,指向下一字符
指针变量在自增自减时,根器其类型的长度确定增减量,从而保证指针变量总是指向后一个或前一个元素。在编程时,不必考虑其实际增量是多少字节。
注意*和++,--结合使用时,属于同一优先级,但结合性是从右到左
例如:*p++等价于*(p++),其含义是:取出P当前所指向位置的内容,然后P指向下一个元素
*++p等价于*(++p),其含义是:移动p指向下一个元素,然后取出P所指向位置的内容
++*p等价于++(*p),其含义是:把p所指向位置的内容增1。
(*p)++取出p所指向位置的内容,然后该内容加1.
3.关系运算
指针的关系运算仅限于以下两种情况:
(1)指向同一数据类型的两个指针变量之间可以进行<、<=、>、>=、==和!=等关系运算,这一运算常用于指针与数组的结合使用中,判断两指针的位置关系,例如:
p1==p2 //两指针指向同一位置
p1>p2 //p1相对p2位于高地址位置
pl<p2 //p1相对p2位于低地址位置
(2)指针变量与0之间也可以进行关系运算,用来判断指针是否是空指针。
p==0或p!=0
题目:
int a=10,b,*p;
p=&a;b=(*p)++;
a=11,b=10
指针与数组
通过对数组的学习,我们了解到同一数据类型的数据可以利用数组集中存储在内样一片连续的空间内。利用数组的下标可以实现对数组内各元素的访问。当指针与数组进行结合使用时,为我们提供了另一种间接访问数组成 员的方法。
指针与一 维数组
1. 数组名充当“指针”
数组一旦定义,数组名就是数组的首地址,即数组第0个元素的地址,数组名也可以充当“指针”的角色,通过加减“位移量”来表示数组内各个元素的地址。
数组名与指针变量的区别在于,“数组名”是常量,它的值就是数组的首地址,不能像指针变量那样进行自增或自减运算,数组名也不能出现在“赋值运算符”的左边。
int a[10];
a++; //不合法,a是常量
a=1000; //不合法,a是常量,不能给常量赋值
2、定义指向数组的指针变量
定义一个与数组类型相同的指针指向数组。
int i,a[10],*p;
p=a;
p+i表示相对指针p的当前位置向后移动i个位置的地址,而p的位置是不动的,始终指向数组的·首地址。数组名可以充当指针,指向数组的指针也可以充当数组名
指针域二维数组
1、数组名充当指针
通过前面的学习,我们知道二维数组定义时有行有列,但计算机的内存工具之分,是一片连续的空间。因此,在存放“维数组元素到内存空间时,是按照行从上至下,每一行从左至有的顺序依次来存放的。但在表示一维数组的地址时,比一维数组委复杂一些,有“行地址”与“列地址”之分。下面举例说明:
定义一个二维数组:
int a[3][4];
a为二维数组名,此数组有3行4列,共12个元素。也可以这样理解,数组由三个元素组成: a[0],a[1]、a[2].而它们本身又是一个一维数组, 且都含有4个元素。
例如,a[0]所代表的一维数组包括的4个元素为:
a[0][0] a[0] [1] a[0][2] a[0][3]
(I)行地址:数组名a代表二维数组的首地址,a等价于a+0,因此a也代表第0行的首地址。依此类推,a+1 代表第1行的首地址,a+2代表第2行的首地址,
(2)列地址:如前所述把a[0]、a[1]、 a[2]看成是一维数组名, 即它们分别代表它们所对应的数组的首地址,也就是说,a[0]等价于a[0]+0, 代表第0行中的第0列元素的地址,即&a[0][0]; a[0]+1即代表第0行第1列元素的地址,即&a[0][1]。 a[1]等价于a[1]+0,代表第1行中第0列元素的地址,即&a[1][0]; 总结出一般通式,a[i]+j即代表第i行第j列元素的地址,即&a[i][j]。
以上是利用数组的下标来表示数组元素的地址,我们知道数组名也可以充当“指针”,也可以采用指针的形式来表示元素的地址,之间可以等价转换
2.定义指向数组的指针
由于二维数组有“行地址”和“列地址”之分,因此定义指向维数组的指针时,就有“行指针”与“列指针”之分,两者在定义的格式和遍历二维数组的方式上都有所不同。
1)列指针
定义指向二维数组的列指针的形式为: 类型 *指针变量名
利用列指针访问二维数组
#include <stdio.h>
void main()
{
int a[3][4],i,j,*p;
p=a[0]; //指针指向第0行0列的地址(列地址)
printf("input number:\n");
for(i=0;i<3;i++)
{ for(j=0;j<4;j++)
scanf("%d",p+i*4+j);
}
printf("The two-dimensional array is:\n");
for(i=0;i<3;i++)
{ for(j=0;j<4;j++)
printf("%-4d",*(p+i*4+j));
printf("\n");
}
}
2)行指针
定义指向二维数组的行指针的形式为:
类型 (*指针变量名)[长度]
说明:在定义的格式上,“*指针变量名”两边的圆括号不能省略,如果省略,将变成另一个概念指针数组,后面紧跟一个方括号,其中的长度是指二维数组的列数。
#include <stdio.h>
void main()
{
int a[3][4],i,j,(*p)[4];
p=a; //指针指向第0行0列的地址(列地址)
printf("input number:\n");
for(i=0;i<3;i++)
{ for(j=0;j<4;j++)
scanf("%d",*(p+i)+j);
}
printf("The two-dimensional array is:\n");
for(i=0;i<3;i++)
{ for(j=0;j<4;j++)
printf("%-4d",*(*(p+i)+j));
printf("\n");
}
}
指针域函数
当指针变量作为被调函数的形参时,主调函数的实参必须是地址值,因为地址才能赋值给指针变量。即形参的指针指向了实参变量的地址。实现了双向传递。
#include <stdio.h>
void swap(int *p,int *q)
{
int temp;
temp=*p;
*p=*q;
*q=temp;
printf("(*p)=%d,(*q)=%d\n",*p,*q);
}
void main()
{
int a,b;
printf("input :a and b:");
scanf("%d%d",&a,&b);
printf("交换前:a=%d,b=%d\n",a,b);
swap(&a,&b);
printf("交换后;a=%d,b=%d\n",a,b);
}
#include <stdio.h>
void swap(int *p,int *q)
{
int temp;
temp=p;
p=q;
q=temp;
printf("(*p)=%d,(*q)=%d\n",*p,*q);
}
void main()
{
int a,b;
printf("input :a and b:");
scanf("%d%d",&a,&b);
printf("交换前:a=%d,b=%d\n",a,b);
swap(&a,&b);
printf("交换后;a=%d,b=%d\n",a,b);
}
原因在于swap函数中交换了变量p和q,相对于将两个指针的指向互换,而没有交换原理所指变量的值
指针变量作为函数的返回值
返回指针的函数一般定义格式:
类型名 *函数名(参数表){ }
float *fun(int x,float y);
表示函数fun的返回值类型是指向float型变量的指针。
#include <stdio.h>
int *max(int a,int b,int c) //函数返回值为int *
{
int m;
m=a>b?a:b;
m=m>c?m:c;
return (&m);
}
void main()
{
int x,y,z;
int *mp;
printf("input x,y,z:");
scanf("%d,%d,%d",&x,&y,&z);
mp=max(x,y,z);
printf("the max number is %d\n",*mp);
}
题目:以下语句正确的是D
A、char a[6]={"hello!"}; B、char a[7],a="helllo!";
C、int *a="hello!"; D、char *a;a="hello!";
int *p[3]; 定义一个指针变量数组p,该数组含有3个元素,每个元素都是类型为int的指针。