当前位置: 首页 > 科技观察

说说C语言中经常使用的指针和数组

时间:2023-03-23 01:16:32 科技观察

定义指针:C语言中某种数据类型数据存储的内存地址,例如:指向各种整数的指针或指向结构体的指针。数组:C语言数据类型相同的若干个元素连续存储的一种形式。数组在编译时就已经确定了,指针要到运行时才能真正确定。所以,阵法的这些身份(记忆)一旦确定,就不能轻易改变,它们(记忆)会伴随阵法一生。教鞭有很多选择,他一生可以选择不同的生活方式。例如,字符指针可以指向单个字符并同时表示多个字符。C语言中经常使用指针和数组。在极少数情况下,数组和指针是“通用的”,例如,数组的名称表示数组第一个数据的指针。以下代码:#includechararray[4]={1,2,3,4};intmain(void){char*p;诠释我=0;p=数组;for(;i<4;i++){printf("*array=%d\n",*p++);}return(0);}这里我们将数组名array作为数组中第一个数据的指针赋值给p。但是不能写成*array++。准确的说,数组名可以作为右值,不能作为左值(这里不解释左值和右值的概念)。数组名的值其实是一个指针常量,所以我想你可以理解为什么数组名不能作为左值。如果要用指针p访问数组后面2个数据,下面的写法是合法的。chardata;/*第一种写法*/p=array;data=p[2];/*第二种写法*/p=array;data=*(p+2);/*第三种写法*/p=数组+指针与二维数组先说二维数组。二维数组在概念上是二维的,有行有列,但内存中的所有数组元素都是连续排列的。它们之间没有“间隙”。以下面的二维数组a为例:inta[3][4]={{0,1,2,3},{4,5,6,7},{8,9,10,11}};从概念上讲,a的分布就像一个矩阵:0123.4567.891011。但是内存是连续的,没有这种“矩阵内存”,所以二维数组a分布是一块连续的内存。C语言允许将一个二维数组分解为多个一维数组进行处理。对于数组a,可以分解为三个一维数组,即a[0]、a[1]、a[2]。每个一维数组包含4个元素,例如a[0]包含a[0][0]、a[0][1]、a[0][2]、a[0][3]。那么如何理解指针的定义如下呢?整数(*p)[4];括号中的*表示p是一个指针,它指向一个数组,数组的类型是int[4],这正是a所包含的一维数组的类型。那么下面的定义有什么区别呢?整数*p[4];这里首先要说明一下*和[]的优先级,[]的优先级高于*,所以int*p[4];等价于int*(p[4]);。所以它是一个指针数组。这里很绕,我们继续:int(*p)[4];是一个数组指针,指向二维数组中每个一维数组的类型,指向一个数组。整数*p[4];是一个指针数组,它是一个数组,数组中的每一个数都是一个指向int类型的指针。指针数组和数组指针对于指针数组,大家已经很清楚了,就不详细解释了,先说说数组指针吧。看一个例子:#includeintmain(){inta[3][4]={{0,1,2,3},{4,5,6,7},{8,9,10,11}};整数(*p)[4];p=一个;printf("%d\n",sizeof(*(p+1)));return(0);}forarraypointerp如下:然后是printf("%d\n",sizeof(*(p+1)));的结果是16,如果要打印a[1][0]的值,代码如下:printf("%d\n",*(*(p+1)));如果要打印a[1][1]的值,代码如下:printf("%d\n",*(*(p+1)+1));这个代价是自己体会的,p是一个数组指针,它指向的是一个数组,所以对于获取它指向的值,也是*p,无论是指向数组还是值,都指向[0].要得到a[0][0],需要写成**p。对指针进行加(减)运算时,其向前(向后)步长与其指向的数据类型有关,p指向的数据类型为int[4],则p+1前进4×4=16wordssection,p-1后面有16个字节,正好是数组a中包含的每个一维数组的长度。也就是说,p+1会让指针指向二维数组的下一行,p-1会让指针指向数组的上一行。最后再看一下数组指针和指针数组。诠释*p1[4];是一个指针数组。整数(*p2)[4];是一个数组指针。“[]”的优先级高于“*”。对于指针数组,p1先与“[]”组合成一个数组定义,数组名为p1,int*修饰数组的内容,即数组的每个元素。那么它本质上就是一个数组,这个数组里面有4个指向int类型数据的指针。对于数组指针,“()”优先级高于“[]”,“*”和p2构成指针定义,指针变量名为p2,int修饰数组的内容,即每一个元素数组。这里的数组没有名字,它是一个匿名数组。那么它本质上就是一个指针,指向一个包含4个int类型数据的数组。既然我们已经深入介绍了指针数组和数组指针,那么让我们多谈谈。#includeintmain(){chara[5]={'A','B','C','D'};字符(*p3)[5]=&a;字符(*p4)[5]=一个;return0;}上面代码编译通过,报warning。告警如下:注意:不同的编译器编译结果可能不同。我的编译方法请参考《使用vscode编译C语言》。p3定义中“=”号两边的数据类型完全一致,而p4定义中“=”号两边的数据类型不一致。左边的类型是指向整个数组的指针,右边的数据类型是指向单个字符的指针。因此,上面的警告。但是由于&a和a的值相同,而且当变量是右值时,编译器只是取变量的值,所以运行起来没有问题。但是,编译器仍然会警告您不要这样使用它。再举个栗子:intvector[10];intmatrix[3][10];int*vp,*vm;vp=vector;vm=matrix;上面代码第五行是错误的,因为vm指向的是整型指针,但是matrix不是前向指针,它是指向整型数组的指针。以下是正确的写法:intmatrix[3][10];int(*vm)[10];vm=matrix;数组指针的应用上面说了这么多,可能大部分开发者都用不到。数组指针在很多情况下可以代替二维数组。一些程序员喜欢使用指针数组而不是多维数组。一个常见的用法是处理字符串。#includechar*Names[]={"Bill","Sam","Jim","Paul","Charles",0};voidmain(){char**nm=名称;while(*nm!=0)printf("%s\n",*nm++);}具体运行我就不解释了,运行结果如下:注意数组最后一个元素初始化为0,并重复while循环检查是否已到达数组末尾。值为零的指针通常用作循环数组的终止符。此零值指针称为空指针(NULL)。使用空指针作为结束符,在增删元素时不需要更改遍历数组的代码,因为此时数组仍然以空指针结束。操作写到这里想到一个“操作”,先检查一下下面的代码是否正确。p[-1]=0;初看这段代码,觉得很奇怪,甚至以为是错误。在日常的C语言开发中,基本看到下标是负数,但是想了想,没有书上提到过下标。可以是负的。看下面的代码:voidmain(){intdata[4]={0,1,2,3};诠释*p;p=数据+2;printf("p[-1]是%d\n",p[-1]);printf("*(p-1)is%d\n",*(p-1));}运行结果如下:不但可以编译通过,而且正确的输出结果是1。这说明C下标引用和间接访问表达式是相同的。当然,这种操作是不被鼓励的。代码需要可读性高,而不是这样的操作。这里只是演示一下下标引用和简短表达式之间的关系。