目录
1.指针变量
数据在内存中的地址也称为指针,如果一个变量存储了一份数据的指针,我们就称它为指针变量。
1.1地址
#include <stdio.h>
int main()
{
int a=10;//定义int型变量a并赋值为10;
int *p;//定义int型指针 p;
p=&a;//指针指向 a;
printf("%d\n",a);//变量a的值;
printf("%d\n",*p);//解地址p取得的既定内存中的值(变量a的值);
printf("%p\n",&a);//变量a在内存中的地址;
printf("%p\n",p);//指针p所指的对象(即变量a)在内存中的地址;
printf("%p\n",&p);//指针p自身在内存中的地址;
return 0;
}
指针变量与其所指的变量间类型必须匹配,例如char类型的指针对应char类型的变量。
在定义和取值时,需要加上 * (指针运算符),* 与 &同时使用时会相互抵消。
指针自身的地址与指针所指对象的地址在内存中是两个不同的地址。
1.2指针的偏移
#include <stdio.h>
int main()
{
int a=10;//定义int型变量a并赋值为10;
int *p;//定义int型指针 p;
p=&a;//指针指向 a;
printf("%p\n",&a+1);//变量a偏移1个元素位置后在内存中的地址;
printf("%p\n",p+1);//指针p所指的对象(即变量a)偏移1个元素位置后在内存中的地址;
return 0;
}
当变量a是int 型时,偏移一个元素位置即4个字节(char型为1个字节),即与变量类型相同。
变量a在内存中的地址是常量(指针p的地址同样为常量),所以当指针偏移一个位置,使用 *p 则无法输出变量a。指针的地址的大小与编译器的位数有关,32位编译器指针地址为4字节,64位为8字节。
1.3空指针与野指针
无论是空指针还是野指针,都是错误的。
#include <stdio.h>
int main()
{
int a=10;//定义int型变量a并赋值为10;
int *p;//定义int型指针 p;
int *q=NULL; //空指针;
*p=20;//指针未指向既定内存,野指针;
*q=20;//空指针,无法访问;
return 0;
}
1.4指针与元素值
通过指针改变元素值有两个方法,一是通过解地址符号即*p直接改变元素的值;
#include <stdio.h>
int main()
{
int a=15,b=99,c=222;//定义三个int型变量a,b,c;
int *p=&a;//定义指针p(定义需要加上 * ),并指向变量a;
*p=b; //解地址p(即*p=变量a),可得a=b,所以a=99;
c=*p; //同理 c=a,c=99;
printf("%d %d %d %d\n",*p,a,b,c);//输出*p=99,a=99,b=99,c=99;
return 0;
}
二是通过改变指针所指向的元素来改变解地址后的值(并非元素的本值)。
#include <stdio.h>
int main()
{
int a=15,b=99;//定义变量a,b并分别赋值15和99;
int *p=&a,*q=&b;定义指针p指向a,定义指针q指向b;
printf("%d\n",*p);//*p=a,所以值为15;
printf("%d\n",a);//值为15;
printf("%d\n",*q);//*q=b,所以值为99;
printf("%d\n",b);//值为99;
p=q;//将指针q所指向的地址赋值给指针p,此时指针p指向变量b而非变量a;
printf("%d\n",*p);//指针p指向变量b,所以*p=b=99;
return 0;
}
2.指针与数组
2.1指针与一维数组
2.1.1指针指向数组方式与输出方式
两种指向方式,一种完整形式,另一种是简化形式;
三种输出方式(除了以非指针方式a[0]外),分别为解地址p、p[N]、解地址a;
#include <stdio.h>
int main()
{
int a[5]={1,2,3,4,5};
int *p;
p=&a[0];//完整形式的指针与数组指向关系,指向数组a的首元素地址;
p=a; //简化形式,同样指向数组a的首元素地址;
printf("%d",*p); //解地址p方式输出a[0],首元素地址;输出1;
printf("%d",p[1]);//输出第二个元素;输出2;
printf("%d",*a);//解地址a方式输出,数组a[N]中,a就是数组的首元素地址;输出1;
return 0;
}
2.1.2指针在数组中的偏移
如果指针变量 p 已指向数组中的一个元素,则 p+1 指向同一数组中的下一个元素,p-1 指向同一数组中的上一个元素。
#include <stdio.h>
int main()
{
int a[5]={1,2,3,4,5};
int *p;
p=a; //简化形式,同样指向数组a的首元素地址;
printf("%d\n",a[1]);//数组方式输出第二个元素;
//此三种方法都使用了指针的偏移;
printf("%d\n",*a+1);//数组首元素地址+1,输出第二个元素;
//此方法中 a 与指针用法类似,但是a不是指针,因为a不是变量,是常量,所以a不能被修改;
//a++在这里不能使用,会报error;
printf("%d\n",*(p+1));//指针+1方式输出第二个元素;
printf("%d\n",p[1]);//指针[1]方式输出第二个元素;
return 0;
}
2.2指针与二维数组a[M][N]
2.2.1 *p
定义:
*p(定义单个元素的指针)在二维数组中有三种方式与二维数组产生指向关系
p=&a[0][0] 是完整指向形式,指向首地址为a的二维数组的第0行第0列,指向1;
p=a[0] 是简化形式,指向首地址为a的二维数组的第0行,也就是将二维数组的行内元素看作一个整体,指向首元素 1;
p=*a 是简化形式,指向首地址为a的二维数组的第0行,同上;
输出:
printf("*p=%d\n",*p); //输出1;
printf("p[1]=%d\n",p[1]); //前面定义p指针指向第0行,这里p[1]指第0行第1个,即输出2;
2.2.2 (*q)[N]
(*q)[3](定义容量为3的一维数组的行指针)在二维数组中有两种方式与二维数组产生指向关系
q=&a[0] 是完整指向形式,使指针q指向二维数组的第0行,如果使用此方法进行指向,输出时则根据这里指向的行数进行输出;例如q=&a[1],则指向数组第1行 ,输出q[1][1],则输出指向的那一行后面的2行(指针指向的行数在输出时记为0),具体看下例;
如有数组a[3][3],即三行三列,定义指针 *p=&a[1],指向数组第1行首元素,输出p[2][1]时就会超出数组内存范围,因为定义数组指向第1行首元素地址,第0行不在此范围,所以只能输出原数组的第1行和第2行,输出第2行第2个元素时应表达为p[1][1]。
q=a 是简化形式,指向二维数组的第0行地址;
输出
printf("*(*(q+0)+0)=%d\n",*(*(q+0)+0)); //(指向0行时)输出二维数组的第0行第0个元素
printf("*(q[2]+1)=%d\n",*(q[2]+1)); //(指向0行时)输出二维数组的第2行第1个元素
printf("q[2][2]=%d\n",q[2][2]); //(指向0行时)输出二维数组的第2行第2个元素
#include <stdio.h>
#define M 3
#define N 3
int main()
{
int a[M][N]={
{1,2,3},
{4,5,6},
{7,8,9},
};
int *p;//单个元素指针
p=&a[0][0]; //完整表达形式;
p=a[0]; //a[0]代表数组起始元素地址;
p=*a; //*a代表数组起始元素地址;
printf("*p=%d\n",*p); //输出1;
printf("p[1]=%d\n",p[1]);//前面定义p指针指向第0行,这里p[1]指第0行第1个,即输出2;
printf("\n\n");
int (*q)[3];//定义容量为3的一维数组的行指针;
q=a; //指向二维数组的第0行地址;
q=&a[0]; //完整表达式,使指针q指向二维数组的第0行;
//如果使用第二种方法进行指向,输出时则根据具体的行数进行输出;
printf("*(*(q+0)+0)=%d\n",*(*(q+0)+0));//输出二维数组的第0行第0个元素,输出1;
printf("*(q[2]+1)=%d\n",*(q[2]+1));//输出二维数组的第2行第1个元素,输出8;
printf("q[2][2]=%d\n",q[2][2]);//输出二维数组的第2行第2个元素,输出9;
return 0;
}
输出结果为:
2.3数组指针与指针数组
数组指针是指向数组的指针,它还是一个指针,只不过指向数组而已,2.1和2.2所说的指针就是数组指针。
指针数组实际是一个数组,长度是由数组本身决定,这个数组的所有元素都是指针类型,存放的都是地址。指针数组是一个由指针变量组成的数组,也就是说其中的元素都是指针变量。
#include <stdio.h>
#define M 3
int main()
{
int a[M]={10,20,30}; //定义一个一维数组;
int *p[M]; //定义一个指针数组;
for(int i=0;i<M;i++){
p[i]=&a[i]; //使用for循环将指针数组中的每个指针变量分别指向下标相同的一维数组;
}
for(int i=0;i<M;i++){
printf("a[%d]:%d\n",i,*p[i]); //通过指针数组输出一维数组中的元素;
}
return 0;
}
输出结果:
3.指针与函数
3.1指针类型的函数变量
函数的参数可以是单个int 、char变量、数组名(数组首元素地址),也可以是指针变量。
函数的参数遵循实参与虚参相统一原则:参数个数一致且变量类型一致;
#include <stdio.h>
void x(int *p1,int *p2,int *p3){
void y(int *p1,int *p2);
if(*p1<*p2){
y(p1,p2);
}
if(*p2<*p3){
y(p2,p3);
}
if(*p1<*p3){
y(p1,p3);
}
}
void y(int *p1,int *p2){
int z;
z=*p1;
*p1=*p2;
*p2=z;
}
int main()
{
int a,b,c;
int *p1,*p2,*p3;
scanf("%d%d%d",&a,&b,&c);
p1=&a;
p2=&b;
p3=&c;
x(&a,&b,&c);
printf("%d>%d>%d\n",*p1,*p2,*p3);
return 0;
}
3.2函数指针
函数同样可以作为指针来使用:
定义指针后应注意将指针的地址指向函数的地址,才能实现对应关系。
声明格式:数据类型(*函数名)(参数) 例如 int(*p)(int)
# include <stdio.h>
int Max(int, int); //函数声明
int main(void)
{
int(*p)(int,int); //定义一个函数指针
int a,b,c;
p=Max; //把函数Max赋给指针变量p, 使p指向Max函数
printf("please enter a and b:");
scanf("%d%d",&a,&b);
c=(*p)(a,b); //通过函数指针调用Max函数
printf("a=%d b=%d max=%d\n",a,b,c);
return 0;
}
int Max(int x,int y){ //定义Max函数
int z;
if (x>y){
z=x;
}else{
z=y;
}
return z;
}
输出结果:
4.指针与字符串
4.1字符串
#include <stdio.h>
int main()
{
char str[10]="ABCD";
printf("%s\n",str);//str为字符串首地址,输出ABCD;
printf("%s\n",str[2]);从C开始输出,输出CD;
return 0;
}
4.2指针在字符串中的运用
#include <stdio.h>
int main(void)
{
char *a="abc";
printf("输出字符:%c \n",*a); //输出字符,使用"%c"
printf("输出字符:%c \n",*(a+1)); //输出字符,使用"%c"
printf("输出字符串:%s \n",a); //输出字符串,使用"%s";而且a之前不能有星号"*"
}
输出结果:
4.3二种定义多个字符串的方法
#include <stdio.h>
int main()
{
char a[3][10]={"aaa","bbb","ccc"};
char *b[3]={"ddd","eee","fff"};
for(int i=0;i<3;i++){ //二维字符串数组的存储方式
printf(" a[%d]=%s\n",i,a[i]);
printf("所占地址:%p\n",a[i]);
}
for(int i=0;i<3;i++){ //一维指针数组的存储方式
printf(" b[%d]=%s\n",i,b[i]);
printf("所占地址:%p\n",b[i]);
}
}
输出结果: