指针
变量访问常用的方式有两种,一种是直接通过变量名访问,另一种则是通过访问变量的存放位置访问,指针变量,即存有变量存放地址的变量
例如,你打出租车去中国计量大学,你可以和司机说学校名中国计量大学去,也可以和司机说杭州钱塘区下沙学源街258号的地址去。
示例代码
#include<stdio.h>
int main()
{
int a=10;
int *pa=&a;
printf("a is %d\n",a); //变量名访问
printf("the number pa save is %d\n",*pa);//地址访问(指针)
return 0;
}
指针定义
//数据类型 *变量名;
int *p;
double *p;
//&用于获取变量地址
int *pa=&a;//将a的地址赋给pa指针变量
//*用于获取地址中所保存的内容
int a=*pa;//将pa所指向地址存储的值赋给a
指针类型
既然指针存储的是变量的地址为什么还要区分类型呢?
指针的类型会影响两个因素:1.决定指向空间的大小 2.决定增量
#include<stdio.h>
int main()
{
int a = 0x1234;
int *p = &a;
char *c = &a;
printf("the address of p = %p\n",p);
printf("the address of c = %p\n\n",c);
printf("the content of p = %p\n",*p);
printf("the content of c = %p\n\n",*c); //访问时会根据指针类型访问不同空间大小,虽然指针指向地址相同,但是空间大小并不一致
printf("the next address of p = %p\n",++p);
printf("the next address of c = %p\n\n",++c); //同时自增的大小也不同
}
因此,在定义指针变量时要注意指针类型一致。
指针应用场景
函数封装
#include<stdio.h>
void exchange_1(int a,int b)
{
int tmp = a;
a = b;
b = tmp;
}
void exchange_2(int *pa,int *pb)
{
int tmp = *pa;
*pa = *pb;
*pb = tmp;
}
int main()
{
int a=10;
int b=20;
exchange_1(a,b);
printf("after exchange1 a is %d,b is %d\n",a,b);
exchange_2(&a,&b);
printf("after exchange2 a is %d,b is %d\n",a,b);
}
从结果中可以看出经过exchange_1函数交换,原来的a,b值并未发生交换,这是因为函数并不是对a,b原有的地址进行操作,而经过exchange_2后,a,b 发生了交换
需要你指向特定的存储区域
1.单片机
2.armbootloarder
指针与数组
通过指针引用数组
定义指针指向数组,并通过指针偏移遍历数组
#include<stdio.h>
int main()
{
int arr[3]={1,2,3};
int *p;
p=arr;//定义指针指向数组,数组名就是数组的首地址
printf("首元素是:%d\n",*p);
int length=sizeof(arr)/sizeof(arr[0]);
printf("arr中的元素为:");
for(int i=0;i<length;i++)
{
printf("%d ",*p);
p++;
}
return 0;
}
见怪不怪
1.指针当作数组名
#include<stdio.h>
int main()
{
int arr[6]={0,1,2,3,4,5};
int *p=arr;
for(int i=0;i<6;i++)
{
printf("%d ",p[i]);
}
return 0;
}
2.数组名拿来加
#include<stdio.h>
int main()
{
int arr[6]={0,1,2,3,4,5};
int *p=arr;
for(int i=0;i<6;i++)
{
printf("%d ",*(arr+i));
}
return 0;
}
指针二维数组
二维数组本质上还是数组,不同点是其数组元素还是一个数组(子数组),因此我们就可以知道a表示的是父数组的地址;
a[0],*a表示的子数组的地址
#include<stdio.h>
int main()
{
int arr[4][4]={{11,12,13,14},
{21,22,23,24},
{31,32,33,34},
{41,42,43,44}};
printf("父数组地址是0x%p\t偏移一位后为0x%p\n",arr,arr+1);
printf("子数组地址是0x%p\t偏移一位后为0x%p\n",*arr,*(arr)+1);
printf("子数组地址是0x%p\t偏移一位后为0x%p\n",arr[0],arr[0]+1);
}
小总结
二级(多级)指针
二级指针可以理解为指针的指针,即该指针变量保存的是指针变量的地址
#include<stdio.h>
int main()
{
int data=100;
int *p=&data;
printf("data的地址是:0x%p\n",&data);
printf("p保存的是data的地址:0x%p,内容是%d\n",p,*p);
int **p2=&p;
printf("p的地址是:0x%p\n",&p);
printf("p2保存的是.p的地址:0x%p,内容是0x%p\n",p2,*p2);
int ***p3=&p2;
printf("p2的地址是:0x%p\n",&p2);
printf("p3保存的是p2的地址:0x%p,内容是0x%p\n",p3,*p3);
}
数组指针与指针数组
数组指针
定义:指向一个数组的指针 int (*p)[4]
#include<stdio.h>
int main()
{
int arr[4][4]={{11,12,13,14},
{21,22,23,24},
{31,32,33,34},
{41,42,43,44}};
int(*p)[4]=arr;//定义一个数组指针指向int[4]的数组
//数组指针才真正等价于一个二维数组名
for(int i=0;i<4;i++)
{
for(int j=0;j<4;j++){
printf("%d\t",*(*(p+i)+j));//通过该指针遍历数组
}
printf("\n");
}
}
二级指针不可直接指向二维数组
#include<stdio.h>
int main()
{
int arr[4][4]={{11,12,13,14},
{21,22,23,24},
{31,32,33,34},
{41,42,43,44}};
int **p;
p=arr;//会出问题;一般认为*p是野指针
printf("The address of arr:0x%p\n",arr);
printf("p=0x%p\n",p);
printf("*p=0x%p\n",*p);
printf("*arr=0x%p\n",*arr);
}
在编译时产生了报错。
#include<stdio.h>
int main()
{
int arr[4][4]={{11,12,13,14},
{21,22,23,24},
{31,32,33,34},
{41,42,43,44}};
int(*p1)[4]=arr;//先定义一个数组指针
int **p=&p1;//定义二级指针指向该数组指针
**p=100;//通过二级指针修改数组
printf("%d",arr[0][0]);
}
指针数组
指针数组是一个保存指针变量的数组,其数组中的每个元素都为指针变量
#include<stdio.h>
#include<stdlib.h>
int main()
{
int a=10;
int b=20;
int c=30;
int d=40;
int* p[4]={&a,&b,&c,&d};
for(int i=0;i<4;i++)
{
printf("%d ",*p[i]);
}
}
函数指针与指针函数
函数指针
如果在程序中定义了一个函数,在编译时,编译系统为函数代码分配一段存储空间,这段存储空间的起始地址(又称入口地址)成为这个函数的的指针。
函数指针是指向函数的指针变量。
函数指针可以像一般函数一样,用于调用函数、传递参数。
#include<stdio.h>
void printWelcome()
{
puts("welcome to use");
}
int Increase(int data)
{
return ++data;
}
int main()
{
void (*p)(); //define functionPointer
p=printWelcome;//point to function
(*p)(); //use function
int (*pa)(int);
pa=Increase;
int a=10;
int b=(*pa)(a);
printf("%d\n",b);
printWelcome();
return 0;
}
指针函数
指针函数只指针作为函数的形参或者返回值。
指针和字符串
字符串可以粗略理解为字符变量的数组,其应用同数组与指针。