指针简介
指针(Pointer)是C语言的一个重要知识点,指针与底层硬件联系紧密,使用指针可操作数据的地址,实现数据的间接访问。
计算机存储机制
右边这个图就是我们实际的内存的一个框图,那么在我们计算机的内存里呢,我们通常把内存分配成这样一个线性的空间,就内存分配为一个线性的区域,每个区域都是以字节为单位的,比如0x78就是一个字节数据,每个字节都会对应一个地址。
比如我们定义了一个整型的int型变量a,给它赋值为0x12345678,在计算机系统里,int代表的是一个4个字节的数据,所以一个int类型的变量就跨越了4个地址,这种把小端(78)放在内存的第一位的存储模式叫小端存储模式,同样也有把大端(12)放在第一位的这种大端存储模式。short代表2给字节的数据,也是同样的小端存储模式。 而我们定义一个数组,它的每一个数据都是按顺序来存储的,char代表一个1个字节的数据,用char定义了一个数组,如图所示它的每一个数据都是按顺序来存储的,而当我们用short定义数组,数组中的一个元素占用2个字节,每个元素都是按照顺序来存储的,而元素内部的数据则分开从小端储存。
定义指针
指针即指针变量,用于存放其他数据单元(变量/数组/结构体/函数等)的首地址。若指针存放了某个数据单元的首地址,则这个指针指向了这个数据单元,若指针存放的值是0,则这个指针为空指针。
指针占用的位宽等于系统的位宽,为什么呢?
首先补充,位(bit)和字节(byte)都是电脑的计量单位。字节是电脑中最基本的单位,它用于衡量电脑的存储容量,一个存储单元能存储一个字节的内容,即8个二进制位的内容。一个8位宽的设备,一次可以处理一个字节。
因为指针需要存储足够的信息来定位内存中的数据。例如,在32位的系统中,指针的位宽也是32位,它可以存储2^32个可能的地址,这足以覆盖整个32位系统的内存空间。
通过sizeof我们也可以看出指针变量和普通变量所占字节的差别。
指针的操作
*p是以p存储的地址为引导取该地址存储数据的内容
P++指针向下移动一个数据宽度,此数据宽度指的是指针指向变量a的数据宽度。
数组与指针
数组是一些相同数据类型的变量组成的集合,其数组名即为指向该数据类型的指针。数组的定义等效于申请内存,定义指针和初始化
注意事项
指针的应用
传递参数
使用指针传递大容量的参数,主函数和子函数使用的是同一套数据,避免了参数传递过程中的数据复制,提高了运行效率,减少了内存占用
#include<stdio.h>
int FindMax(int*array,int Count)//这里定义了一个局部指针变量array,申请了8个字节的内存,这是形参,储存了一个指向数组a首地址的指针,通过指针间接访问了数组a的内容
{
int i;
int max=array[0];
for(i=1;i<Count;i++)
{
if(array[i]>max)
{
max=array[i];
}
}
return max;
}
int main(void)
{
int a[]={13,2,3,5,4,30};//数组传参时只能间接访问
int Max;
Max=FindMax(a,6);//这是实参
return 0;
}
有时为了防止子函数篡改主函数内容,常在形参定义前加上const
这个过程内部经历了什么?
在主函数中int a[]={13,2,3,5,4,30}先用6*4个字节储存了数组的数据,又用4个字节定义了一个Max,此时到了Max=FindMax(a,6)将数组a的首地址传入子函数,首先,子函数中定义了一个局部变量array,这个变量是int*,所以他会再申请8字节的内存,形参定义了一个指针变量,并且把a值赋给形参这个初始值,array的初始值就是a,当走到max=array[0],首先array[0]是取出array当作指针下的第一个数据,我们并不把a取出来,而是把a当作地址去找这个地址下的内容,这里的array[0]和主函数中a[0]是同一个数组,同一个地址。
数组名本身就是指针常量,他的值是该数组首元素的地址,而在子函数中定义的局部指针变量,其实就是把实参赋给形参都是指针,只是创建的array指针变量变成了指向数组a首地址的指针,仅此而已。
使用指针传递输出参数,利用主函数和子函数使用同一套数据的特性,实现数据的返回,实现多返回值函数的设计
#include<stdio.h>
void FindMaxandCount(int*max,int*count,int*array,int length)
{
int i;
*max=array[0];
*count=1;
for(i=1;i<length;i++)
{
if(array[i]>*max)
{
*max=array[i];
*count=1;
}
else if(array[i]==*max)
{
(*count)++;
}
}
}
int main()
{
int a[]={13,2,3,5,4,30};
int Max;
int Count;
FindMaxandCount(&Max,&Count,a,sizeof(a)/sizeof(a[0]));
printf("Max=%d Count=%d",Max,Count);
return 0;
}
将地址赋给指针,让指针指向此地址,在用*取内容实现间接访问
传递返回值
将模块内的公有部分返回,让主函数持有模块的“句柄”,便于程序对指定对象的操作
#include<stdio.h>
int main(void)
{
char a;
char s[10];
FILE *f=fopen("F:\\test.txt","r");//f就是句柄
a=fgetc(f);
fgets(s,15,f);
fclose(f);
printf("%c",a);
printf(s);
return 0;
}
直接访问物理地址下的数据
访问硬件指定内存下的数据,如设备ID号等
#include<stdio.h>
#include"LCD1602.h"
void main()
{
unsigned char code*p;
LCD_Init();
LCD_ShowString(1,1,"HelloWorld");
p=(unsigned char code*)0xFF9;
LCD_ShowHexNum(2,1,*P,2);
LCD_ShowHexNum(2,3,*(p+1),2);
LCD_ShowHexNum(2,5,*(p+2),2);
LCD_ShowHexNum(2,7,*(P+3),2);
LCD_ShowHexNum(2,9,*(P+4),2);
LCD_ShowHexNum(2,11,*(P+5),2);
LCD_ShowHexNum(2,13,*(P+6),2);
while(1)
{
}
}
将复杂格式的数据转为字节,方便通信与存储
#include<stdio.h>
/******************************************************************/
unsigned char AirData[20];
void SendData(const unsigned char*data,unsigned char count)
{
unsigned char i;
for(i=0;i<count;i++)
{
AirData[i]=data[i];
}
}
void ReceiveData(unsigned char*data,unsigned char count)
{
unsigned char i;
for(i=0;i<count;i++)
{
data[i]=AirData[i];
}
}
/**********************************************************************/
int main()
{
unsigned char i;
unsigned char DataSend[]={0x12,0x34,0x56,0x78};
float num=12.345;
unsigned char*p;
p=(unsigned char *)&num
SendData(p,4);
/**************************************/
printf("\nAirData=");
for(i=0;i<20;i++)
{
printf(%x,AirData[i]);
}
/***********************************************/
unsigned char DataReceive[4];
float *fp;
ReceiveData(DataReceive,4);
fp=(float *)DataReceive;
printf("\nnum=%f",*fp);
//printf("\nDataReceive=");
//for(i=0;i<4;i++)
// {
// printf("%x ",DataReceive[i]);
// }
return 0;
}