c/c++语言开发共享指针和数组的掌握以及内存的管理

前言:对于c语言的学习,我记得最初就有人说最难莫过于指针,当初我们的学习呢,又是先学习了数组,然后接触的就是指针,指针和数组被很多书籍放在一块进行教学,指针是c/c++中的精华,而

前言:对于c语言的学习,我记得最初就有人说最难莫过于指针,当初我们的学习呢,又是先学习了数组,然后接触的就是指针,指针和数组被很多书籍放在一块进行教学,指针是c/c++中的精华,而难点就在于对指针和数组的掌握以及内存的管理,所以在这里,我们要对这个问题,进入一些研讨:

1 .什么是指针

在探究之前我们要弄清楚指针的概念,

指针做什么

  int *p;

学过c的人都应该知道,这定义了一个指针,在这里p到底是个什么东西呢?
其实,p也就是一个变量,而对于变量,就可以理解为一个左值,会开辟一块内存空间,然后在这块空间中储存内容。

理解指针

  int *p;  int a=10;  p=&a;

在这几句话,也不难理解,正因为像咱们在上面说的。开辟好了p这块空间,所以咱们现在就要在这块空间中,存放内容,而这个内容,在这里&a,也就是a的地址了。
如果你还不能理解,那么我用图来给你解释:
c/c++语言开发共享指针和数组的掌握以及内存的管理
我想这样你应该会理解指针的作用,即存放地址。对于上面那个例子,p这块存放地址的空间我们叫做指针变量,p中存放的内存地址处的内存我们就称为p所指向的内存,通常我们也会说p指向了a。

“*”的作用

对于上面的理解,我们留下一个问题,就是“* ”,在定义一个指针时,我们需要加“ *”,而在使用时,我们也会加“ *”。
比如下面这段程序:

  int *p;  int a=10;  p=&a;  *p=20;

“* ”我们就可以理解为,当一个基本的数据类型加上它时,这样就会构成一个数据类型的指针,这个指针我们要记住,大小永远是4个字节。而当我们写成 p 这样的形式时,我们在这里,称呼 *为解引用。就好像你现在手中会有一把钥匙,可以去通过指针来改变它所指向的那块空间的内容。

2.什么是数组

数组概念

数组,我们通常会理解为一组类型相同元素的集合。
比如:

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

学过c的人应该都知道,这意思就是定义了一个数组,其中包含了5个int类型的元素。
c/c++语言开发共享指针和数组的掌握以及内存的管理
当定义数组时,编译器会根据元素的个数和元素类型确定大小开辟空间,比如,上面的arr,我们就会分配20字节大小个空间。在这里,这块空间是不能再次改变的。

乱乱的&arr和&arr[0]
对于数组,我们一定要清楚&arr和&arr[0]这两个概念,&arr,这里说的就是对整个数组取地址。而&arr[0]在这里所说的也就是对数组的首个元素的地址。&arr和&arr[0]这两个值是相等的,虽然他们相等,但是意义是不一样的。这个的区别就好比陕西省政府与西安市政府,他们的所在地都一样,都是在西安,但是意义不一样。

数组名的左值右值
左值和右值,简单说就是可以出现在“=”右边的就是右值,可以出现在“=”左边的就是左值。左值,他要是一块可以被修改的空间,右值,所说的值一块空间中所带的内容。

接下来我们要探讨一个问题了,数组名可以做左值吗?右值呢?

当arr作为右值时,我们要清楚,arr就是代表首元素的地址,在这里的arr就相当于arr[0]。
而当arr作为左值呢,在这我们就要根据左值右值的概念考虑了,左值必须是一块可以修改的空间,而这里,arr待变的是arr数组首元素的地址,地址不能被修改,所以,arr是不能作为左值的。

3.指针数组之间的联系

在我当初学习指针和数组时,总会觉得他们之间有种关系,并且好像彼此之间是一回事,在这里,我要告诉你,指针和数组之间是没有任何关系的!!!

很多人容易把这两个概念弄混淆很大的原因就是应为在访问时,指针和数组他们总是可以达到一样的目的。
例如:

  char *p="abcdef";

指针形式和下标形式访问指针

对于上面的的指针变量p,根据前面咱们分析的可以知道,p是一块4个字节大小的空间,里面存放了首字符的地址。
比如我们需要访问’c’:

指针形式:*(p+2);这个意思就是先取出p的地址,然后给它按照字符型进行偏移2次,然后把所指向的地址进行解引用,访问其中所存放的内容。

数组下标形式:p[2];这里编译器会把下标形式解析成为指针形式的操作。这里的意思就是先取出p中存储的地址值,再加上其中括号中2个元素的偏移,得出新地址,然后再取出内容。

这两种方法最后使得我们所得到的结果都是一样的,两种方式本质上都是以指针为形式进行访问。

指针形式和下边形式访问数组

  char arr[]="123456";

在这里我们需要访问’3’

指针形式:*(arr+2);这里就是得到arr的首字符地址,然后偏移两个字符,然后得到所指向地址,然后解引用,得到其中的内容。

数组下标形式:arr[2];这里编译器会把下标形式解析成为指针形式的操作。首先得到arr首元素的地址,然后偏移两个字符,得到新的地址,再取出内容。

经过了上述两个过程的分析,我们应该都知道指针和数组就是两个不一样的东西,他们只是可以”以指针的形式“和”以数组下标形式“来进行访问。

区分a与&a
首先我们通过牛客网的一道题来进行分析:

  #include  int main()  {      int a[5]={1,2,3,4,5};      int *ptr=(int *)(&a+1);      printf("%d,%d,*(a+1),*(ptr-1)");      return 0;  }

在这里我们要分析:
&a+1:这个说的就是取了a的地址,然后向后偏移一个int,也就是到了这个数组最后一个位置的下一个地址。
(int )(&a+1):这个意思是将上述的地址强制转化为int 类型的地址。
所以执行完第二句话后,这里的ptr中存放的就是a这个数组的下一个数组的首地址。
c/c++语言开发共享指针和数组的掌握以及内存的管理
*(a+1):这个的意思参考上面咱们分析的,所以也就可以得到这个的结果就是数组第二个元素。
所以,最终输出的结果也就是2和5;
这里我们依然强调一下&a说的是数组的地址,对他+1指的是偏移整个数组。
a这里代表的是a[0],首元素的地址。

4.指针数组和数组指针

指针数组:这里强调的是数组,然后数组中的元素都是指针,数组的大小是通过数组本身来决定。
数组指针:这里强调的是指针,就是说它还是一个指针变量,只不过他指向数组,至于他所指向的数组多大,这个就不知道了。

  int *p1[5];  int (*p2)[5];

在这里我总结下来,指针数组和数组指针主要的区别主要是看优先级结合。”()”的优先级是最高的,下来是”[ ]”,再下来是”*“;

int* p1[5]:我们可以理解为,先于[10]结合,就是一个数组p1,然后是和”int*“结合,就表示数组中的内容全部都是int*类型的,所以这就是一个指针数组,数组中包含的是10个指向int类型数据的指针。所以他就是一个指针数组。

int (*p2)[5]:这里首先优先级最高的是”()“,所以先结合”()“,就是强调了这是个指针,然后和”[10]”进行结合,所以p2就是一个指向一个包含10个int大小的数组的指针。所以他就是一个数组指针。

c/c++语言开发共享指针和数组的掌握以及内存的管理

5.函数指针,函数指针数组和函数指针数组指针

通过刚才对于指针数组和数组指针的理解,接下来我们来探讨一下函数数组和函数指针。

函数指针


什么是函数指针?

函数指针:顾名思义,这里强调的依然是指针,函数指针也就是说函数的指针,他是一个指向函数的指针。

    char *fun3(char *p1,char *p2);

这个我想大家再熟悉不过了,函数为fun3,两个char* 类型的形参 p1,p2,函数返回值char *。

  char * *fun2(char *p1,char *p2);

这里函数为fun2,两个char 类型的形参 p1,p2,函数返回值char **

  char *(*fun1)(char * p1,char *p2);

在这,我们就可以用优先级来进行判断,它先于()结合,可以知道fun1是一个指针,然后它指向的是一个函数。而这个函数呢,就是一个有两个char* 类型的形参 p1,p2,函数返回值char *的函数。

函数指针的使用
例:

  #include  int max(int x,int y)  {      return (x>y? x:y);  }  int main()  {      int (*ptr)(int, int);      int a, b, c;      ptr = max;      scanf("%d%d", &a, &b);      c = (*ptr)(a,b);      printf("a=%d, b=%d, max=%d", a, b, c);      return 0;  }

在这一段程序中,ptr是指向函数的指针变量,所以可把函数max()赋给ptr作为ptr的值,即把max()的入口地址赋给ptr,以后就可以用ptr来调用该函数。

接下来看下一个例子:

  void fun()  {      printf("call fun!n");  }  int main()  {      void (*p)();      *(int *)&p=(int)fun;      (*p)();      return 0;  }

分析:
void (*p)():这一句说的就是我在这里定义了一个指针变量p,p用来指向函数,函数的返回值和参数都是void类型的。
*(int )&p=(int)fun:在这,就是说将fun函数的地址强制转换成int类型,然后赋给了p指针变量。
(*p)():表示对函数fun进行调用。
所以我们要清楚,函数指针还是一个指针,里面存放的是函数的首地址,然后我们通过首地址来调用函数。

  delphi">(*(void (*)())0)();

这个是什么呢?

对于这个,其实咱们也可以通过分析进行解决。
void (*)():这个是一个void类型的一个函数指针,这个函数返回值为空,并且参数也为空。
(void (*)())0:这个就是将0强制转换成这个函数指针的类型,也就是说一个函数保存在首地址为0的一段区域。
(* ( void (*)())0):这是取得首地址为0的的内容,就是保存在首地址为0的的函数。
(* (void (*)())0)():这是最终对这个函数进行调用,因为函数的参数为空,所以调用时参数也是空的。

接下来我们再进行分析一个类似的:

  (*(char * *(*)(char **,char **))0)(char * *,char * *);

是不是看这有些晕呢,咱们继续一层一层进行分析。
char * * ( * )(char * * ,char * * ):这个所说的就是一个函数指针,这个函数指针指向返回值为char * * * ,参数为两个char * *的参数。
(char * * ( * )(char * * ,char * * ))0:这是将0强制转换为这个函数指针的类型,也就是说一个函数保存在首地址为0的一段区域。
( * (char * * ( * )(char * * ,char * * ))0 ):这是取得首地址为0的的内容,就是保存在首地址为0的的函数。
( * (char * * ( * )(char * * ,char * * ))0)(char * * ,char * * ):这是最终对这个函数进行调用因为函数的参数为(char * * ,char * * ),所以调用也是给(char * * ,char * * )参数。

函数指针数组


函数指针数组:我们也可以参考前面的分析方法,它是一个数组,这个数组中的元素就是我们前面所提到的函数指针。

  char *(* p[3])(char *p);

上面这个就是一个函数指针数组,首先它是有3个元素的一个数组,里面存放的是指向函数的指针,这些指针指向一些返回值类型为指向字符的指针。
在这里的关键你要知道这个是数组,数组中的元素是函数指针。

函数指针数组指针


到这里,我想许多人看见以后肯定非常晕了,一层套一层的,其实,没什么复杂的,万变不离其宗,咱们还是一层一层来进行分析。
首先给出一个函数指针数组指针

  char *(*(*p)[3])(char *p);

在这里,首先按照优先级从个最里面看,最里面的(*p)这是一个指针。
(*( *p)[3])这个说了这个指针是指向含有三个元素的数组的指针。
char*( *(*p)[3])(char *p)这个说了这个数组中存放的元素是3个函数指针,这些指针指向一些返回值为指向字符的指针。然后这个函数的参数是指向字符的指针。

本文来自网络收集,不代表计算机技术网立场,如涉及侵权请联系管理员删除。

ctvol管理联系方式QQ:251552304

本文章地址:https://www.ctvol.com/c-cdevelopment/603568.html

(0)
上一篇 2021年5月11日
下一篇 2021年5月11日

精彩推荐