1、指针简介
指针(Pointer)与底层硬件联系紧密,使用指针可操作数据的地址,实现数据的间接访问。
我们可以定义一个变量,通过引用变量来直接访问数据;也可以通过指针获取数据的地址,从而间接访问数据。
(1)计算机存储机制
计算机存储机制分为大端模式和小端模式,不过目前大多数计算机采用小端模式的存储方式。
首先,计算机存储机制是将内存划分为一个线性区域,小端模式即将数据的低位存放在对应内存的首地址,然后依次往后存放。例如,存放int型变量数据时,数据0x12345678大小为4个字节,在内存中,该变量的低位即‘78’存放于内存地址都首地址。在内存中,各数据本身是按照小端模式存储的,但是对于数组数据,各数据之间是按顺序进行存储的。
(2)指针的定义
指针即指针变量,用于存放其他数据单元(变量/数组/结构体/函数等)的首地址,从根本上看,指针是一个值为内存地址的变量(或数据对象)。若指针存放了某个数据单元的首地址,则这个指针指向了这个数据单元,若指针存放的值是0,则这个指针为空指针。
指针变量所占的字节大小是由系统位宽所决定的。
(3)指针的操作
当'*'号单独位于指针变量前面,前面没有变量类型,那么'*'号的含义是取内容,即取出该指针指向的内存中的数据。
指针的也加减运算是以该指针所指向的数据类型(例如int、char、short等)的位宽为单位的。例如,某指向int型数据的指针p,在经过p++操作后,指针变量p的地址加4。
指针的加减运算通常运用在数组中,单独的指针加减运算会使指向指向一个未被定义的地址,会造成程序错误。
2、数组与指针
数组是一些相同数据类型的变量组成的集合,其数组名即为指向该数据类型的指针。数组的定义等效于申请内存、定义指针和初始化
#include<stdio.h>
#include<stdlib.h>
int main(void)
{
//char a[] = {0x33,0x34,0x35};
int *a;
a=malloc(3*4);
*a = 0x33;
*(a+1) = 0x34;
*(a+2) = 0x35;
printf("%x\n",*a);
printf("%x\n",*(a+1));
printf("%x\n",*(a+2));
return 0;
}
3、注意事项
在对指针取内容之前,一定要确保指针指在了合法的位置,否则将会导致程序出现不可预测的错误;
同级指针之间才能互相赋值,跨级赋值将会导致编译器报错或警告。
4、指针的应用
(1)传递参数
1)使用指针传递大容量的参数,主函数和子函数使用的是同一套数据,避免了参数传递过程中的数据复制,提高了运行效率,减少了内存占用;
直接调用参数时,经历了一个复制的过程,首先变量a申请了四个字节地址,然后形参又额外申请了四个字节地址,在子函数中,将实参复制到形参中。此方法隔离了形参与实参,子函数中对形参的修改不会影响到实参的内容。
面对大容量数据时,使用直接传参的方法工作量较大,且c语言没有提供传递数组的方法。利用指针进行间接传参,就可以减少工作量,并且减少内存的应用。首先,在子函数中,形参定义为指针变量,该指针变量在内存中申请了8个字节的地址,在函数将实参传给形参,此时,形参指针变量的地址中存放的是数组的首地址,array[i]将以此访问数组中的各元素。但是使用指针传参,子函数可以对主函数中的变量进行修改,为了避免子函数对主函数变量的影响,可以在子函数形参的变量类型前加“const”进行修饰,关键字const用来定义常量,如果一个变量被const修饰,那么它的值就不能再被改变。
int a[] = {0x66,0x77,0x88};
也可以定义一个全局变量,子函数和主函数都使用全局变量作为参数,那个老师说“我只能说,你要是非要这么做也行”,但是这样做不利于函数的封装。
2)使用指针传递输出参数,利用主函数和子函数使用同一套数据的特性,实现数据的返回,可实现多返回值函数的设计。
C语言的函数只能返回一个值,如果想返回多个值 应该怎么做呢?
通过在子函数中更改主函数的值,实现多返回值函数的设计。
#include<stdio.h>
void FindMax(int *max,int *cont,const int *arry)
{
int i;
*max = arry[0];
*cont = 1;
for(i=0;i<6;i++)
{
if(*max<arry[i+1])
{
*max = arry[i+1];
*cont = 1;
}
else if(*max == arry[i+1])
{
(*cont)++;
}
}
}
int main(void)
{
int a[] = {1,2,3,4,5,3};
int Max;
int Cont;
FindMax(&Max,&Cont,a);
printf("%d\n",Max);
printf("%d\n",Cont);
return 0;
}
(2)传递返回值
将模块内的公有部分返回,让主函数持有模块的“句柄”,便于程序对指定对象的操作、
#include<stdio.h>
int Time[] = {23,45,57};//该数组申明不能放在子函数中,因为在子函数中它只是一个临时变量,子函数结束后该变量会消失
/*int *GetTime(void)
{
int Time[] = {23,45,57};
return Time;
}*/ // 例如该用法是错误的
int *GetTime(void) //看作整体,返回值是int*;数组名就是地址,整个函数中始终传递的都是Time数组的地址。
{
return Time;
}
int main(void)
{
int *pt;
pt = GetTime();
printf("%d\n",pt[0]);
printf("%d\n",pt[1]);
printf("%d\n",pt[2]);
return 0;
}
利用C语言读写文件
#include<stdio.h>
int main(void)
{
char a;
FILE *f = NULL;
f = fopen("D:\\text.txt","r");//
a=fgetc(f);
fputc('A',f);
fputs("\nHelloWorld!",f);
fclose(f);
printf("a=%c\n",a);
return 0;
}
(3) 直接访问物理地址下的数据
访问硬件指定内存下的数据,如设备ID号等;
将复杂格式的数据转换为字节,方便通信和存储。
1)利用指针传递数组
#include<stdio.h>
unsigned char AirData[20];
void SendData(unsigned char *data,unsigned char count)
{
unsigned char i;
for(i=0;i<count;i++)
{
AirData[i]=data[i];
}
}
int main(void)
{
unsigned char DataSend[]={0x12,0x34,0x52,0x64};
SendData(DataSend,4);
printf("%x\n",AirData[0]);
printf("%x\n",AirData[1]);
printf("%x\n",AirData[2]);
printf("%x\n",AirData[3]);
return 0;
}
2)利用指针发送浮点型变量
#include<stdio.h>
unsigned char AirData[20];
unsigned char DataReceive[4];
float a=12.345;
void SendData(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 *p;
p = (unsigned char *)&a;
SendData(p,4);
float *fp;
ReceiveData(DataReceive,4);
fp = (float *)DataReceive;
printf("%f\n",*fp);
return 0;
}
完结!
第一篇,可能只有自己懂,希望可以坚持下来,再接再厉!