指针介绍:
- 指针也是个变量,或者说也是个数据类型。
- 指针用于存放其他数据单元(变量/数组/结构体/函数等)的首地址。比如int型变量a的地址装在指针变量p上,但p自己有自己的地址
- 若指针存放了某个数据单元的首地址,则这个指针指向了这个数据单元,若指针存放的值是0,则这个指针为空指针
- 指向:指针变量装哪个的地址,就指向哪一个空间
定义指针变量:
- *号:在定义指针变量时,仅仅代表一个标识符,比如其实char *是一体的,只是中间有个空格,所以看起来没有层次感,当然把*写前面也行,比如char* p;
- 但是值得注意的是当为char* p,p1时,或者char *p,p1时,p1其实为char型,而不是个指针变量。
- 不同系统环境,指针变量的字节x 取不同字节
- 16位系统:x=2字节
- 32位系统:x=4字节
- 64位系统:x=8字节
数据类型 | 指向该数据类型的指针 | ||
(unsigned) char | 1字节 | (unsigned) char * | x字节 |
(unsigned) short | 2字节 | (unsigned) short * | x字节 |
(unsigned) int | 4字节 | (unsigned) int * | x字节 |
(unsigned) long | 4字节 | (unsigned) long * | x字节 |
float | 4字节 | float * | x字节 |
double | 8字节 | double * | x字节 |
指针的使用:
操作方式 | 举例 | 解释 |
取地址 | p=&a; | 将数据a的首地址赋值给p |
取内容 | *p; | 取出指针指向的数据单元 |
加 | p++; | 使指针向下移动1个数据宽度 |
p=p+5; | 使指针向下移动5个数据宽度 | |
减 | p--; | 使指针向上移动1个数据宽度 |
p=p-5; | 使指针向上移动5个数据宽度 |
- *号:内存操作符(解引号运算符)
- *p 表示,这个指针指向一个变量,也就是p=a,对*p进行的操作等于对a操作
- 在定义代码 int *p 里,仅表示定义了一个指针变量名为p的标识符
- 在其他代码里,表示取内容,即取出指针指向的数据单元,比如指针p指向a的话,即a=2,p=&a,那么*p= *&a,*和&相互抵消,所以*p=a,*p=2
#include<stdio.h>
int main(void)
// 指针是地址型数据类型,仅此而已
{ //指针就是装指向空间地址的变量,比如a的地址装在指针变量p上,但p自己有自己的地址
//指向:指针变量装哪个的地址,就指向哪一个空间
int a = 12;
int b = 15;
int *p = &a; //指针的定义格式:int *p;
//指针初始化:int *p=&a,右边记得加&符号
//指针类型要跟指向空间的数据类型一样!
printf("%p,%p,%p\n", &p, p, &a);
//&p表示变量p的地址; p表示变量p空间里装的内容,也就是a的地址,p=&a,; &a表示变量a的地址
&与*互为逆运算, *p=*(&a)=a
p = &b; //指针赋值: 指针变量=&变量,这时不要加*号了
printf("%p,%p,%p", &p, p, &b);
return 0;
}
- p++:使指针向下移动1个数据宽度
- 数据宽度为多少?
- 答:以指向空间的变量类型字节大小为准,比如指向一个int型,那么一个数据宽度就是4字节
- 数据宽度为多少?
//验证p++的功能
#include<stdio.h>
int main()
{
int a = 12;
int* p;
p = &a;
printf("%d\n", a);
printf("%d\n",p);
printf("%d\n", *p);
p++;
printf("%d\n", p);
return 0;
}
数组与指针:
- 数组和指针其实是一个东西,只是表达形态不同
- 数组是一些相同数据类型的变量组成的集合,其数组名即为指向该数据类型的指针。
- 数组的定义等效于申请内存,定义指针,初始化这三个过程:
- 利用下标引用数组数据也等效于指针取内容
//验证数组和指针:利用下标引用数组数据也等效于指针取内容
#include<stdio.h>
int main()
{
int a[]={1,2,3};
int* p;
p = &a;
printf("a[0]=%d\n", a[0]);
printf("a[1]=%d\n", a[1]);
printf("a[2]=%d\n", a[2]);
printf("*a=%d\n", *a);
printf("*(a+1)=%d\n", *(a+1));
printf("*(a+2)=%d\n", *(a + 2));
return 0;
}
注意事项
- 在对指针取内容之前,一定要确保指针指在了合法的位置,否则将会导致程序出现不可预知的错误
- 同级指针之间才能相互赋值,跨级赋值将会导致编译器报错或警告,通过&和*可以把变量变为同级指针
指针在嵌入式中的应用
- 传递参数
- 使用指针传递大容量的参数,主函数和子函数使用的是同一套数据,避免了参数传递过程中的数据复制,提高了运行效率,减少了内存占用
//指针传递参数,例子:找出数组的最大值
#include<stdio.h>
int findmax(int* p, int nlength);
int main()
{
int a[6] = { 12,34,52,74,42,212 };
int max;
findmax(&a,6);
max= findmax(&a, 6);
printf("%d", max);
}
int findmax(int* p, int nlength)
{
int max;
int i;
max = p[0];
for (i = 1; i < nlength; i++)
{
if (max < p[i])
{
max = p[i];
}
}
return max;
}
-
- 缺点是原变量的值容易被改变,可以加个const修饰
- 使用指针传递输出参数,利用主函数和子函数使用同一套数据的特性,实现数据的返回,可实现多返回值函数的设计。不是return,而是通过指针修改主函数中的变量,以达到多个返回值的效果
//指针传递参数:实现多返回值
#include<stdio.h>
void Find_Max_Count(int *Max,int *Count,const int *array, int *nlength)
{
int i;
*Max=array[0]; //形参里的*Max,即实参里的&max,即*Max=*&max=max,所以最终为:max=array[0]
*Count = 1; //形参*Count=实参count=1
for (i = 1; i < nlength; i++)
{
if (*Max < array[i])
{
*Max = array[i];
*Count = 1;
}
else if (array[i]==*Max)
{
(*Count)++;
}
}
}
int main()
{
int a[6] = { 12,13,11,14,14,14 };
int max;
int count;
Find_Max_Count(&max, &count,a,6);
printf("max=%d\n", max);
printf("count=%d\n",count);
}
- 传递返回值
- 将模块内的公有部分返回,让主函数持有模块的“句柄”,便于程序对指定对象的操作
//指针:传递返回值,将模块内的公有部分返回,让主函数持有模块的“句柄”,便于程序对指定对象的操作
#include<stdio.h>
int Time[] = {23,11,12};//假设有个封装文件里,里面有个Time
int *Get_time(void) //返回值是一个int *型,即int型的指针
{
return Time;
}
void main()
{
int* p;
p = Get_time(); //p也指向了Time
printf("time[0]=%d\n", p[0]);
printf("time[1]=%d\n", p[1]);
printf("time[2]=%d\n", p[2]);
}
-
- 注意的是,不能返回局部变量的指针,比如Time得是全局变量,如果是某个子函数中的局部变量,而局部变量在函数结束之后就会被销毁,所以虽然能访问到那个地址,但其实也是没用的
- 直接访问物理地址下的数据(与单片机结合使用,比如获取某外设的ID)
- 访问硬件指定内存下的数据,如设备ID号等
- 将复杂格式的数据转换为字节,方便通信与存储