一、指针简介
指针(Pointer)是C语言的一个重要知识点,其使用灵活、功能强大,是C语言的灵魂 指针与底层硬件联系紧密,使用指针可操作数据的地址,实现数据的间接访问
1.1计算机存储机制
位,字节
字节 byte 位 bit
位等价于比特(bit),同一个单位,最小的单位
1位=1比特;1字节=8位(两个16进制的数)在十六进制表示中,每个十六进制数字可以表示4个比特 1字=16位;1字=2字节
int a = 0x12345678; /*int类型占4个字节*/
short b = 0x5A6B;/*short类型2个字节*/
char c[ ] = {0x33, 0x34, 0x35};/*char类型数据一个字节*/
补充:
小端分配(Little Endian)是一种数据存储方式,它将多字节数据类型的最低有效字节存储在内存的最低地址处,而最高有效字节存储在最高地址处。这种存储方式的名称源于计算机处理信息的方式与书写语言的差异。在西方书写中,我们习惯从左到右进行书写,而在计算机内部处理数据时,数据通常是从右到左进行处理的。因此,在小端分配中,数据的低位部分在存储器中的地址是比高位部分的地址更小的。
例如,对于一个 32 位整数变量,其值为 0x12345678,该变量在小端分配存储方式下的存储方式如下:
地址 | 0x4000 | 0x4001 | 0x4002 | 0x4003 |
---|---|---|---|---|
数据内容 | 0x78 | 0x56 | 0x34 | 0x12 |
可以看到,在小端分配中,该变量的最低有效字节 0x78 存储在内存地址 0x4000 处,而最高有效字节 0x12 则存储在地址 0x4003 处。(高位高放,低位低放)右边为低,左边为高
注意:
变量定义的时候不分配地址,初始化的时候才给分配地址;
数组必须连续存储,若数组内部元素都跨字节,那么元素间顺序(大端)存,元素内按小端存
1.2定义指针
指针即指针变量,用于存放其他数据单元(变量/数组/结构体/函数等)的首地址。若指针存放了某个数据单元的首地址,则这个指针指向了这个数据单元,若指针存放的值是0,则这个指针为空指针
定义一个指针变量:
64位系统最多有 2^64个字节 所以变量最小宽度占8字节 64个位
#include<stdio.h>
int mian(void)
{
int a;
int *p,p1; //p是一个指向int类型的指针变量;p1是一个整型
printf("%d\n",sizeof(a)); //int 4 char 1
printf("%d\n",sizeof(p)); //int 8 char 8
//数据类型的大小和指针类型大小没有关系,指针类型大小与系统位数有关。所以int a是4位,int *p是8位
}
1.3指针的操作
#include <stdio.h>
int main(void)
{
int a = 0x66;
int *p;
p = &a;
// 也可以写成 int *p = &a;
printf("%x\n",a); //66
printf("%x\n",p); //62fe47
printf("%x\n",*p); //66
p++;
printf("%x\n",p); //62fe48(加了一个数据宽度,int为4字节)
return 0;
}
1.4数组与指针
例:
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int *a;
a = malloc(3*4); <-----> // char a[] = {0x33, 0x34, 0x35};
*a = 0x33;
*(a+1) = 0x34;
*(a+2) = 0x35;
// char *p;
// p = a;
printf("a[0]=%x\n",a[0]); //33 <--> printf("*p=%x\n",*p);
printf("a[1]=%x\n",a[1]); //34 printf("*(p+1)=%x\n",*p+1);右边说明数组名就是指针
printf("a[2]=%x\n",a[2]); //35 printf("*(p+2)=%x\n",*p+2);
printf("*a=%x\n",*a); //33
printf("*(a+1)=%x\n",*(a+1)); //34
printf("*(a+2)=%x\n",*(a+2)); //35
return 0;
}
二、指针应用
2.1传递参数
值传递(安全,但是占用内存)
#include <stdio.h>
void fun(int param)
{
param=0x88;
printf("%x\n",param); //88
}
int main(void)
{
int a = 0x66;
fun(a);
printf("%x\n",a);//66 形参不影响实参
return 0;
}
指针传递(传递大容量的参数)此处子函数修改值会影响主函数,用const
#include <stdio.h>
int FindMax(const int *array,int Count) //用const,只读
{
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[] = {1,2,3,5,4,3};
int Max;
Max = FindMax(a,6);
printf("Max=%d\n",Max);
return 0;
}
指针传递:(指针传递输出参数,可实现多返回值函数的设计)
#include <stdio.h>
void FindMaxAndCount(int *max,int *count,const int *array,int length) //用const,只读
{
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(void)
{
int a[] = {1,2,5,5,4,3};
int Max;
int Count;
FindMaxAndCount(&Max,&Count,a,6);
printf("Max=%d\n",Max); //5
printf("Count=%d\n",Count); //2
return 0;
}
2.2 传递返回值
#include <stdio.h>
/****************/
int Time[]={23,59,55};
int *GetTime(void)//返回值是int*类型,即返回值就是整型指针类型
{
return Time;
}
/****************/
int main(void)
{
int *pt;//定义一个指针变量
pt = GetTime(); //GetTime的返回值是Time的首地址,用pt指针接一下
printf("pt[0]=%d\n",pt[0]);
printf("pt[1]=%d\n",pt[1]);
printf("pt[2]=%d\n",pt[2]);
return 0;
}
综合:
#include <stdio.h>
int main(void)
{
//用指针传递参数和体现模块的“句柄”
// FILE *f = fopen("F:\\test.txt","w");
// fputc('A',f); //在f写个字符A
// fputs('HelloWorld!',f); //在f写个HelloWorld!
char a;
char s[10];
FILE *f = fopen("F:\\test.txt","r");
a = fgetc(f); //普通的值传递
fgets(s,15,f); //返回的输出参数
fclose(f);
printf("%c",a);
peintf(s);
return 0;
}
2.3单片机中应用
1)访问硬件指定内存下的数据,如设备ID号等
#include <REGX52.H>
#include "LCD1602.h"
void main()
{
// unsigned char *p; //定义一个指针变量
unsigned char code *p; //定义一个指针变量(加code,访问程序存储空间)
LCD_Init();
LCD_ShowString(1,1,"Hello");
//直接读取ID号存放的RAM区
// p = (unsighed char *)0xF1; //强制转换,跨级赋值
// LCD_ShowHexNum(2,1,*p,2);//*p是取内容
// 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);
//ID号存放在程序的地址读取,加code访问程序存储空间
p = (unsighne char code *)0x1FF9; //强制转换
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)
{
}
}
2)将复杂格式的数据转换为字节,方便通信与存储
#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(void)
{
/*****************************/
unsigned char i;
unsigned char DataSend[]={0x12,0x34,0x56,0x78};
SendData(DataSend,4);
printf("\nAirData=");
for(i=0;i<20;i++)
{
printf("%x ",AirData[i]);
}
/*****************************/小数
float num = 12.345;
unsigned char *p;
p = (unsigned char *)#
SendData(p,4)
unsigned char DataReceive[4];
float *fp;
ReceiveData(DataReceive,4);
fp = (float *)DataReceive;//强制类型转换
printf("\nnum=%f",*fp);
/*****************************/
return 0;
}
补充:结构体指针
结构体指针是指一个指向结构体的指针,它可以用于访问和修改结构体中的成员变量。在 C 语言中,结构体指针的定义和初始化形式如下:
struct person {
char* name;
int age;
};
struct person p; // 定义结构体变量
struct person *pp; // 定义结构体指针变量
pp = &p; // 将结构体指针变量设置为结构体变量的地址
pp->name = "Tom"; // 使用 -> 运算符访问结构体指针成员变量
pp->age = 20;
其中,&p
表示取结构体变量 p
的地址,pp
表示指向结构体 person
的指针,pp->name
表示访问结构体指针成员变量 name
,pp->age
表示访问结构体指针成员变量 age
。
另外,在定义结构体指针时,也可以在指针类型前加上关键字 typedef
,以便更方便地使用结构体指针:
typedef struct {
char* name;
int age;
} person;
person p; // 定义结构体变量
person *pp = &p; // 定义结构体指针变量并初始化为结构体变量的地址
pp->name = "Tom"; // 访问结构体指针成员变量
pp->age = 20;
在这段代码中,person
是一个结构体类型别名,它等价于上面定义的 struct person
。定义结构体变量时可以直接使用别名 person
,定义结构体指针时也可以直接使用 person *
。