指针进阶之数组指针和指针数组

news2024/12/28 21:03:45

文章目录

  • 一、指针数组
    • 1.概念
    • 2.用法
      • (1)案例一
      • (2)案例二
  • 二、数组指针
    • 1.概念
      • (1)引子
      • (2)写法
      • (3)辨析
      • (4)总结
      • (5)案例
    • 2.&数组名VS数组名
  • 三、数组指针的使用
    • 1.案例
      • (1)案例一
        • 方法1
        • 方法2
        • 方法3
      • (2)案例二
        • 方法1
        • 方法2
        • 总结
      • (3)案例三
        • 方法1
        • 方法2
        • 方法3
    • 2.回顾总结

一、指针数组

在初识指针那一节,已经介绍了指针数组,这里再强调一下。

指针这一章,可以从初识指针开始看起。

1.概念

指针数组是数组,用来存放指针的。

比如:

    int arr[10]={0};	//整型数组,数组用来存放整型(int)
    char ch[5]={0};		//字符数组, 数组用来存放字符(char)
    int* parr[4];		//指针数组,数组用来存放整型指针(int*)
    char* pch[5];		//指针数组,数组用来存放字符指针(char*)

又比如:

int* arr1[10];	//整型指针的数组(arr1数组里面有10个元素,每个元素是int*类型)
char* arr2[4];	//一级字符指针的数组(arr2数组里面有4个元素,每个元素是char*类型)
char* *arr3[5];	//二级字符指针数组(arr3数组里面有5个元素,每个元素是char**类型)

2.用法

(1)案例一

指针数组有什么用呢?

既然指针数组是用来存放指针的数组,那么最能想到的用法,就是将一些变量的地址存放进一个数组里面。

比如,现在这里有这么多整型变量:

int a=10;
int b=20;
int c=30;
int d=40;

可以将它们的地址(地址的类型是int*)存放进整型指针数组里面:

int* arr[4]={&a,&b,&c,&d};	//该指针数组里面存放的是整型指针(int*)

F10调试:(打开监视,分别输入&a,&b,&c,&d)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dAkkHr5a-1672797885922)(D:\Typora图片\image-20230103094505642.png)]

同样可以输入arr:(可以看到arr数组里面存放的内容及地址)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lu5bPtxe-1672797885923)(D:\Typora图片\image-20230103094638428.png)]

以上我们可以看到,arr里面存放的确实是a,b,c,d的地址。

既然是地址,那么该地址解引用之后就可以得到原来存储的值。

用一个循环来输出:

int i=0;
for(i=0;i<4;i++){
    printf("%d ",*(arr[i]));
}

如下结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QnyNkr8I-1672797885923)(D:\Typora图片\clip_image048.png)]

案例一代码(供大家学习使用):

int main(){
    int a=10;
	int b=20;
	int c=30;
	int d=40;
    int* arr[4]={&a,&b,&c,&d};
    int i=0;
	for(i=0;i<4;i++){
   	 	printf("%d ",*(arr[i]));
	}
    return 0;
}

(2)案例二

上面的用法是最原始,最初级的用法。

下面再来看一个高级用法。

现在有三个整型数组:

int arr1[]={1,2,3,4,5};
int arr2[]={2,3,4,5,6};
int arr3[]={3,4,5,6,7};

📦 我想把arr1,arr2,arr3存起来。

这三个数组名,是首元素地址

arr1就是1的地址,arr2就是2的地址,arr3就是3的地址。(三个数组名分别是三个整型的地址)

既然三个数组名分别是三个整型地址,那么想要将这三个数组名存放起来,就需要一个整型指针数组

假设这个整型指针数组名为parr,它的类型就是int*

那么,就可以这样来写:

int* parr[]={arr1,arr2,arr3};

🤕 可以探讨一下它们的内存结构

parr数组里面,存了三个数组名(arr1,arr2,arr3),类型都是int*

通过三个数组名,可以分别找到该数组名对应的首元素地址。(比如通过数组名arr1,找到arr1数组首元素1的地址)

如下图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nHKAXcKb-1672797885924)(D:\Typora图片\clip_image050.jpg)]

有了这样一个指针数组,我们就能很好的将三个数组维护起来。

一个一维数组(parr),维护了三个一维数组(arr1,arr2,arr3)。


❓如何通过parr数组输出所有整型元素?

只要我们能够找到对应每个数组(arr1,arr2,arr3)首元素的地址,就能输出每个元素。

比如,我们能够拿到arr1数组首元素1的地址,就能输出12345。

①可以先通过一个for循环,拿到parr数组里面的内容,即arr1,arr2,arr3(数组名即首元素地址)。

int i=0;
for(i=0;i<3;i++){
    parr[i];	
    // parr[0]-->arr1首元素地址
    // parr[1]-->arr2首元素地址
    // parr[2]-->arr3首元素地址
}

②既然拿到了对应数组(arr1,arr2,arr3)首元素地址,从该地址往后输出,就可以输出每个数组的元素了。

比如,parr[0]拿到arr1首元素地址,那么parr[0]+0就是数组arr1第一个元素1的地址,解引用,就能输出这个数1。

parr[0]+1就是第二个元素2的地址,解引用,就能输出这个数2。

那么,利用循环,就可以挨个输出所有元素了。

int j=0;
for(j=0;j<5;j++){
    *(parr[i]+j);
    //*(parr[0]+0) --> 1
    //*(parr[0]+1) --> 2
}

③将上面两个循环整合起来,就是:

int i=0;
for(i=0;i<3;i++){	
    int j=0;
	for(j=0;j<5;j++){	
    	printf("%d ",*(parr[i]+j));
	}	
    printf("\n");	//换行
}

输出看一下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Et58PjES-1672797885925)(D:\Typora图片\clip_image051.png)]

总结:通过parr[i]找到存储的每个数组首元素地址,通过parr[i]+j找到每个元素的地址,解引用找到每个元素。

案例二所有代码:(供大家学习使用)

int main(){
    int arr1[]={1,2,3,4,5};
	int arr2[]={2,3,4,5,6};
	int arr3[]={3,4,5,6,7};
    int* parr[]={arr1,arr2,arr3};
    int i=0;
	for(i=0;i<3;i++){	
   		 int j=0;
	for(j=0;j<5;j++){	
    	printf("%d ",*(parr[i]+j));
	}	
    printf("\n");	
	}
    return 0;
}

二、数组指针

1.概念

数组指针是指针,用来存放数组的地址。

(1)引子

我们已经熟悉,

整型指针:int* pint,能够指向整型数据的指针。可以存放整型的地址。

浮点型指针:float* pf,能够指向浮点型数据的指针。可以存放浮点型的地址。

字符指针:char* pc,能够指向字符数据的指针。可以存放字符的地址。

数组指针应该是:能够指向数组的指针。可以存放数组的地址。

(2)写法

经过上面的分析,可以得出:

数组指针–-指向数组的指针 –- 用于存放数组的地址

int arr[10] = { 0 };
arr -- 首元素地址
&arr[0] -- 首元素地址
&arr -- 数组的地址

&arr是数组的地址。既然是数组的地址,就可以存放到数组指针中。


📦举例

将下面数组的地址(&arr)存起来。

int arr[10]={1,2,3,4,5,6,7,8,9,10};

既然要存放数组的地址,就要有一个数组指针

定义一个指针变量p,里面存放数组的地址。

🚗 那该如何来写这个数组指针变量呢?

①这样写行吗:

int* p=&arr; 

这里的p是一个整型指针,整型指针里面存放的是整型元素,不能存放一个数组的地址。这样表示不行!

②这样呢:

int* p[10]=&arr; 

这里的p是一个指针数组int*表示该数组里面存放的是整型指针类型的元素,p为数组名,[10]表示数组里面有10个元素。

为什么它是数组而不是指针?因为“[]”优先级比“*”高,p就是一个数组,里面存放指针。这样表示也不行!

我现在想表示的是一个指针!!!里面存放的是地址。

③那我们这样写:

int(*p)[10]=&arr;

既然第二种写法,“[]”的优先级比“*”高,导致p先与“[]”结合,成为了数组。

那我们可以用小括号,将*p包裹起来,让p先与*号结合,使p成为一个指针

  • P前面是*,说明p是一个指针(定义变量的时候,*不是解引用操作),指向什么呢?

除去*p不看,剩下的是“int[10]数组类型。

  • [10]说明p指向的是一个数组,数组里面有10个元素。

  • int说明数组里面存放的是整型元素。

所以p是一个指针,指向的是一个数组,数组里面有10个元素,每个元素是int类型。

④综上所述,下面的p变量就有能力存放arr数组的地址。

int arr[10]={1,2,3,4,5,6,7,8,9,10};	//arr数组里面有10个元素,每个元素是int类型
int(*p)[10]=&arr;	
//P为数组指针--> 是一个指针,用来存放数组的地址。指向的是一个数组,数组里面有10个元素,每个元素是int类型

(3)辨析

我们再来辨析一下:

①案例一

int* p1[10];  	

p1首先和[]结合,是一个数组。数组里面10个元素。每个元素是“int*”类型,p1是一个存放指针的数组。

②案例二

int(*p2)[10]; 	

p2首先和*结合,是一个指针变量。指针指向数组,数组里面有10个元素,每个元素是int类型。

(4)总结

🍰总结

(1)int(*p)[10]

p先和*结合,说明p是一个指针变量,然后指向的是一个大小为10个整型的数组。

所以p是一个指针,指向一个数组,叫数组指针。

(2)注意

[]的优先级高于*,所以必须加上()来保证p先和*结合。

(5)案例

再来举两个例子,巩固一下上边学的内容。

  • 案例一

这里我想将数组arr的地址存进数组指针pa中,这里pa的类型如何表示呢?

char* arr[5];

大家可以自己写一下试试。

正确写法如下:

char* arr[5];//arr数组里面有5个元素,每个元素是char*类型
char* (*pa)[5]=&arr;	//将数组地址(&arr)取出来,放进数组指针pa里面

解释:

  • pa是个指针变量名,pa前面的*表示它是一个指针。

  • [5]表示pa指向的数组是5个元素的。

  • char*表示pa指向数组的元素类型是char*。

  • arr这个数组有5个元素,每个元素类型是char*。pa有能力指向这个数组,5个元素,每个元素类型是char*


  • 案例二

这里我想将数组arr2的地址存进数组指针pa2中,这里pa2的类型如何表示呢?

int arr2[10]={0};

大家可以再去试试。

正确写法如下:

int arr2[10]={0};
int (*pa2)[10]=&arr2;

解释

  • 首先pa2是用来存放arr2的地址,所以pa2是一个指针。指针表示为:*pa2
  • arr2是一个数组,里面存放10个元素,每个元素是int类型。所以pa2指针也需要有10个int类型的空间来存放。即:int [10]

以后需要写数组指针的时候,千万别写错了。

2.&数组名VS数组名

这个部分之前说数组的时候讲解过,这里再拿出来讲解一下。

对于下面的数组:

int arr[10];

arr&arr分别是啥结果?

我们知道arr是数组名,数组名表示数组首元素的地址

&arr到底是啥?这里取出的是数组的地址

不妨先来看一下它们分别输出的地址,来看一段代码:

int main(){
    int arr[10]={0};
    printf("%p\n",arr);
    printf("%p\n",&arr);
    return 0;
}

输出结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-isLgjVmW-1672797885925)(D:\Typora图片\clip_image006-16727079857892.jpg)]

可见,数组名&数组名打印的地址是一样的。


难道两个是一样的吗?

我们将它们的地址分别加一,再看一段代码:

int main(){
    int arr[10]={0};
    printf("arr=%p\n",arr);
    printf("&arr=%p\n",&arr);
    printf("arr+1=%p\n",arr+1);
    printf("&arr+1=%p\n",&arr+1);
}

输出结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iv1wjByu-1672797885926)(D:\Typora图片\clip_image007-16727079857871.png)]


我们来算一算地址变化:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-usJjUgyr-1672797885926)(D:\Typora图片\image-20230103161743150.png)]

&数组名–>&数组名+1

跳过一个数组(10个元素,每个元素为整型,一个整型4个字节,一个数组就是10*4=40个字节)
在这里插入图片描述

数组名–> 数组名+1

跳过一个元素(一个元素是整型,一个整型4个字节)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cXzw1wPa-1672797885927)(D:\Typora图片\image-20230103162039282.png)]

根据上面的代码我们可以发现:

其实&arrarr,虽然值是一样的,但是意义是不一样!

🍰总结

实际上,&arr表示的是“一整个数组首元素的地址”,而不是数组单个首元素的地址。

&arr+1,跳过整个数组的大小。所以&arr+1相对于&arr的差值是40。

所以上面定义数组指针的时候,就可以将数组的地址(&arr)交给一个指针。

即:

int arr[10]={1,2,3,4,5,6,7,8,9,10};	
int (*p)[10]=&arr;	

三、数组指针的使用

那数组指针是怎么使用的呢?

1.案例

(1)案例一

看一段代码:

int main(){
    int arr[10]={1,2,3,4,5,6,7,8,9,0};
    return 0;
}

现在要将数组arr里面的内容全部打印出来。

如何打印呢?

方法1

 int arr[10]={1,2,3,4,5,6,7,8,9,0};
 int (*pa)[10]=&arr;	//将数组arr的地址赋值给数组指针变量pa

既然数组指针指向的是数组,那数组指针中存放的应该是数组的地址。

我们知道,pa是数组指针,指向arr数组,里面存的是数组arr的地址。

既然拥有了数组arr的地址,解引用就可以拿到这个数组。(就相当于拿到了一个数组的数组名)即:

*pa	//数组的地址解引用,就拿到了这个数组

咱们现在拿到了数组名*pa,要去找数组内每个元素,就可以按照正常数组元素的访问来拿到每个元素。即:

(*pa)[0];	//数组的第一个元素

再利用for循环,就可以输出数组的每个元素。

整体代码如下:

int main(){
    int arr[10]={1,2,3,4,5,6,7,8,9,0};
    int (*pa)[10]=&arr;	//将数组arr的地址赋值给数组指针变量pa
    int i=0;
    for(i=0;i<10;i++){
        printf("%d ",(*pa)[i]);
    }
    return 0;
}

输出看一下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-V9D8Ee1b-1672797885927)(D:\Typora图片\clip_image012-16727079857993.jpg)]

方法2

还有一种输出方法。

上面说了,*pa就等于找到了这个数组(*pa==arr)。

arr是数组名,数组名是首元素地址。

arr+i就是从arr数组首元素的位置向后移动i,指向下标为i的元素。

再次解引用,就是第i个元素了。即:

*(*pa+i)

循环,即可输出所有元素。

整体代码如下:

int main(){
    int arr[10]={1,2,3,4,5,6,7,8,9,0};
    int (*pa)[10]=&arr;	//将数组arr的地址赋值给数组指针变量pa
    int i=0;
    for(i=0;i<10;i++){
        printf("%d ",*(*pa+i));
    }
    return 0;
}

输出看一下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-F2JjSDPx-1672797885927)(D:\Typora图片\clip_image013-16727079858246.png)]

方法3

上面两种方法越写越别扭。

这种多简单:

int main(){
    int arr[10] = { 1,2,3,4,5,6,7,8,9,0 }; 
	int* p = arr;//arr为数组名,交给一个p指针。这个指针指向第一个元素。 
	int i = 0; 
	for (i = 0; i < 10; i++) { 
     	//p表示第一个元素的地址,p+i表示往后的元素的地址 
    	 //找到后面的元素,就解引用即可:*(p + i) 
     	printf("%d ", *(p + i)); 
	} 
    return 0;
}

输出看一下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-grx25EF8-1672797885928)(D:\Typora图片\clip_image015.jpg)]


🌵 可以看到,第三种方法,特别简单。

前面两种方法,将这个题目复杂化了。其实数组指针并不是这样使用的。

(2)案例二

❓ 所以数组指针究竟什么时候使用呢?

一般情况下,数组指针要在二维数组以上使用,才方便一些。

比如这里有一个二维数组:

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

想要把数组的元素打印输出。该怎么办呢?

方法1

定义一个输出函数print1,将arr数组传递过去:

print1(arr,3,5);	//将数组名,行数,列数传递过去

提一嘴,因为遍历数组,需要知道行和列,所以需要将数组的行数与列数传递过去。

在print1函数中,通过两个for循环,来遍历数组。即:

void print1(int arr[3][5], int x, int y) { 
   int i = 0; 
   int j = 0; 
   for (i = 0; i < x; i++) { 
     for (j = 0; j < y; j++) { 
     	printf("%d ", arr[i][j]); 
     } 
     printf("\n"); //换行
   } 
} 
  int main() { 
    int arr[3][5] = { {1,2,3,4,5},{2,3,4,5,6},{3,4,5,6,7} };//定义一个二维数组 
    //我想把这个二维数组打印一下 
    print1(arr,3,5);//封装一个函数,把数组名和行列传过去 
    return 0; 
} 

输出结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FZrmX2Ba-1672797885928)(D:\Typora图片\clip_image017.jpg)]

方法2

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

既然arr是数组名,那么arr就是首元素地址(除了两种特例)。

❓ 对于刚才写的二维数组来说,它的首元素是什么呢?

是1吗?不是的,这里的“1”是数组第一行第一个元素。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xJ7Buzsi-1672797885929)(D:\Typora图片\clip_image019.jpg)]

🚗 怎么讨论呢?

  • 当我们说,数组名是首元素地址。

如果是二维数组,首先得把二维数组想象成一维数组。

把arr想象成一维数组。

把第一行当成一个元素,第二行当成一个元素,第三行当成一个元素。每行只有一个元素。

arr[3][5]其实就有三个元素。

  • 又回到刚才说的,数组名就是首元素地址。

其实第一行就是它的第一个元素,第一行就是数组的第一个地址

第一行是一个一维数组,这个数组有5个元素,每个元素是Int类型。即:int[5]。

我们现在这个二维数组有三个元素(三行),每行由五个整型构成。


这样我们就有另外一种写法:

还是写一个输出函数:

print2(arr,3,5);

第一种方法,我们是把arr当作数组传递上去的,所以当时是用数组(int arr[3][5])接收的。

现在,我们将arr当作二维数组第一行的地址传递上去。

Print2函数传上去的第一个参数是第一行的地址(arr),第一行是一个一维数组,所以传上去的是一维数组的地址

一维数组的地址,应该放到“数组指针”里面去。

这个数组指针指向的不是二维数组,而是一个一维数组(第一行),这个一维数组,有五个元素,每个元素是整型。

那我们可以这样写:

int (*p)[5]

解释一下这行代码:

“*p”表示p为指针,“[5]”表示指针指向的是一个数组,5个元素,“int”表示每个元素是int类型。

此时p有能力指向二维数组第一行。

这样就将arr传递上去的第一行元素的地址接收了。

如下:

void print2(int (*p)[5],int x,int y){
    
}

因为这是一个指向数组的指针,指向的是数组。

当我们指向整型的时候,加一,跳过一个整型;当指向一个数组的时候,加一,会跳过一个数组。

P指向的数组(第一行)是5个元素,我们加一就会跳到第二行,就会指向第二行。

因为跳过5个整型元素,加一就会指向下一行了。每次加一,会跳过一行!

所以怎么写呢?

  • (p+i)表示跳过i行,就指向了下标为i的这一行(得到了第i行的地址),这时候我们解引用,就找到了这一行,即:
*(p+i)

这时候,我们就拿到了这一行的数组名arr[i],表示第i行的地址。

  • 要找到具体某个元素,就要在这个地址上再加一个j。找到下标为j的这个元素的地址。即:
  *(p+i)+j
  • 然后括起来,解引用,就找到了i行j列的元素了。即:
*(*(p+i)+j)

整合代码:

 void print2(int (*p)[5], int x, int y) { 
   	int i = 0; 
   	for (i = 0; i < x; i++) { 
     	int j = 0; 
    	for (j = 0; j < y; j++) { 
       		printf("%d ", *(*(p + i) + j));      
    	} 
    	printf("\n"); 
    } 
 } 
int main() { 
   	int arr[3][5] = { {1,2,3,4,5},{2,3,4,5,6},{3,4,5,6,7} };
   	print2(arr, 3, 5); 
    return 0; 
  } 

输出看一下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GlL6sIq5-1672797885929)(D:\Typora图片\clip_image021.jpg)]

总结

第一种写法,参数是数组的形式。

第二种写法,参数是指针的形式。

接下来,咱们再来解释一遍这个代码:

*(*(p+i)+j)

p+i:P表示指向了第一行(二维数组第一行,即第一个一维数组)的数组指针,加i表示跳过i行,即指向第i个一维数组(第i行)的数组指针。

*(p+i):解引用之后,就是第i个一维数组(第i行)的地址,也就是每一个一维数组的数组名。

*(p+i)+j:然后我们要拿到每一个一维数组(每一行)的里面的元素,先将刚才的一维数组的地址往后挪,加j,就是每i个一维数组的第j个元素的地址。

*(*(p+i)+j):然后解引用,就是每i个一维数组(每i行)的第j个元素。

还可以这样写:(*(p+i))[j] :每i个一维数组第j个元素。

📑注意

1.p+i就是指针在二维数组中行的移动,*(p+i)找到的是第i行这个一维数组的地址。

2.数组的地址和数组首元素地址的大小是一样的。

3.*(p+i)+j就是在这个一维数组里面移动,最后再取值。

4.可以这么认为,数组指针中放着的是数组名,解引用后得到数组首地址。

(3)案例三

方法1

我们再来看一个例子:

int main() { 
   int arr[10] = { 1,2,3,4,5,6,7,8,9,10 }; 
   int i = 0; 
   int* p = arr;//我们把数组名交给了一个整型指针,数组名是首元素地址,首元素是整型。 
   //arr就是整型数组的地址,放到整型指针中。 
   for (i = 0; i < 10; i++) { 
     	printf("%d ", *(p + i));//p为首元素地址,(p+i)是下标为i的元素的地址,解引用就找到这个元素了 
   } 
   return 0; 
} 

输出结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VpbCPcPN-1672797885929)(D:\Typora图片\clip_image023.jpg)]


方法2

再来想一下,

我们是把arr赋给了p,就说明arr和p是一回事。

数组名就是首元素地址,首元素地址arr放入p里面,p+iarr+i是一个道理。

那我们就可以把p+i改成arr+i

如下:

int main() { 
   int arr[10] = { 1,2,3,4,5,6,7,8,9,10 }; 
   int i = 0; 
   int* p = arr;
   for (i = 0; i < 10; i++) { 
     	printf("%d ", *(arr + i));
   } 
   return 0; 
} 

输出结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-raVQJ6co-1672797885929)(D:\Typora图片\clip_image025.jpg)]


方法3

那我们就可以再写一种。

数组名本来可以这样来写:arr[i] --> 找下标为i的元素

arr[i] == *(arr+i) == *(p+i) == p[i]

p[i]就是访问以p为起始地址,下标为i的元素。

arr[i]也是一样,以arr为起始地址,访问下标为i的元素。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MleJ7mjU-1672797885930)(D:\Typora图片\clip_image027-16727079858337.jpg)]


那我们案例二的代码中*(p+i)就可以写成p[i]

即:

*(*(p+i)+j)	==	*(p[i]+j)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5Ugb3eMT-1672797885930)(D:\Typora图片\clip_image031-16727079858339.jpg)]

那么又有一种写法:

*(*(p+i)+j)	==	*(p[i]+j)	==	(*(p+i))[j]	==	p[i][j]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hfPcXSRo-1672797885930)(D:\Typora图片\clip_image033-167270798583410.jpg)]

2.回顾总结

好了,现在我们来回顾一下刚才学的几个概念:

(1)例1

int arr[5];

arr是一个整型数组,数组有5个元素,每个元素是整型。

简而言之,就是:arr是由5个元素组成的整型数组

(2)例2

int* parr1[10];

parr1首先和[]结合,说明它是一个数组,数组10个元素,元素类型是int*。parr1是一个指针数组

(3)例3

int (* parr2)[10];

parr2首先和*结合,说明它是一个指针,指针指向的是一个数组,数组有10个元素,每个元素的类型是整型。parr2数组指针

(4)例4

int (* parr3[10])[5];

parr3首先和[]结合,说明它是一个数组,有10个元素。

对于一个数组来说,明确了它的数组名和它的元素个数,剩下的就是它的元素类型。

比如:int arr[10] -> arr为数组名,[10]表示它有10个元素,除了数组名和元素个数之外,剩下的int就是元素类型。

那么回到这个,剩下的int(* )[5]就是元素类型。和第三个题很像,是一个指针,指向数组的指针,即数组指针。

则:parr3是一个数组,该数组有10个元素,每一个元素是一个数组指针。该数组指针指向的数组有5个元素,每个元素是int类型。

我们来画一下它的大概图解:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EJLereDH-1672797885931)(D:\Typora图片\clip_image035-16727079858338.jpg)]

好啦,下次见~
请添加图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/140258.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ThreeJS—OrbitControls使其控制模型而不是场景

转载核心代码 项目场景&#xff1a; 来公司之前公司有一个地球组件&#xff0c;大概是张这个样子的⬇️&#xff0c;会转有飞线&#xff0c;有城市涟漪&#xff0c;很炫酷。可惜不是我做的。 一个大屏项目上需要额外增加一些需求 转动到某一城市&#xff0c;暂停转动&#…

K8S Pod 基本使用

K8S Pod 基本使用 Pod基本概念 Pods是在Kubernetes集群中创建和管理最小的部署单元&#xff0c;一个Pod内部可以运行一个或多个容器&#xff0c;多个容器之间具共享的存储和网络资源&#xff0c;共享运行上下文。Pod共享运行时上下文是通过linux 命名空间实现&#xff0c;不同…

项目管理工具dhtmlxGantt甘特图入门教程(五):甘特图实例特点

DHTMLX专注于JavaScript和HTML5 UI小部件和库&#xff0c;以帮助开发人员更快地构建功能丰富的、交互式的Web界面。 遵循现代网络开发的标准和做法&#xff0c;DHTMLX提供针对桌面和移动设备定制的顶级Web应用程序框架。 本文重点介绍使用Gantt Instance的主要功能。 DhtmlxG…

springBoot集成阿里云短信

第一步导入依赖 <dependencies><dependency><groupId>com.atguigu</groupId><artifactId>service-base</artifactId><version>0.0.1-SNAPSHOT</version></dependency><dependency><groupId>org.springfram…

【3】SCI易中期刊推荐——人工智能领域(中科院1区)

🚀🚀🚀NEW!!!SCI易中期刊推荐栏目来啦 ~ 📚🍀 SCI即《科学引文索引》(Science Citation Index, SCI),是1961年由美国科学信息研究所(Institute for Scientific Information, ISI)创办的文献检索工具,创始人是美国著名情报专家尤金加菲尔德(Eugene Garfield…

百货商场用户画像描绘与价值分析在线实习项目

通过实习可以让学生通过实践进行检验&#xff0c;它是衔接学生在校学习与步入社会两个阶段的重要桥梁。一个好的实习机会可以帮助学生积累工作经验&#xff0c;验证职业宣传同时提升综合素质。 在线实习项目重点是让学生巩固并拓展已学到的数据分析理论知识&#xff0c;对…

【无标题】测试开发 | TestNG 与 Junit 对比,测试框架如何选择?

TestNG 和 Junit 作为两大流行的测试框架&#xff0c;有哪些区别&#xff1f;各有哪些优缺点&#xff1f;该如何选择呢&#xff1f;这里简要总结下&#xff1a; 1. Junit 更适合隔离性比较强的单元测试&#xff1b; 2. TestNG 是比 Junit 涵盖功能更全面的测试框架&#xff0c…

【自学Java】Java for循环

Java for循环 Java for循环 在 Java 语言 中&#xff0c;关键字 for 用来操作已经知道了循环次数的循环操作&#xff0c;前面我们介绍过 while 和 do while 循环控制&#xff0c;他们一般情况下用于循环次数不知道的情况下。 Java for语句详解 语法 for(赋值初值 : 判断条…

第七章:使用Netlify零成本部署组件文档

第七章&#xff1a;使用Netlify无成本发布组件文档 为什么使用Netlify&#xff1f; 一开始一共有三个方案&#xff1a; 1、Github Page 2、Netlify 3、Vercel Github Page只支持一个repo发布一个网站&#xff0c;而我们的项目是一个mononrepo项目&#xff0c;后续可能还有其他…

数字转汉字支持负数和小数

private static String numToChinese(String str) {if(strnull)return null;boolean isPositivetrue;//判断是不是负数if(str.charAt(0)-)isPositivefalse;StringBuffer sbnew StringBuffer();if(!isPositive)sb.append("负");//整数部分和小数部分&#xff08;如果有…

2023年1月实时获取地图边界数据方法,省市区县多级联动【附区县乡镇街道geoJson文件下载】

首先&#xff0c;来看下效果图 在线体验地址&#xff1a;https://geojson.hxkj.vip&#xff0c;并提供实时geoJson数据文件下载 可下载的数据包含省级geojson行政边界数据、市级geojson行政边界数据、区/县级geojson行政边界数据、省市区县街道行政编码四级联动数据&#xff0…

QT 学习笔记(十三)

文章目录一、QDataStream 二进制文件读写1. QDataStream 简介2. QDataStream 实际演示2.1 QDataStream 读写文件操作2.2 实现代码——主窗口头文件 widget.h2.3 实现代码——主窗口源文件 widget.cpp二、QTextStream 文本文件读写1. QTextStream 简介2. QTextStream 实际演示2.…

【王道操作系统】3.2.3 页面置换算法(最佳置换算法、先进先出置换算法、最近最久未使用置换算法、普通时钟置换算法、改造型时钟置换算法)

页面置换算法(最佳置换算法、先进先出置换算法、最近最久未使用置换算法、普通时钟置换算法、改造型时钟置换算法) 文章目录页面置换算法(最佳置换算法、先进先出置换算法、最近最久未使用置换算法、普通时钟置换算法、改造型时钟置换算法)1.最佳置换算法---OPT2.先进先出置换算…

[安洵杯 2019]不是文件上传(文件名不能用“,数据库可识别hex编码)

打开题目尝试上传文件发现只能上传图片&#xff0c;然后看见了图片的路径&#xff0c;但是图片码连不上蚁键&#xff0c;结合题目&#xff0c;然后看到了题目给出的github源码 <?php include("./helper.php"); $show new show(); if($_GET["delete_all&qu…

STS安装,SVN配置及WEB工程配置简单说明_1.0.0

Spring Tool Suite构建java web项目概述1, 下载安装Spring Tool Suite Spring Tool Suite 简称为sts, 为构建基于Spring企业应用&#xff0c;提供了最好的Eclipse开发环境。STS 提供了最新基于Java和Spring应用所需要的所有工具,STS官网主页地址为http://spring.io/tools/sts/,…

画布创作,签名大致就是起笔落笔

画布创作&#xff0c;签名大致就是起笔落笔 前言 在画布创作过程中&#xff0c;签名手的基本形态是&#xff0c;不停的点、划。 就像我们写字一样&#xff0c;字越写越大&#xff0c;但是我们不知道笔画的大小。 一个字是一个非常小的点、横&#xff0c;一个横就把这个字给分…

JVM学习(一):简单聊聊性能调优这门艺术

一、性能调优背景说明1.1 生产环境中的问题生产环境发生了内存溢出该如何处理&#xff1f;生产环境应该给服务器分配多少内存合适&#xff1f;如何对垃圾回收器的性能进行调优&#xff1f;生产环境CPU负载飙高该如何处理&#xff1f;生产环境应该给应用分配多少线程合适&#x…

2-2进程管理-处理机调度

文章目录一.调度的层次二.进程调度的时机、切换与过程、调度方式三.调度器、闲逛进程四.调度算法的评价指标五.调度算法&#xff08;一&#xff09;先来先服务SCFS&#xff08;二&#xff09;短作业优先算法SJF&#xff08;三&#xff09;高响应比优先HRRN&#xff08;四&#…

cas6.6关于redis连接问题的一次记录,主要问题 1远程主机强迫关闭了一个现有的连接,主要问题2ERR unknown command `HELLO`

项目背景&#xff1a; 公司要求用cas单点登录&#xff0c;这个cas需要引入redis当做ticket缓存&#xff0c;但是出现连接不上的问题由于用redismanager可视化软件看了是可以连接的&#xff0c;进行了查询是redis配置文件中需要配置参数问题截图如下 问题输出如下 2023-01-04…

192:vue+openlayers: 选择feature,弹窗操作,删除所选feature

第192个 点击查看专栏目录 本示例的目的是介绍如何在vue+openlayer中使用select来选择feature元素,选择的过程中弹窗出现删除和关闭按钮,删除的内容是selected的feature,关闭的是功能浮窗。 直接复制下面的 vue+openlayers源代码,操作2分钟即可运行实现效果; 注意如果Ope…