C语言——高阶指针

简介: 指针的指针

数组

   inta[10];

int a[10]; //a代表的是数组首元素的地址a+1 步长 4&a+1 步长 40 ;&a代表整个数组的地址;指针也是一种数据类型,指针的步长就看他指向内存空间的类型; 所以内存空间加1就是地址偏移40

#define  _CRT_SECURE_NO_WARNINGS

#include <stdlib.h>

#include <string.h>

#include <stdio.h>

voidmain(void)

{

   inta[10]; //a代表的是数组首元素的地址  &a代表整个数组的地址  a+1 4  &a+1步长 40 .

   //

   {

       //定义一个数组类型

       typedefint(myTypeArray)[10]; //

       myTypeArraymyArray;

       myArray[0] =10;

       myArray[1] =11;

       printf("%d \n", myArray[0]);

       printf("%d \n", myArray[1]);

   }

   {

       //定义一个指针数组类型

       typedefint(*PTypeArray)[10];  //int *p

       PTypeArraymyPArray; //sizeof(int) *10

       myPArray=&a;

       //int b = 10;

       //int *p = NULL;

       //p = &b;

       (*myPArray)[0] =20;

       printf("a[0]: %d \n", a[0]);

   }

   {

       //定义一个指向 数组类型的指针 数组类的指针

       int(*MyPointer)[10]; //变量 告诉C编译器 给我分配内存

       MyPointer=&a;

       (*MyPointer)[0] =40;

       printf("a[0]: %d \n", a[0]);

   }

   printf("hello...\n");

   system("pause");

   return;

}

1、为什么需要指针?

指针解决了一些编程中基本的问题。

第一,指针的使用使得不同区域的代码可以轻易的共享内存数据。当然你也可以通过数据的复制达到相同的效果,但是这样往往效率不太好,因为诸如结构体等大型数据,占用的字节数多,复制很消耗性能。但使用指针就可以很好的避免这个问题,因为任何类型的指针占用的字节数都是一样的(根据平台不同,有4字节或者8字节或者其他可能)。

第二,指针使得一些复杂的链接性的数据结构的构建成为可能,比如链表,链式二叉树等等。

第三,有些操作必须使用指针。如操作申请的堆内存。还有:C语言中的一切函数调用中,值传递都是“按值传递”的,如果我们要在函数中修改被传递过来的对象,就必须通过这个对象的指针来完成。

2、指针是什么?

指针是程序数据在内存中的地址,而指针变量是用来保存这些地址的变量。由于内存中的每一个字节都有一个唯一的编号,因此,在程序中使用的变量,常量,甚至数函数等数据,当他们被载入到内存中后,都有自己唯一的一个编号,这个编号就是这个数据的地址。指针就是这样形成的。

charch='a';

int  num=97;

我们可以大致画出变量ch和num在内存模型中的存储。(假设 char占1个字节,int占4字节)

3、指针与变量的关系

用来保存 指针 的变量,就是指针变量。如果指针变量p1保存了变量 num的地址,则就说:p1指向了变量num,也可以说p1指向了num所在的内存块 ,这种指向关系,在图中一般用 箭头表示。

intmain(void)

{

   intnum=97;

   int*p1  =&num;

   char*p2= (char*)(&num);

   printf("%d\n",*p1);    //输出  97

   putchar(*p2);          //输出  a

   return0;

}

指针的值:很好理解,如上面的num 变量 ,其地址的值就是0028FF40 ,因此 p1的值就是0028FF40。数据的地址用于在内存中定位和标识这个数据,因为任何2个内存不重叠的不同数据的地址都是不同的。

指针的类型:指针的类型决定了这个指针指向的内存的字节数并如何解释这些字节信息。一般指针变量的类型要和它指向的数据的类型匹配。

由于num的地址是0028FF40,因此p1 和 p2的值都是0028FF40

*p1 : 将从地址0028FF40 开始解析,因为p1是int类型指针,int占4字节,因此向后连续取4个字节,并将这4个字节的二进制数据解析为一个整数 97。

*p2 : 将从地址0028FF40 开始解析,因为p2是char类型指针,char占1字节,因此向后连续取1个字节,并将这1个字节的二进制数据解析为一个字符,即’a’。

同样的地址,因为指针的类型不同,对它指向的内存的解释就不同,得到的就是不同的数据。

4、指针类型

5、数组指针和指针数组

“数组指针”和“指针数组”,只要在名词中间加上“的”字,就知道中心了——

数组的指针:是一个指针,什么样的指针呢?指向数组的指针。

指针的数组:是一个数组,什么样的数组呢?装着指针的数组。

然后,需要明确一个优先级顺序:()>[]>*,所以:

(*p)[n]:根据优先级,先看括号内,则p是一个指针,这个指针指向一个一维数组,数组长度为n,这是“数组的指针”,即数组指针;

*p[n]:根据优先级,先看[],则p是一个数组,再结合,这个数组的元素是指针类型,共n个元素,这是“指针的数组”,即指针数组。

根据上面两个分析,可以看出,p是什么,则词组的中心词就是什么,即数组“指针”和指针“数组”。

int*p1[5]//指针的数组

int (*p2)[5]//数组的指针

首先,对于语句int* p1[5],因为“[]”的优先级要比 * 要高,所以 p1 先与“[]”结合,构成一个数组的定义,数组名为 p1,而“int”修饰的是数组的内容,即数组的每个元素。也就是说,该数组包含 5 个指向 int 类型数据的指针,如图 1 所示,因此,它是一个指针数组。其次,对于语句int ( p2)[5],“()”的优先级比“[]”高,“*”号和 p2 构成一个指针的定义,指针变量名为 p2,而 int 修饰的是数组的内容,即数组的每个元素。也就是说,p2 是一个指针,它指向一个包含 5 个 int 类型数据的数组,如图 2 所示。很显然,它是一个数组指针,数组在这里并没有名字,是个匿名数组。由此可见,对指针数组来说,首先它是一个数组,数组的元素都是指针,也就是说该数组存储的是指针,数组占多少个字节由数组本身决定;而对数组指针来说,首先它是一个指针,它指向一个数组,也就是说它是指向数组的指针,在 32 位系统下永远占 4 字节,至于它指向的数组占多少字节,这个不能够确定,要看具体情况。

5.1、 数组指针 (*p)[n]

#include "stdafx.h"

 

 

intmain()

{

   //一维数组

   inta[5] = { 1, 2, 3, 4, 5 };

   //步长为5的数组指针,即数组里有5个元素

   int (*p)[5];

   //把数组a的地址赋给p,则p为数组a的地址,则*p表示数组a本身

   p=&a;

 

   //%p输出地址, %d输出十进制

   //\n回车

   //在C中,在几乎所有使用数组的表达式中,数组名的值是个指针常量,也就是数组第一个元素的地址,它的类型取决于数组元素的类型。

   printf("%p\n", a); //输出数组名,一般用数组的首元素地址来标识一个数组,则输出数组首元素地址

   printf("%p\n", p); //根据上面,p为数组a的地址,输出数组a的地址

   printf("%p\n", *p); //*p表示数组a本身,一般用数组的首元素地址来标识一个数组

   printf("%p\n", &a[0]); //a[0]的地址

   printf("%p\n", &a[1]); //a[1]的地址

   printf("%p\n", p[0]); //数组首元素的地址

   printf("%d\n", **p); //*p为数组a本身,即为数组a首元素地址,则*(*p)为值,当*p为数组首元素地址时,**p表示首元素的值1

   printf("%d\n", *p[0]); //根据优先级,p[0] 表示首元素地址,则*p[0]表示首元素本身,即首元素的值1

   printf("%d\n", *p[1]); //为一个绝对值很大的负数,不表示a[1]...表示什么我还不知道

 

   

 

   //将二维数组赋给指针

   intb[3][4];

   int(*pp)[4]; //定义一个数组指针,指向含4个元素的一维数组

   pp=b; //将该二维数组的首地址赋给pp,也就是b[0]或&b[0],二维数组中pp=b和pp=&b[0]是等价的

   pp++; //pp=pp+1,该语句执行过后pp的指向从行b[0][]变为了行b[1][],pp=&b[1]

 

   intk;

   scanf_s("%d", &k);

 

   return0;

}

 

根据上面二维数组可以得出,数组指针也称指向一维数组的指针,所以数组指针也称行指针。

5.2、指针数组 *p[n]

指针数组:是数组——装着指针的数组。

看下面的例子进行理解:

数组指针:是指针——指向数组的指针。

看下面的例子进行理解

#include "stdafx.h"

 

 

intmain()

{

   inta=1;

   intb=2;

   int*p[2];

   p[0] =&a;

   p[1] =&b;

 

   printf("%p\n", p[0]); //a的地址

   printf("%p\n", &a); //a的地址

   printf("%p\n", p[1]); //b的地址

   printf("%p\n", &b); //b的地址

   printf("%d\n", *p[0]); //p[0]表示a的地址,则*p[0]表示a的值

   printf("%d\n", *p[1]); //p[1]表示b的地址,则*p[1]表示b的值

 

 

   //将二维数组赋给指针数组

   int*pp[3]; //一个一维数组内存放着三个指针变量,分别是p[0]、p[1]、p[2],所以要分别赋值

   intc[3][4];

   for (inti=0; i<3; i++)

       pp[i] =c[i];

 

   intk;

   scanf_s("%d", &k);

 

   return0;

}

 

最后,从上文来看:

数组指针是一个指针变量,占有内存中一个指针的存储空间;

指针数组是多个指针变量,以数组的形式存储在内存中,占有多个指针的存储空间。

6、举例1

typedefstruct

{

   unsignedshortad[3][5][100];

} photo_sample_data_stru;            

photo_sample_data_stru  *Photo_Sample_Read_Base_Point,Photo_Sample_Read_data ;

unsignedshortdata_0_0_x[100],data_0_1_x[100];

unsignedshort   *Photo_Sample_Point0,*Photo_Sample_Point1;

uint32_taddr;

voiddata_init(void)

{

   uint16_tPhoto_Sample_Line,photo_sample_time;

   for(Photo_Sample_Line=0;Photo_Sample_Line<5;Photo_Sample_Line++)

   {

       for(photo_sample_time=0;photo_sample_time<100;photo_sample_time++)

       {

           Photo_Sample_Read_Base_Point->ad[0][Photo_Sample_Line][photo_sample_time]=photo_sample_time; //第一路;

           Photo_Sample_Read_Base_Point->ad[1][Photo_Sample_Line][photo_sample_time]=photo_sample_time; //第二路;

           Photo_Sample_Read_Base_Point->ad[2][Photo_Sample_Line][photo_sample_time]=photo_sample_time; //第三路;

       }

   }

}

voidmain()

{

   intnum=2021;

   printf("num addr = 0x%x\r\n",(int)&num);

   printf("num  = %d\r\n",num);

   printf("\r\n");

   

   Photo_Sample_Read_Base_Point=&Photo_Sample_Read_data;

   data_init();

   printf("Photo_Sample_Read_Base_Point addr = 0x%x\r\n",(int)(Photo_Sample_Read_Base_Point));

   printf("Photo_Sample_Read_Base_Point  = %d\r\n",(int)(Photo_Sample_Read_Base_Point->ad[0][0][0]));

   printf("\r\n");

   

   

   Photo_Sample_Point0=Photo_Sample_Read_Base_Point->ad[0][0];

   printf("Photo_Sample_Point0 addr = 0x%x\r\n",(int)Photo_Sample_Point0);

   for( i=0;i<10;i++)

   {

       data_0_0_x[i]=Photo_Sample_Point0[i];

       printf("Photo_Sample_Point0[%d] = 0x%x \r\n",i,Photo_Sample_Point0[i]);

   }

   printf("\r\n");

   

   Photo_Sample_Point1=Photo_Sample_Read_Base_Point->ad[0][1];

   printf("Photo_Sample_Point1 addr = 0x%x\r\n",(int)Photo_Sample_Point1);

   for( i=0;i<10;i++)

   {

       printf("Photo_Sample_Point1[%d] = 0x%x\r\n",i,Photo_Sample_Point1[i]);

       data_0_1_x[i]=Photo_Sample_Point1[i];

   }

   printf("\r\n");

}

先看输出结果:

分析1:Photo_Sample_Read_Base_Point是指向Photo_Sample_Read_data的指针。Photo_Sample_Read_Base_Point地址是0x1000034C

分析2:Photo_Sample_Point0,Photo_Sample_Point1这两个指针,以及指针给数组赋值。Photo_Sample_Point0 addr = 0x1000034cPhoto_Sample_Point1 addr = 0x10000414

Photo_Sample_Point0[i] 实际为Photo_Sample_Read_data.ad[0][0][i]

Photo_Sample_Point1[i] 实际为Photo_Sample_Read_data.ad[0][1][i]

7、举例2:

typedef struct

{

unsigned char photo_ad_data[20][2];    

} photo_sample_stru;                      

photo_sample_stru         *photo_data_point[6];        

static photo_sample_stru   pmt_Data_Load[6];

static unsigned int Data_Loc_o;

static unsigned int Data_Loc_p;

void main()

{

for (Data_Loc_o=0;Data_Loc_o<6;Data_Loc_o++)  //  清空数据缓冲区  

{

photo_data_point[Data_Loc_o] =  &pmt_Data_Load[Data_Loc_o];

  //pmt_first =  photo_data_point[0];

for (Data_Loc_p=0;Data_Loc_p<20;Data_Loc_p++)                    

{

  photo_data_point[Data_Loc_o]->photo_ad_data[Data_Loc_p][0] = 0;

  photo_data_point[Data_Loc_o]->photo_ad_data[Data_Loc_p][1] = 0;

   

}

}

}

结果如下:分析: 我们下面看出,photo_sample_stru *photo_data_point[6]; 是指向photo_sample_stru 类型的 指针数组(即指针的数组)。利用指针把pmt_Data_Load里面数据清零。

8、举例3

#define MAX_SEG         100

#define MAX_CYCLE       10 //最大循环个数的宏定义


typedef  uint8_t UCHAR ;

typedef struct

{

  short           RunNow;                       //运行标志(正常开机,断电后重启)

  short           RunEnd;                       //运行结束标志

  short           FolderName[12];                //目录名

  short           FileName[12];                  //文件名

  short           SaveYear;                      //年(文件的保存时间)

  short           SaveMonth;                     //月(文件的保存时间)

  short           SaveDate;                      //日(文件的保存时间)

  short           SaveHour;                      //时(文件的保存时间)

  short           SaveMinute;                    //分(文件的保存时间)

  short           SaveSecond;                    //秒(文件的保存时间)

  short           HotlidSet;                      //热盖设置

  short           VolumeSet;                     //样本容量

  short           ModeSet;                       //运行的模式

  short           TestModeSet;                   //模拟模式

  short           FirstPause;                    //首节暂停

  short           EditSeg;                       //节点个数

  short           EditCycle;                     //循环个数

  short           TempSet[MAX_SEG];              //节点温度

  short           HourSet[MAX_SEG];              //节点时间---时

  short           MinuteSet[MAX_SEG];            //节点时间---分钟

  short           SecondSet[MAX_SEG];            //节点时间---秒

  short           SpeedSet[MAX_SEG];             //速度

  short           TempCYCSet[MAX_SEG];           //每个循环节点提升温度

  short           MinuteCYCSet[MAX_SEG];         //分――每个循环节点增加时间

  short           SecondCYCSet[MAX_SEG];         //秒――每个循环节点增加时间

  short           GradSet[MAX_SEG];              //梯度

  short           CycleSet[MAX_CYCLE];           //每个循环次数

  short           CycleBeginSet[MAX_CYCLE];      //循环开始节点

  short           CycleEndSet[MAX_CYCLE];        //循环终止节点

  short           GradExtSet[6][MAX_SEG];        //独立运行信息

  //short           AloneSet[MAX_SEG];             //分区设置

  short           TubeVolSet;                    //试管体积

  short           TubeTypeSet;                   //试管类型

  short           Fill[580];             //用于填充,结构总长度2Kwords

}StructFile;


typedef struct

{

   unsigned long    File_Add;//地址

   short   Seg;

   short   cyc_inside;

   short   cyc_outside;

   short   outside_flag;

   short   OverShootFlag;

   short   OverTime;

   short   Now_Temp;

}StructCalTime;


StructCalTime *Cal_Point;

StructCalTime Cal_TimeA;


void main()

{

uint8_t *u8_point;

u8_point = (uint8_t*)(&Cal_TimeA.File_Add);

for(uint16_t i=1;i<sizeof(StructCalTime);i++)

*(u8_point+i+4)=i;//赋值



Cal_TimeA.File_Add=(unsigned long)(&Cal_TimeA);//取地址

Cal_RunFile = *((StructFile*)(Cal_TimeA.File_Add));//重指向


// Cal_RunFile = *((StructFile*)((unsigned long)(&Cal_TimeA)));//直接赋值

}

目录
相关文章
|
1天前
|
C语言
C语言指针——练习
C语言指针——练习
|
1天前
|
存储 C语言 C++
C语言进阶——指针
C语言进阶——指针
|
2天前
|
存储 编译器 C语言
C语言内功修炼--指针详讲(进阶)
C语言内功修炼--指针详讲(进阶)
|
2天前
|
存储 C语言
C语言内功修炼---指针详讲(初阶)
C语言内功修炼---指针详讲(初阶)
|
2天前
|
存储 编译器 程序员
从C语言到C++④(第二章_类和对象_上篇)->类->封装->this指针(下)
从C语言到C++④(第二章_类和对象_上篇)->类->封装->this指针
4 0
|
2天前
|
存储 编译器 C语言
从C语言到C++④(第二章_类和对象_上篇)->类->封装->this指针(中)
从C语言到C++④(第二章_类和对象_上篇)->类->封装->this指针
5 0
|
3天前
|
存储 编译器 C语言
C语言初阶⑦(指针初阶)知识点+笔试题(上)
C语言初阶⑦(指针初阶)知识点+笔试题
10 0
|
2天前
|
Java C语言 C++
从C语言到C++④(第二章_类和对象_上篇)->类->封装->this指针(上)
从C语言到C++④(第二章_类和对象_上篇)->类->封装->this指针
5 0

热门文章

最新文章