目录
指针简介
1.指针是C语言的一个重要知识点,其使用灵活、功能强大,是C语言的灵魂
2.指针与底层硬件联系紧密,使用指针可以操作数据的地址,实现数据的间接访问
计算机的存储机制
一般都为小端存储:即低地址存放低字节数据
大端存储:低地址存放高字节数据
定义指针
指针即指针变量,用于存放其他类型数据(变量,数组,结构体,函数)的首地址,若指针存放了数据的首地址那么就是指针指向了这个数据单元,如果指针存放的是0,那么我们称它为空指针。
定义一个指针变量:
指针的大小跟系统位数相关。2x8,4x8,8x8,原因是位数越大可以使用的内存就越大,内存越大地址就越大,所以我们64位系统的指针大小需要用8字节来存放。
注意下图中p为int*类型,p1为int类型
指针的操作
星号在C语言中有三种含义:
第一种,变量变量 表示相乘
第二种, 数据类型* 表示定义一个该数据类型的指针变量
第三种,*变量 表示取这个变量的内容,即先找该变量的地址,然后再取地址里的值,即 指针通过地址间接访问数据
对指针变量的++ --操作是指对该指针移动该指针类型的位置,例如int *p;p++;该指针指向地址位置会移动int类型大小的位置,即移动四个字节。
数组与指针
数组是一些相同数据类型的变量组成的集合,其数组名即为指向该数据类型的指针。数组的定义等效于申请内存、定义内存和初始化。
数组即指针
数组取下标是指针取内容的另一种形态。
打印结果:
注意事项:
1.在对指针取内容之前一定要确保指针指向了合法的位置,否则将出现无法预知的错误,所以说数组越界等同于指针指向了非法的位置。
2.同级指针才能相互赋值,否则会报错或警告。
指针的应用
传递参数
1.使用指针传递大容量的参数,主函数和子函数使用的是同一套数据,避免了参数传递过程中的数据复制,提高了运行效率,减少了内存占用。
例如:
#include <stdio.h>
void fun(int param) {
printf("%x\n", param);
}
int main() {
int a = 0x66;
fun(a);
return 0;
}
结果为:
它在内存中的调用方式:值传递实际上是在内存中新开辟一块相同大小区域复制过去进行操作,隔离了主函数和子函数的数据,保护数据不被子函数更改。举例:学霸(主函数)借给学渣(子函数)抄作业,学霸说你可以抄作业(读取、复制等操作)但是不能改(赋别的值)我的作业。如下图所示:
但是,值传递存在一个弊端就是如果数据特别大,那么我们使用值传递会特别的占用内存(原因是会在内存中复制一份相同的区域和值),既费时又费力。所以我们引出了地址传递:
例如:我们有一个子函数功能是找出主函数中一个数组内最大的数。
#include <stdio.h>
int FindMax(int *array, int count) {
int i;
int max = array[0];
for (i = 1; i < count; i++) {
if (array[i] > max) {
max = array[i];
}
}
return max;
}
int main() {
int a[] = {13, 2, 3, 4, 5, 3};
int Max;
Max = FindMax(a, 6);
printf("Max=%d\n", Max);
return 0;
}
输出结果为:
在内存中表现为:当子函数发生地址传递时,会在内存中创建一个指针变量来存放实参的地址,我们在子函数中对这个地址进行操作就相当于对同一个数组进行操作。如下图所示:
注意:C语言给予了一个方法可以避免子函数对传递的地址中的值进行修改,即使用const关键字来进行常量化,如下:
int FindMax(const int *array, int count) {
int i;
int max = array[0];
for (i = 1; i < count; i++) {
if (array[i] > max) {
max = array[i];
}
}
return max;
}
2.使用指针传递输出参数,利用主函数和子函数使用同一套数据的特性,实现数据的返回,可实现多返回值函数的设计。
在子函数中我们的return关键字只能返回一个参数的值,所以我们可以使用指针来传递参数。
例如:实现程序,返回数组中最大的数并返回它出现的次数。
#include <stdio.h>
int FindMaxAndCount(int *max, int *count, const int *array, int lenth) {
int i;
*max = array[0];
*count = 1;
for (i = 1; i < lenth; i++) {
if (array[i] > *max) {
*max = array[i];
*count = 1;
} else if (array[i] == *max) {
(*count)++;
}
}
}
int main()
{
int a[] = {13, 2, 30, 4, 5, 30};
int Max;
int Count;
FindMaxAndCount(&Max, &Count, a, 6);
printf("Max=%d\n", Max);
printf("Count=%d\n", Count);
return 0;
}
结果为:
传递返回值
将模块内的公有部分返回,让主函数持有模块的“句柄”,便于程序对指定对象的操作。
例如:假设我们有一个时钟Time,我们让指针作为返回值来持有Time的句柄。
#include <stdio.h>
/****************/
int Time[] = {23, 59, 55};
int *GetTime(void) {
return Time;
}
/****************/
int main(void) {
int *pt;
pt = GetTime();
printf("pt[0]=%d\n", pt[0]);
printf("pt[1]=%d\n", pt[1]);
printf("pt[2]=%d\n", pt[2]);
return 0;
}
结果为:
注意此处不要将一个子函数局部变量的地址返回出去,会出现不可预知的错误。
直接访问物理地址下的数据
1.访问硬件指定内存下的数据,如设备ID号等
以51单片机为例,每个单片机具有全球唯一ID,程序存储器的最后七位字节的值为全球唯一ID,单片机内部RAM的F1H~F7H单元的内容也为全球唯一ID。
#include <REGX52.H>
#include "LCD1602.h"
int main(void)
{
unsigned char *p;
LCD_Init();
LCD_ShowString(1,1,"HelloWorld!");
p=(unsigned char *)0xF1;
LCD_ShowHexNum(2,1,*p,2);//此处等同于LCD_ShowHexNum(2,1,*((unsigned char *)0xF1),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){
}
return 0;
}
结果为:
2.将复杂格式的数据转换为字节,方便通信与存储
例如:假设有一个无线模块SendDATA ,我们来模拟发送和接收数据,只能以一个字节一个字节的发送和接收。我们有一个float类型的调试参数,我们可以通过使用指针来让float一个字节一个字节的发送接收。float占4字节。
#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};
float num=12.345;
unsigned char *p;
p=(unsigned char *)#
SendData(p, 4);//直接把p当作数组发送出去
//模拟将数据发送出去
printf("\nAirData=");
for (i = 0; i < 20; i++) {
printf("%x ", AirData[i]);
}
//模拟接收数据
unsigned char DataReceive[4];
float *fp;
ReceiveData(DataReceive, 4);
fp=(float *)DataReceive;//直接强制转换引用num的数
printf("\nnum=%f",*fp);
//printf("\nDataReceive=");
//for (i = 0; i < 4; i++) {
// printf("%x ", DataReceive[i]);
//}
return 0;
}
结果为:
像这种数据完全可以当作数组来进行字节处理,读入和读出。