先从存储的角度对二维数组作一个全面的了解。二维数组在内存中的存储,是按照先行后列依次存放的。从内存的角度看,可以这样说,二维数组其实就是一个一维数组,在内存中没有二维的概念。如果把二维数组的每一行看成一个整体,即看成一个数组中的一个元素,那么整个二维数组就是一个一维数组,它以每一行作为它的元素,这个应该很好理解。
第一,来详细介绍二维数组与指针的关系。-
首先定义个二维数组 array[3][4],p 为指向数组的指针。
若p=array[0],此时p指向的是二维数组第一行的首地址,则 p+i 将指向array[0]数组中的元素array[0][i]。由以上所介绍的二维数组在内存中的存储方式可知,对数组中的任一元素array[i][j] ,其指针的形式为:p+i*N+j (N为每一行的长度)。 元素相应的指针表示法为:*(p+i*N+j) ,下标表示法为:p[i*N+j] 。
For Example:
array[4][3]={{1,2,3},{4,5,6},{7,8,9},{10,11,12}};
int * p=array[0];
数组array有四个元素,分别为array[0],array[1],array[2],array[3],每个元素为包含3个元素的一维数组,
如array[0]的3个元素为 array[0][0],array[0][1],array[0][2]。
元素array[2][2]对应指针为:array+2*3+2,
指针表示法为:*(array+2*3+2) ,
下标表示法为:array[2*3+2] 。
特别注意:虽然 array[0] 与 array 都是数组首地址,但两者指向的对象不同,这点要非常明确。array[0] 是一维数组的名字,它指向的是一维数组array[0]的首地址,所以 *array[0]与array[0][0]为同个值。而 array 是二维数组的名字,它指向的是所属元素的首地址,其每个元素为一个行数组。它是以‘行’来作为指针移动单位的,如array+i 指向的是第 i 行。对 array 进行 * 运算,得到的是一维数组 array[0] 的首地址,所以 *array 与 array[0] 为同个值。如果定义 int* p,p为指int类型的指针,指向int 类型,而不是地址。故以下操作 :p=array[0] (正确) ,p=array (错误) 。这点要非常注意。
第二,看看如何用数组名作地址表示其中元素。
对二维数组array ,array[0] 由 array指向,故*array 与array[0] 是相同的,依次类推可得 array[i] 由array+i 指向,*(array+i) 与array[i]是相同的。 因此,对于数组元素 array[i][j] ,用数组名表示为 *(*(array+i)+j) ,指向该元素的指针为 *(array+i)+j 。
注意:数组名虽然是地址,但与指向数组的指针性质不同。指针变量可以随时改变其所指向对象,而数组名不可以,一旦被定义,就不能通过赋值使其指向另外一个数组,但是在Java中则可以。
第三,顺便了解一下不太常用的‘行数组指针’。
对于二维数组array[4][3],与int* p 。二维数组名array 不能直接赋值给p。原因前面已讲过,两只的对象性质不同。 在C语言中,可以通过定义一个行数组指针,使得这个指针与二维数组名具有同样的性质,实现它们之间可以直接赋值。行数组指针定义如下:
int (*p)[3]; 它表示,数组 *p 具有三个int类型元素,分别为 (*p)[0] , (*p)[1] , (*p)[2] ,即 p指向的是具有三个int类型的一维数组,也就是说,p为行指针。此时,以下运算 p=array 是正确的。
第四,二维数组作为函数参数。
二维数组作为函数参数一般有两种方式:(1) void func(int **array){...} (2) void func(int array[ ][N])
注意第二种方式一定要指明二维数组的列数
当二维数组名作为函数实参时,对应的形参必须是一个行指针变量。
和一维数组一样,数组名传送给变量的是一个地址值,因此,对应的形参也必须是一个类型相同的指针变量,在函数中引用的将是主函数中的数组元素,系统只为形参开辟一个存放地址的存储单元,而不可能在调用函数时为形参开辟一系列存放数组的存储单元。
int main()
double a[3][4];
……
fun(a);
……
fun(double (*a)[n])
……
-
C语言中二维数组行指针是什么
2017-09-08 13:57:00如果把二维数组的每一行看成一个整体,即看成一个数组中的一个元素,那么整个二维数组就是一个一维数组,它以每一行作为它的元素,这个应该很好理解。第一,来详细介绍二维数组与指针的关系。-首先定义个二维数组 ...转载于:https://www.cnblogs.com/li1020863344/p/7494290.html
-
C语言二维数组的指针
2019-09-12 16:27:53一维数组的指针,指向的是单个元素 虽然a[0]与a的首地址都是a[0][0],但是随着指针的偏移,a每次偏移12B,而a[0]每次偏移4B 为int a[2][3]定义一个指针int *p; 为什么*p=a是错误的呢?因...以int a[2][3]={};这个二维数组为例子:
a为a[2][3]的数组名,二维数组的指针,指向的是一维数组
而a[0]数组的指针,是单个元素a[0][0];一维数组的指针,指向的是单个元素
虽然a[0]与a的首地址都是a[0][0],但是随着指针的偏移,a每次偏移12B,而a[0]每次偏移4B
为int a[2][3]定义一个指针int *p;
为什么*p=a是错误的呢?因为a指向的是a[0]这个一维数组,大小为12B,但是*p定义的大小为4B;
那如何编写才是正确的呢?
定义int (*p)[3],在用p=a,这样地址内存空间大小一样,就没有问题了
这里需要注意的是:*p[3]表示的是指针数组,在这个指针数组里,包含*p[0],*p[1],*p[2];
-
(指针)——图文并茂形象理解指针(一级,二级,指针与一维数组,指针与二维数组,指针与函数)
2020-02-11 21:46:28指针难,是因为指针针对不同的情况,有不同的用法,一头扎进去会比较混乱,...指针是一种存放地址的变量类型 这里我们可以看到,在内存单元当中,每一个内存单元都有一个地址(物理地址) 指针就是用来保存被指向...指针难,是因为指针针对不同的情况,有不同的用法,一头扎进去会比较混乱,搞不清楚状况,让人头疼,但是!!!理清关系之后,对于指针的运用会很清晰。
这里,我们通过对指针各种情况的说明来认识指针,看看指针真的有那么难吗?
1、什么是指针?
首先!指针是一种变量类型
其次!指针是一种存放地址的变量类型
这里我们可以看到,在内存单元当中,每一个内存单元都有一个地址(物理地址)
指针就是用来保存被指向的内存单元的地址(如图上的:0x100)
而指针 p 本身也有一个物理地址(如图上的:0x105)
只有先把最基本的搞清楚,再来看看这个概念
什么是一级指针,什么是二级指针?
1、一级指针:
1、这里再强调:
*p = *(&a )= *(0x100)= 6
这里的 * 表示 “取值运算符” ,也就是说:将里边的内容取出来
2、所谓一级指针就是指针 * 一次(取值一次),就得到了变量的内容,这样的指针就是一级指针。
2、二级指针:
这里看到是二级指针的表示,实际上不论是一级指针,二级指针,还是五级指针,八级指针,(当然一般二级就够用了),道理都只一样的,无非就是指针保存前级指针的地址(指针也是一个变量,也在内存中存放着,也有自己的物理地址)
接着我们看看指针与数组、函数等等的各种关系,一边推导一边深入理解指针
1、定义一个指针指向一维数组的 [ 0 ] 号元素(首元素)( A[5] )
这里可以看到:
当指针指向一维数组的第一个元素时, p = &A[0] = A(数组名)
A—>数组名----->首地址 和 &A[0]相等
补充个知识:p[n] = *(p+n)
因此:
对于第一个元素 A[0]来说p = &A[0] = A
*p = *(&A[0]) = p[0] = *(A)= A[0](这他娘的看着复杂,其实很简单,就是一个加 * 取值的过程,这些都是等价的)
对于第二个元素 A[1] 来说
p+1 = &A[0] + 1 = &A[1] = A+1
*(p+1) = *(&A[0] + 1) = *(&A[1]) = *(A+1) = A[1] = p[1]
对于第三个元素 A[2] 来说
p+2 = &A[0] + 2 = &A[2] = A+2
*(p+2) = *(&A[0] + 2) = *(&A[2]) = *(A+2) = A[2] = p[2]
以此类推,对一维数组的每一个元素的遍历都可以这样利用指针来进性表示、访问、操作。
2、定义一个指针指向一维数组整体( A[5] )
这里注意:此时的指针是指向 A[5] 这个数组整体的
像一个渔网一样,网的范围是包住了这个数组整体因此定义这样的指针:
数据类型:int [ 5 ]
指针:(*p)—>这里的p就是指针名,*号仍然是标志作用,告诉程序 变量p是一个指针
按理说:指针定义应该是这样的:
int [5] (*p)
前边是类型:int [ 5 ]
后边是指针名和 *号:(*p)
但是为了表达起来比较直观,比较好看
所以一个指向数组整体的指针就是这样的:
int (*p)[ 5 ] = &A
注意
指针的定义,指向谁就取谁的地址,比如我们要指向一维数组的第一个元素,那么 p = &A[ 0 ]。只不过一维数组名(A)也表示数组首地址,因此 p = &A[ 0 ] = A 这样也可以(A和&A[ 0 ]等价)
这里我们指向的是一个数组整体,因此这个指针 p = &A
那么对于指针指向一维数组整体
对于数组的第一个元素:p = &A
*p = *(&A) = A = &A[ 0 ]
**p = *( * (&A) ) = *(A) = *( &A[ 0 ] ) = A[ 0 ]
对于数组的第二个元素:
*p = *( &A ) = A = &A[ 0 ]
*p + 1 = &A[ 0 ] + 1 = &A[ 1 ] :这里就通过首地址加一得到第二个元素的地址
*( *p + 1)= *( &A[ 0 ] + 1 )= *( &A[ 1 ] ) = A[ 1 ]
对于数组的第三个元素:
还是同上边一样,这里直接用指针来表示
A[ 2 ] = *( *p + 2 )
3、定义指针指向二维数组中的首元素的第一个数据(这里数组为A[3][3])
注意:
A[ 0 ] 为二维数组的第一个元素(首元素)
A[ 1 ] 为二维数组的第二个元素
A[ 2 ] 为二维数组的第三个元素这里:指针定义为指向二维数组 的首元素 的第一个数据
int *p = &A[0][0];
p = &A[0][0]
对于第一个数据 A[0][0]
p = &A[0][0]
*p = *( &A[0][0] ) = A[0][0]
对于二维数组的 首元素的 第三个数据 A[0][2]
p = &A[0][0]
p + 2 = &A[0][0] + 2 = &A[0][2]*(p + 2) = *( &A[0][0] + 2 ) = A[0][2]
对于二维数组 的第二个元素的第一个数据 A[1][0]
p = &A[0][0]
p + 3 = &A[0][0] + 3 = &A[1][0]*(p + 3) = *( &A[0][0] + 3 ) = *( &A[1][0] ) = A[1][0]
对于二维数组的第三个元素的第三个元素 A[2][2]
p = &A[0][0]
p + 8 = &A[0][0] + 8 = &A[2][2]*(p + 8) = *( &A[0][0] + 8 ) = *( &A[2][2] ) = A[2][2]
这样的方式,通过定义指针指向二维数组的第一个元素(首元素)的第一个数据,来对二维数组进行遍历、访问、操作。
定义一个指针指向二维数组首元素整体
首先我们将二维数组 A[3][3] 分成行列式的形式,这样便于我们查看指针的移动状态。定义:指针指向二维数组的首元素整体—>A[ 0 ]
int (*p)[ 3 ] = &A[ 0 ];
p = &A[ 0 ];
对于二维数组的首元素的第一个数据 A[0][0]
p = &A[ 0 ]
*p = *( &A[ 0 ] ) = A[ 0 ] = &A[ 0 ][ 0 ] (由于A[ 0 ]为二维数组的首元素,也代表第一个数据的首地址,所以 A[ 0 ] = &A[0][0])
*(*p) = *( &A[0][0] ) = A[0][0]
对于二维数组的第零个元素(首元素)的第二个数据 A[0][1]
p = &A[ 0 ]
*p = *( &A[ 0 ] ) = A[ 0 ] = &A[ 0 ][ 0 ]
*p + 1 = &A[ 0 ][ 0 ] + 1 = &A[0][1]
*( *p + 1 ) = *( &A[ 0 ][ 0 ] + 1 ) = *( &A[ 0 ][ 1 ] ) = A[ 0 ][ 1 ]
对于二维数组的第一个元素的第二个数据 A[ 1 ][ 1 ]
p = &A[ 0 ]
p + 1 = &A[ 0 ] + 1 = &A[ 1 ]
*(p + 1) = *( &A[ 1 ] ) = A[ 1 ] = &A[ 1 ][ 0 ]
*(p + 1) + 1 = &A[ 1 ][ 0 ] + 1*( *(p + 1) + 1 ) = *( &A[ 1 ][ 1 ] ) = A[ 1 ][ 1 ]
对于二维数组的第二个元素的第三个数据 A[ 2 ][ 2 ]
p = &A[ 0 ]
p + 2 = &A[ 0 ] + 2 = &A[ 2 ]
*(p + 2) = *( &A[ 2 ] ) = A[ 2 ] = &A[ 2 ][ 0 ]
*(p + 2) + 2 = &A[ 2 ][ 0 ] + 2 = &A[ 2 ][ 2 ]
*( *(p + 2) + 2 ) = *( &A[ 2 ][ 2 ])
对于指针针与数组的情况就是这样,用指针先搞清楚它指向谁,怎样移动,移动多少,* 的运用不是乱加,并且指针的知识还有很多,不仅是与数组,还有与函数等等,指针的灵活性很高,是一个很方便的工具。
在C语言中 * 的使用有这几种:
1、乘法运算: * (乘法运算符号)
2、定义指针时:* (起标志作用,告诉程序这是一个指针)
3、配合指针变量:* (取值运算符—>将指针指向的空间的值取出来)
接下来,我们看看指针与函数
1、指针函数:
指针函数:
首先它是一个函数
怎样的函数?
一个返回值为指针类型的函数什么意思呢?
void Func0 (int , int) :这是一个函数名为Func0的函数,返回值为空(void),函数的参数为(int, int)两个整型参数int Func1 (int, int):这是一个函数名为Func1的函数,返回值为一个 int 整型数,函数参数为 (int, int)两个整型参数
int * Func2 (int, char):这是一个函数名为Func2的函数,返回值为一个指向int型变量的指针,函数参数为 (int, char)一个int型,一个char型。
2、函数指针
函数指针:
首先是一个指针
什么样的指针?
一个指向函数的指针什么意思呢?
int Func (int, char)
所以要定义一个指针指向这个函数,那么这个指针的数据类型就是 int (int, char)
这里有一个函数:
int Add (int , int)
定义函数指针:
int (*pFunc) (int, int) = &Add;
这里的pFunc就是一个指向函数Add的指针
或者可以这样
int Add(int,int);------->函数Add
int (*pFunc)(int, int);------>指向 int (int,int)函数类型的指针
pFunc = &Add;
或者
pFunc = Add;这样也可以,不用取地址符也行在这里
一般我们这样定义一个函数指针:
typedef int (*FUNC)(int, int)
这样我们就可以用 FUNC 来定义一个函数指针了当我们需要用到该函数时,直接调用指针即可,就相当于在调用函数,会比较方便。
这里的FUNC只是一个名字,随便起一个其他名字都可以,但一般起名称无论是函数名、数组名、变量名,最好做到见名知意。
指针数组
指针数组:
首先是一个数组
什么样的数组?
一个数组元素全都是指针的数组char *Arr[ 3 ] = { " hello " , " world " , " abcd " };
这个数组有三个元素:
第一个元素表示 hello 这个字符串的首地址
第二个元素表示 world 这个字符串的首地址
第三个元素表示 abcd 这个字符串的首地址
printf ( " %s ", Arr[ 2 ] );
就会输出 abcd 这个字符串
关于指针的知识还有很多,在这里把这些指针与数组,与函数的关系表达出来,对指针是什么,它是怎样去定义,怎样移动,怎样取到它所指向的空间的值通过图形,以及推到,把它的思路理清楚,初次编写,有不足之处还请多多指教。
-
20201123-C语言补充-二维数组-再谈二维数组与指针
2020-12-07 11:23:24文章目录二维数组1 一维数组中的==指针数组==和==数组的指针==区别1.1 指针数组1.2 数组的指针,即==行指针==1.3 指针可以指向什么?1.4 再看指针数组1.5 数组的指针之赋值1.6 进一步观察行指针,即数组的指针的移动...文章目录
二维数组
1 一维数组中的指针数组和数组的指针区别
1.1 指针数组
int *p[N];
读解,此时,[]优先级比*高,先结合进行运算,即首先,它成为了一个数组。
- 它的大小就是N,即可以存放N个元素
但是什么元素呢?
- 即
int *
这玩艺儿一看就眼熟了,这不就是整型指针吗?是的,就是整型指针 - 也就是说,元素是整型指针
- 故叫
指针数组
说白了,它仍然是数组。就这里的这个定义来看,它和普通的整型数组不一样。因为,它里面只能装==
指向整型变量的指针
==。1.2 数组的指针,即行指针
前面的叫指针数组,那么这个数组的指针能不能简称为数组指针呢?我个人觉得不可以。
但确实也有人学着学着,就这么叫了。
先看具体的一个示例
int (*p)[N];
大家都知道,
()
的优先级最高,那么,它里面的表达式优先运算,则计算结果就是:-
*p
是指针,再往前看,就是int,也就是说,是指针的基类型是整型 -
而
[]
是数组的标识,前面是指针,然后又做成了数组,这就不好理解了- 数组内有N个元素,基类型肯定也是整型
- 普通情况,
()
这个位置里,应该是一个变量名,而这时,变成了一个指针 - 所以,它是一个数组的指针,含义是定义了一个指向N个元素的一维数组的指针
1.3 指针可以指向什么?
int a; // 变量===本质上就是一个单位的整型内存空间 int *p; // 变量的指针 p = &a; // 让指针指向对应的内存空间 int arr[N]; // 数组 int (*p)[N]; // 数组的指针 p = &arr; // 指针指向数组对应的内存空间【的首地址】
可以明确地看到,什么样的指针,指向什么样的内存空间,必须对应,才可以完成指向,否则,编译出错。
1.4 再看指针数组
#include<stdio.h> #define N 5 int main(void) { int *p[N]; // 定义指针数组,也就是说,这是定义了一组指针 int a = 10; int b = 20; int c = 30; // 一个个完成指针数组里的元素的赋值,指针赋值,即完成指向初始化 p[0] = &a; p[1] = &b; p[2] = &c; printf("变量a = %d, 指针pa = 0X%p, 指针解引用*pa = %d\n", a, p[0], *p[0]); printf("变量b = %d, 指针pb = 0X%p, 指针解引用*pb = %d\n", b, p[1], *p[1]); printf("变量c = %d, 指针pc = 0X%p, 指针解引用*pc = %d\n", c, p[2], *p[2]); return 0; }
运行结果如下:
PS E:\clangstudy\class02> cd "e:\clangstudy\class02\" ; if ($?) { gcc 'arr_point01.c' -o 'arr_point01.exe' -Wall -g -O2 -static-libgcc -std=c11 -fexec-charset=utf-8 } ; if ($?) { &'.\arr_point01' } 变量a = 10, 指针pa = 0X000000000061FE44, 指针解引用*pa = 10 变量b = 20, 指针pb = 0X000000000061FE48, 指针解引用*pb = 20 变量c = 30, 指针pc = 0X000000000061FE4C, 指针解引用*pc = 30
从程序运行可以看出:
- 这是一组指针,还有两个未用到
- 指针占用4个字节的内存大小,顺序排列
- 指针完成指向时,要取普通变量的地址
- 指针解引用可以直接取值
基本知识点:
&
为取地址*
为取值,即指针解引用
1.5 数组的指针之赋值
#include<stdio.h> #define N 5 int main(void) { int (*p)[N]; // 这是一个数组的指针,只有指向有N个元素的数组【本质上,是一个行指针】 int a[N] = {1, 2, 3, 4, 5}; int i; p = &a; // a是数组名,本身也是地址,即数组首地址,但这个指针是数组的指针,就要直接取数组的地址 printf("查看内地址:\n"); printf("\n指针本身的地址:0X%p", &p); printf("\n指针指向的地址:0X%p", p); printf("\n数组的首地址:0X%p", a); printf("\n数组的首元素的地址:0X%p", &a[0]); printf("\n数组的地址:0X%p", &a); printf("\n指针解引用值:%d", *p[0]); a[0] = 11; *p[0] = 111; // 刚好首行首列,操作有效 *p[1] = 22; // 没有这一行,操作无效 *p[3] = 33; // 没有这一行,操作无效 printf("\n打印数组里的值:"); for ( i = 0; i < N; i++) { printf("%d ", a[i]); } // 使用行指针时,先取行,再取行上的列,注意解引用的顺序 *(*(p)+0) = 112; // 首行0列 *(*(p)+1) = 22; // 首行1列 *(*(p)+2) = 33; // 首行2列 printf("\n再打印数组里的值:"); for ( i = 0; i < N; i++) { printf("%d ", a[i]); } return 0; }
运行结果如下:
PS E:\clangstudy\class02> cd "e:\clangstudy\class02\" ; if ($?) { gcc 'arr_point02.c' -o 'arr_point02.exe' -Wall -g -O2 -static-libgcc -std=c11 -fexec-charset=utf-8 } ; if ($?) { &'.\arr_point02' } 查看内地址: 指针本身的地址:0X000000000061FE08 指针指向的地址:0X000000000061FE10 数组的首地址:0X000000000061FE10 数组的首元素的地址:0X000000000061FE10 数组的地址:0X000000000061FE10 指针解引用值:1 打印数组里的值:111 2 3 4 5 再打印数组里的值:112 22 33 4 5
从程序运行可以看出:
- 数组的首地址、首个元素的地址、数组的地址,都是同一个地址
- 指针本身是要占用内存空间的,它是指针变量
- 指针完成指向后,可以存取该地址单元【基类型空间大小】
- 数组的指针指向一个数组,这个数组,就是一个单元
- 数组的指针,第一次解引用,就是取行,再一次解引用,就是取列
- 所以,数组的指针,可以直接和二维数组对应
1.6 进一步观察行指针,即数组的指针的移动
#include<stdio.h> #define N 5 int main(void) { int (*p)[N]; // 这是一个数组的指针,只有指向有N个元素的数组【本质上,是一个行指针】 int a[N] = {1, 2, 3, 4, 5}; int i; p = &a; // a是数组名,本身也是地址,即数组首地址,但这个指针是数组的指针,就要直接取数组的地址 printf("查看内地址:\n"); printf("\n指针本身的地址:0X%p", &p); printf("\n指针指向的地址:0X%p", p); printf("\n数组的首地址:0X%p", a); printf("\n数组的首元素的地址:0X%p", &a[0]); printf("\n数组的地址:0X%p", &a); printf("\n指针解引用值:%d", *p[0]); a[0] = 11; *p[0] = 111; // 刚好首行首列,操作有效 *p[1] = 22; // 没有这一行,操作无效 *p[3] = 33; // 没有这一行,操作无效 printf("\n打印数组里的值:"); for ( i = 0; i < N; i++) { printf("%d ", a[i]); } // 使用行指针时,先取行,再取行上的列,注意解引用的顺序 *(*(p)+0) = 112; // 首行0列 *(*(p)+1) = 22; // 首行1列 *(*(p)+2) = 33; // 首行2列 printf("\n再打印数组里的值:"); for ( i = 0; i < N; i++) { printf("%d ", a[i]); } printf("\n数组的内存大小:%d字节【十六进制】", (int)sizeof(a)); printf("\n完成指向后的p指针所指的地址:0X%p", p); printf("\n完成指向后的p指针移动【行移动】所指的地址:0X%p", p + 1); printf("\n完成指向后的p指针移动【行移动】所指的地址:0X%p", p + 2); printf("\n完成指向后的p指针移动【列移动】所指的地址:0X%p", *(p)); printf("\n完成指向后的p指针移动【列移动】所指的地址:0X%p", *(p) + 1); return 0; }
查看最后的运行结果
PS E:\clangstudy\class02> cd "e:\clangstudy\class02\" ; if ($?) { gcc 'arr_point02.c' -o 'arr_point02.exe' -Wall -g -O2 -static-libgcc -std=c11 -fexec-charset=utf-8 } ; if ($?) { &'.\arr_point02' } 查看内地址: 指针本身的地址:0X000000000061FE08 指针指向的地址:0X000000000061FE10 数组的首地址:0X000000000061FE10 数组的首元素的地址:0X000000000061FE10 数组的地址:0X000000000061FE10 指针解引用值:1 打印数组里的值:111 2 3 4 5 再打印数组里的值:112 22 33 4 5 数组的内存大小:20字节【十六进制】 完成指向后的p指针所指的地址:0X000000000061FE10 完成指向后的p指针移动【行移动】所指的地址:0X000000000061FE24 完成指向后的p指针移动【行移动】所指的地址:0X000000000061FE38 完成指向后的p指针移动【列移动】所指的地址:0X000000000061FE10 完成指向后的p指针移动【列移动】所指的地址:0X000000000061FE14
最后四行表明
- 行移动,一次是20个字节,刚好就是五个int的字节数
- 列移动,一次是4个字节,即一个int的字节数
2 对于数组的地址
2.1 概念
- 数组的地址
- 数组的首地址
- 数组元素的地址,首元素的地址
- 二维数组
a[m][n]
- 数组首地址
- 首行地址
- 首行首列地址
- 第一个元素【仍然是数组】的地址,即
a[0]
的地址 - 第一个数据元素的地址,即
a[0][0]
的地址
一维数组
int a[5];
a表示的是数组的首地址,a等价于&a[0]
二维数组
int a[2][2] = {1, 2, 3, 4};
a表示的整个数组的首地址,a[0]表示的是第一行的首地址,这两者者在数值上是一样的,但含义不同(或者说类型不同),数组名a是对于整个数组,a[0]是对于第一行
从上面的示例运行结果来看,有些地址就是同一个地址
在用数组的地址进行赋值的时候,虽然三者值相同,但是三者不可随意混用(以int
a[2][2]
为例)a--------是int (*)[2]型
a[0]-----是int *型
对于
a[0]
和&a[0][0]
,两个类型都是int *型的,所以下述两种赋值方法等价第一种:
int a[2][2] = {1, 2, 3, 4}; int *p; p = a[0];
第二种:
int a[2][2] = {1, 2, 3, 4}; int *p; p = &a[0][0];
对于
int a[2][2]
来说,如果将a[0]改为&a[0],那么&a[0]和a的类型相同,都为int (*)[2]类型,下面以int a[5][5]
为例,列出了二维数组的元素在不同方式表达下的不同类型。[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BW6lOvME-1607311364560)(20201120-C语言-二维数组.assets/image-20201120145832572.png)]
也可以用一维指针数组来保存二维数组中某个元素的地址
int a[2][2] = {1, 2, 3, 4}; int *p[2]; p[0] = &a[0][0]; printf("%d", *p[0]);
3 二维数组的解引用
以二维数组
a[2][3]={1, 2, 3, 4 ,5, 6};
为例(第一维是行,第二维是列)
第一种:
*(*a+1)
--------等价于a[0][1]
,因为*的优先级比+高,所以先解引用,进入第二维在第二维里面地址+1,再次解引用得到元素值第二种:
*(*(a+1))
------等价于a[1][0]
,比上面第一种多加了一个括号,括号优先级最高,先+1移动地址(注意是在第一维里面移动即行上的移动),然后解引用进入第二维,再解引用得到元素的值第三种:
*(&a[0][0]+1)
-----等价于a[0][1]
,这里使用了&取地址符【注意,这里取出来的是变量元素即基元素的地址,地址就是指针,如果指针移动,将以它为基准,一次移动一个基元素内存大小,本质上,也就是列上的移动】,将原本表示第一个元素的a[0][0]
返回到第二个维度,然后第二维地址+1,再解引用得到元素的值[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UbekSuFT-1607311364564)(20201120-C语言-二维数组.assets/image-20201120150728181.png)]
对于
a[2][3]
的解引用的过程:- 二维数组,共2行,3列
- 一维数组名,本质上是列指针,而二维数组名,本质上是行指针,但这些数组名,都是常指针,即,指向固定不变
- 一维数组名,一次解引用,即可获取对应列上的元素
- 二维数组名,二次解引用,才可以获取对应行的对应列上的元素
- 对于一维,在解引用之前,是可以让指向偏移的,但指针不需要移动,只是指向发生偏移
- 对于二维,在解引用一层之前,可以偏移,即【行偏移】,再一次解引用之前,还可以再偏移,即【列偏移】,偏移到指定位置后,再第二层解引用
- 直接取基元素的地址,则偏移一定是以基元素为准,一次一个基元素的内存单位大小
- 本质上:
- 行指针,基元素变为一个一维数组
- 列指针,基元素即为基元素本身
- 基一旦发生变化,移动或是偏移时,指针跨过的内存单位大小就随之而变化
- 行指针,经过一次解引用,就化为列指针,仍然是地址;也就是说,行指针经过两次解引用后,也就是取值,不再是地址;
- 列指针,经过一次解此用,就是取值,不再是地址;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-V8vTBpkk-1607311364570)(20201120-C语言-二维数组.assets/image-20201120150824653.png)]
示例分析
#include <stdio.h> int main(void) { int a[2][3] = {1, 2, 3, 4, 5, 6}; printf("%d***%d\n", *(a[1]), (*a)[0]); printf("%d***%d\n", *(a[1]+1), (*a+1)[0]); printf("%d***%d\n", *(a[1]+1), (*a+1)[1]); return 0; }
运行结果如下:
PS E:\clangstudy\class02> cd "e:\clangstudy\class02\" ; if ($?) { gcc 'arr_point03.c' -o 'arr_point03.exe' -Wall -g -O2 -static-libgcc -std=c11 -fexec-charset=utf-8 } ; if ($?) { &'.\arr_point03' } arr_point03.c: In function 'main': arr_point03.c:5:19: warning: missing braces around initializer [-Wmissing-braces] int a[2][3] = {1, 2, 3, 4, 5, 6}; ^ { } { } 4***1 5***2 5***3
虽然有警告,也说明内存是线性,仍然可以一次贯穿了来完成赋值
- 二维可以用一维的方式来初始化
- 先移动,再解引用,还是先解引用,再移动,程序员自己要明白清楚
- 数组的下标操作,和解引用有相同的效果,但下标是可以直接定位到相应的行或是列上的
- 行标定位行
- 列标才定位列
- 对于二维数组,带行标的,肯定还是列指针,还可以做偏移,还可以再解引用
- 对于一维数组,只能带列标,带上列标,即取值
- 对于行指针,解引用一次后,就成了一维数组上的列指针,再带上列标,即取值
4 解引用和下标
4.1 下标是数组说法,解引用是指针说法
*(a[1]+1)--------表示的是
a[1][1]
的值过程解析:
- 行标
a[1]
,标号为1,即第二行,转化为列指针 - 偏移
a[1]+1
,偏移量为1,即第二行第二列,仍然是列指针 - 解引用
*(a[1]+1)
,即取值,取的就是a[1][1]
元素的值
(*a+1)[1]--------表示的是
a[0][2]
的值过程解析:
- 解引用
*a
,由行指针转为列指针,指在首行首列,即第1列 - 偏移
(*a+1)
,指在首行第2列 - 取列标
(*a+1)[1]
,仍然是一个数组,如果是列标为[0]
即当前所指位置,而这时,列标号为1,即偏移一个基元素,也就是取首行第二列的下一个元素,即首行第三列的元素,即a[0][2]
元素
4.2 为了方便理解,再一次详细描述一下
先退回一维数组,以
int a[5];
来说,a表示的数组a的首地址,a[2]表示在a的基础上移动2个地址(注意a的类型是int *型的),再解引用得到元素的值,意思是a[2]
实际上包含了两步
- 第一步地址移动
- 第二步解引用得到元素的值(注意第二步,有点隐式转换的意思,经常被人忽略)
现在来解释上面的二维数组就容易多了
-
先来看第一个
*(a[1]+1)
- a[1]代表第二行的首地址,注意这里的维度已经是第二维度了
- 然后括号优先第二维地址+1
- 最后解引用得到元素的值
-
再看第二个
(*a+1)[1]
,这里提一句,因为[]的优先级是比高的所以这里的括号不能去掉- 第一步先解引用进入第二维度(*优先级高于+)
- 然后第二维地址+1
- 然后再在当前基础上再移动一次地址,只要不是
[0]
,就会发生位置偏移 - 最后下标取值
- 得到元素的值,这里可能有点绕,换个说法就是[1]是在当前维度进行移动,然后解引用(“当前维度”有点不太严谨,为了方便理解先将就这么用了)
拿
a[2][1]
来说一共有四步- 其中包含了两次地址移动,两次解引用
- 执行顺序是:
- 地址移动->解引用->地址移动->解引用
- (这里提一句,[]的结合性是左结合的,所以在移动的时候先移动行(第一维)再移动列(第二维))
详细步骤:
- 第一步:在当前维度地址+2,因为a的维度是第一维,所以是第一维地址+2,即行+2
- 第二步:解引用进入第二维度
- 第三步:在当前维度地址+1,因为这时已经进入第二维,所以第二维地址+1,即列+1
- 第四步:解引用得到元素的值
5 理解指针数组的本质==内存空间的分配和使用
概括的说,指针其实就是可变数组的首地址,说是可变数组,是指其包含内容的数量的可变的,并且是可动态申请和释放的,从而充分节约宝贵的内存资源。我一向喜欢一维数组,除非万不得已,我一般是不用二维数组的,多维的则更是很少涉足了。因为一维简单,容易理解,而用指针指向的多维数组就具有相当的复杂性了,也因此更具有讨论的必要。
本质上,就是为了更好地使用和操纵内存
5.1 三个二维数组的比较
int **Ptr; int *Ptr[5]; int (*Ptr)[5];
三例都是整数的二维数组,都可以用形如
Ptr[0][0]
的方式访问其内容;但它们的差别却是很大的。5.2 从四个方面对它们进行讨论
5.2.1 内容:
它们本身都是指针,它们的最终内容都是整数。注意这里说的是最终内容,而不是中间内容,比如你写 Ptr[ 0 ],对于三者来说,其内容都是一个整数指针,即
int *
;Ptr[1][1]
这样的形式才是其最终内容。5.2.2 意义:
(1)、
int **Ptr
表示指向"一群"指向整数的指针的指针。【可以认为是指针数组,只能指向指针,这些被指向的指针是整型指针】
(2)、int *Ptr[5]
表示指向 5 个指向整数的指针的指针。【就是5个指针,成了一组】
(3)、int (*Ptr)[5]
表示指向"一群"指向 5 个整数数组的指针的指针。【即数组的指针,只能指向数组,不能指向整型元素】5.2.3 所占空间:
(1)、
int **Ptr
和 (3)、int (*Ptr)[5]
一样,在32位平台里,都是4字节,即一个指针。但 (2)、int *Ptr[5]
不同,它是 5 个指针,它占5 * 4 = 20个字节的内存空间。5.2.4 用法:
(1)、
int **Ptr
因为是指针的指针,需要两次内存分配才能使用其最终内容。首先,
Ptr = (int **)new int *[5];
这样分配好了以后,它和(2)的
意义相同了;然后要分别对 5 个指针进行内存分配,例如:Ptr[0] = new int[20];
它表示为第 0 个指针分配 20 个整数,分配好以后,Ptr[0]
为指向 20 个整数的数组。这时可以使用下标用法Ptr[0][0]
到Ptr[0][19]
了。
如果没有第一次内存分配,该Ptr
是个"野"指针,是不能使用的,如果没有第二次内存分配,则Ptr[0]
等也是个"野"指针,也是不能用的。当然,用它指向某个已经定义的地址则是允许的,那是另外的用法(类似于"借鸡生蛋"的做法),这里不作讨论(下同)。
(2)、int *Ptr[5]
这样定义的话,编译器已经为它分配了 5 个指针的空间,这相当于(1)中的第一次内存分配。根据对(1)的讨论可知,显然要对其进行一次内存分配的。否则就是"野"指针。
(3)、int (*Ptr)[5]
这种定义我觉得很费解,不是不懂,而是觉得理解起来特别吃力,也许是我不太习惯这样的定义吧。怎么描述它呢?它的意义是"一群"指针,每个指针都是指向一个 5 个整数的数组。如果想分配 k 个指针,这样写:Ptr = (int(*)[5]) new int[sizeof(int)*5*k]
这是一次性的内存分配。分配好以后,
Ptr
指向一片连续的地址空间,其中Ptr[0]
指向第 0 个 5 个整数数组的首地址,Ptr[1]
指向第1 个 5 个整数数组的首地址。综上所述,我觉得可以这样理解它们:
int ** Ptr <==> int Ptr[ x ][ y ];
int *Ptr[ 5 ] <==> int Ptr[ 5 ][ x ];
int ( *Ptr )[ 5 ] <==> int Ptr[ x ][ 5 ];
这里 x 和 y 是表示若干的意思。6 指针数组(数组每个元素都是指针)详解
如果一个数组中的所有元素保存的都是指针,那么我们就称它为指针数组。
指针数组的定义形式一般为:
dataType *arrayName[length];
[ ]
的优先级高于*
,该定义形式应该理解为:dataType *(arrayName[length]);
括号里面说明
arrayName
是一个数组,包含了length
个元素,括号外面说明每个元素的类型为dataType *
。除了每个元素的数据类型不同,指针数组和普通数组在其他方面都是一样的,下面是一个简单的例子:
#include <stdio.h> int main() { int a = 16, b = 932, c = 100; //定义一个指针数组 int *arr[3] = {&a, &b, &c}; //也可以不指定长度,直接写作 int *arr[] //定义一个指向指针数组的指针 int **parr = arr; printf("%d, %d, %d\n", *arr[0], *arr[1], *arr[2]); printf("%d, %d, %d\n", **(parr+0), **(parr+1), **(parr+2)); return 0; }
运行结果:
16, 932, 100
16, 932, 100arr 是一个指针数组,它包含了 3 个元素,每个元素都是一个指针,在定义 arr 的同时,我们使用变量 a、b、c 的地址对它进行了初始化,这和普通数组是多么地类似。
parr 是指向数组 arr 的指针,确切地说是指向 arr 第 0 个元素的指针,它的定义形式应该理解为
int *(*parr)
,括号中的*
表示 parr 是一个指针,括号外面的int *
表示 parr 指向的数据的类型。arr 第 0 个元素的类型为 int *,所以在定义 parr 时要加两个 *。第一个 printf() 语句中,arr[i] 表示获取第 i 个元素的值,该元素是一个指针,还需要在前面增加一个 * 才能取得它指向的数据,也即 *arr[i] 的形式。
第二个 printf() 语句中,parr+i 表示第 i 个元素的地址,*(parr+i) 表示获取第 i 个元素的值(该元素是一个指针),**(parr+i) 表示获取第 i 个元素指向的数据。
指针数组还可以和字符串数组结合使用,请看下面的例子:
#include <stdio.h> int main() { char *str[3] = { "www.cuit.edu.cn", "数学学院学习C语言", "C Language" }; printf("%s\n%s\n%s\n", str[0], str[1], str[2]); return 0; }
运行结果:
www.cuit.edu.cn
数学学院学习C语言
C Language需要注意的是,字符数组 str 中存放的是字符串的首地址,不是字符串本身,字符串本身位于其他的内存区域,和字符数组是分开的。
也只有当指针数组中每个元素的类型都是
char *
时,才能像上面那样给指针数组赋值,其他类型不行。为了便于理解,可以将上面的字符串数组改成下面的形式,它们都是等价的。
#include <stdio.h> int main() { char *str0 = "www.cuit.edu.cn"; char *str1 = "数学学院学习C语言"; char *str2 = "C Language"; char *str[3] = {str0, str1, str2}; printf("%s\n%s\n%s\n", str[0], str[1], str[2]); return 0; }
7 二维数组的内存理解
7.1 基本概念理解
int a[3][4] = { {0, 1, 2, 3}, {4, 5, 6, 7}, {8, 9, 10, 11} };
从概念上理解,a 的分布像一个矩阵:
0 1 2 3 4 5 6 7 8 9 10 11
但在内存中,a 的分布是一维线性的,整个数组占用一块连续的内存:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UeLCBcZf-1607311364577)(20201120-C语言-二维数组.assets/image-20201120160552706.png)]
C语言中的二维数组是按行排列的,也就是先存放 a[0] 行,再存放 a[1] 行,最后存放 a[2] 行;每行中的 4 个元素也是依次存放。数组 a 为 int 类型,每个元素占用 4 个字节,整个数组共占用 4×(3×4) = 48 个字节。
C语言允许把一个二维数组分解成多个一维数组来处理。对于数组 a,它可以分解成三个一维数组,即 a[0]、a[1]、a[2]。每一个一维数组又包含了 4 个元素,例如 a[0] 包含
a[0][0]
、a[0][1]
、a[0][2]
、a[0][3]
。假设数组 a 中第 0 个元素的地址为 1000,那么每个一维数组的首地址如下图所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8V4NRlep-1607311364582)(20201120-C语言-二维数组.assets/image-20201120160640958.png)]
为了更好的理解指针和二维数组的关系,我们先来定义一个指向 a 的指针变量 p:
int (*p)[4] = a; // 典型应用:行指针指向二维数组
括号中的
*
表明 p 是一个指针,它指向一个数组,数组的类型为int [4]
,这正是 a 所包含的每个一维数组的类型。[ ]
的优先级高于*
,( )
是必须要加的,如果赤裸裸地写作int *p[4]
,那么应该理解为int *(p[4])
,p 就成了一个指针数组,而不是二维数组指针数组名 a 在表达式中也会被转换为和 p 等价的指针!
下面我们就来探索一下如何使用指针 p 来访问二维数组中的每个元素。按照上面的定义:
-
p
指向数组 a 的开头,也即第 0 行;p+1
前进一行,指向第 1 行。 -
*(p+1)
表示取地址上的数据,也就是整个第 1 行数据。注意是一行数据,是多个数据,不是第 1 行中的第 0 个元素,下面的运行结果有力地证明了这一点:
#include <stdio.h> int main(){ int a[3][4] = { {0, 1, 2, 3}, {4, 5, 6, 7}, {8, 9, 10, 11} }; int (*p)[4] = a; printf("%d\n", sizeof(*(p+1))); return 0; }
运行结果:
16*(p+1)+1
表示第 1 行第 1 个元素的地址。如何理解呢?
*(p+1)
单独使用时表示的是第 1 行数据,放在表达式中会被转换为第 1 行数据的首地址,也就是第 1 行第 0 个元素的地址,因为使用整行数据没有实际的含义,编译器遇到这种情况都会转换为指向该行第 0 个元素的指针;就像一维数组的名字,在定义时或者和 sizeof、& 一起使用时才表示整个数组,出现在表达式中就会被转换为指向数组第 0 个元素的指针。*(*(p+1)+1)
表示第 1 行第 1 个元素的值。很明显,增加一个 * 表示取地址上的数据。
根据上面的结论,可以很容易推出以下的等价关系:
a+i == p+i a[i] == p[i] == *(a+i) == *(p+i) a[i][j] == p[i][j] == *(a[i]+j) == *(p[i]+j) == *(*(a+i)+j) == *(*(p+i)+j)
【实例】使用指针遍历二维数组
#include <stdio.h> int main(){ int a[3][4]={0,1,2,3,4,5,6,7,8,9,10,11}; int(*p)[4]; int i,j; p=a; for(i=0; i<3; i++){ for(j=0; j<4; j++) printf("%2d ",*(*(p+i)+j)); printf("\n"); } return 0; }
运行结果:
0 1 2 3 4 5 6 7 8 9 10 11
7.1 指针数组和二维数组指针的区别
指针数组和二维数组指针在定义时非常相似,只是括号的位置不同:
int *(p1[5]); //指针数组,可以去掉括号直接写作 int *p1[5]; int (*p2)[5]; //二维数组指针,不能去掉括号
指针数组和二维数组指针有着本质上的区别:
- 指针数组是一个数组,只是每个元素保存的都是指针,以上面的 p1 为例,在32位环境下它占用 4×5 = 20 个字节的内存。
- 二维数组指针是一个指针,它指向一个二维数组,以上面的 p2 为例,它占用 4 个字节的内存。
- 所有的指针,占用的内存空间大小是一样的
- 但指向的内容是由它的基类型决定的
- 所以void型的指针,可以强转为其它任意类型
-
二维数组的指针,指针构成的数组
2016-09-11 14:19:00第十章使用的大部分是二维数组的指针这种形式,即 int (* pz) [2]; 第十一章出现了指针的数组形式,即 const char *mytal[LIM]; 于是我有点懵逼了,到底这俩货是什么意思,可以互相代替吗?下面自己举例子试试... -
二维数组、指针数组、字符串二维数组
2016-08-15 12:54:00一维数组中 a[i] 中的 a 代表了本数组的首地址,相当于 &a[0]。 因此 *a 就等于 a[0]。 那么对 a 加 1,就可以访问下一位: *(a+1) 就等于 a[1]。 可以看出,指针与数组的关系非常密切。好像两个类型没有什么... -
C++ 二维数组和指针数组
2019-04-28 08:13:43二维数组大家都很清楚,就是该数组包含的元素是一个数组,那么和指针数组又有什么关系呢?那么我先解释一下指针数组是什么东西。 首先指针和普通的变量是用来存放东西的,普通变量呢存放的是值,就是普通的值,而... -
c++ 一维数组长度_每天一点C / 一维数组和指针
2021-01-29 17:54:45哈喽,我是老吴,继续记录我的...养成写作习惯的第一步:放弃追求大段的、固定的、安静的写作时间,培养随时写作的能力。同样的技巧也能应用到技术学习上,工程师也可以放弃追求大段的、固定的、安静的看书写代码的... -
数组指针在一维数组和二维数组中应用
2020-08-02 17:30:19(*p)[n]:根据优先级,先看括号内,则p是一个指针,这个指针指向一个一维数组,数组长度为n,这是“数组的指针”,即数组指针; *p[n]:根据优先级,先看[],则p是一个数组,再结合*,这个数组的元素是指针类型,共... -
C_001-函数返回值类型为指向一维数组的指针
2020-09-11 21:39:481.使用了typedef来替换表示指向一维数组的指针类型,float (*PF)[4]中,PF为指针变量,它所指向 的类型为包含4个float型元素的一维数组,用float (* )[4]来表示,除了变量之外的才是类型,想知道某个变量是什么类型,... -
【C++の相关概念】指针数组 数组指针 二维数组传参
2021-01-18 21:41:53数组指针:是指一个指向数组的指针,它其实还是一个指针,只不过是指向数组而已; int(*a)[10]; //a指向一个列数为10的二维数组 区分方法:主要看后面的两个字是什么(前面是修饰作用),因此指针数组是数组,而... -
二维数组&&指针数组与数组指针
2016-03-27 13:09:45二维数组&&指针数组与数组指针 一、首先我们从字面意思理解一下什么是指针数组什么是数组指针 1、指针数组:本质是一个数组,数组中的每一个元素是一个指针。 2、数组指针:本质是一个指针,而指针指向一个数组。 二... -
二维指针不能操作一维数组的原因是什么?
2013-12-21 13:58:10int array_test[3]={1,2,3}; int ** ptr; *ptr = array_test;//本句出错 printf("%lu\n",ptr);... 出错行已经标注,为什么这种方式会出错,二维指针为什么不能操作一维数组?欢迎大家踊跃发表看法~ -
二维数组名是指针的指针吗?
2019-09-26 23:15:45我们知道一维数组名是常量指针,我们可以将一维数组名赋给一个指针类型再对一维数组进行相关的操作,那二维数组名又是什么?我们这样初始化一个二维数组int A[3][3]={1,2,3,4,5,6,7,8}或者为int A[3][3]={ {1,2,3},{... -
高级指针( 指针数组 数组指针 函数指针 函数指针数组 指向函数指针数组的指针 )
2018-03-08 23:02:45例如,一个一维指针数组的定义:int *ptr_array[10]。数组指针知道指针数组是什么之后,那么数组指针是什么呢?数组指针实际上他是一个指针,我们常见的整型指针:int *p 这是一个能够指向整型数据的指针,浮点型... -
出参传递数组指针_每天一点C / 一维数组和指针
2020-12-06 22:20:09哈喽,我是老吴,继续记录我的...养成写作习惯的第一步:放弃追求大段的、固定的、安静的写作时间,培养随时写作的能力。同样的技巧也能应用到技术学习上,工程师也可以放弃追求大段的、固定的、安静的看书写代码的... -
嵌入式学习DAY8 --- 指针和二维数组,数组指针,指针数组
2021-03-03 20:20:41嵌入式入门学习笔记,遇到的问题以及心得体会! day8 概述: 一,指针和二维数组 ...(1)因为arr+1移动了一个一维数组的大小,如果是int **,那么所指向类型是int *,因此加1应该移动4个字节,很明显不符合 -
(一)二维数组&&指针数组与数组指针
2016-03-28 09:54:00一、首先我们从字面意思理解一下什么是指针数组什么是数组指针 1、指针数组:本质是一个数组,数组中的每一个元素是一个指针。 2、数组指针:本质是一个指针,而指针指向一个数组。 二、我们该怎么区分指针数组... -
指向数组的指针和二维数组
2015-08-05 11:43:141.指针的值代表其指向对象的起始地址,指针的...2对于一维数组:若int a[10],则a为该数组的首个元素的常量指针,指向a[0],可通过首地址加偏移量的方法对其它元素进行寻址。a[i]的地址应为a+i。而&a则为指向有10个整型 -
剖析C/C++二维数组与指针的关系
2017-05-09 10:23:47例子: 1> 该二维数组在内存中的存储结构 想象中的结构是表格类型的: 实际内存中的存储结构是连续的两个长度为3...首先,arr[0]与arr[1]都是指针,分别指向第一个和第二个一维数组的首元素地址,示意图如下: -
数组指针、指针数组、二维数组如何理解与使用
2019-08-20 23:05:35数组指针 1.什么是数组指针: 首先它是一个指针,它指向一个数组,在32位系统下任何类型的指针所占内存大小都为4个字节,至于它指向的数组占多少字节,具体要看数组的大小。...(1)对于一维数组: int main(void)... -
c语言二维数组的指针问题
2015-04-15 01:58:52在WinTC下运行如下代码 #include void main() { char a1[][5]={"ab","cd","ef","ghi","sfs"};... * (a1+3) 与 * *(a1+3)的差别是什么,为什么前者只能使用%s输出,它们指向的不都是同一块地址么? -
二维数组与指针怎么理解???
2018-12-04 05:40:16请问,int(*matrix)[100]与int*matrix[100]的区别究竟是什么呢? 我知道后者是一个长度为100的指针数组,但是怎么把前者理解为一个二维数组呢? 谢谢哦! -
类内定义指向二维数组的指针
2013-07-12 10:47:00class A{ private: ...我已经定义N为const了,为什么C++仍然不允许我定义一个(指向列数为N的二维数组的)指针? 在创建一个对象实例时,肯定是要为N初始化的,而且一但初始化后其值是不能改变的, -
C语言中二维数组指针的简要说明
2021-01-01 05:58:58C语言中,指针是一个复杂但又灵活多变的知识点,我们知道,在一维数组中,对于一个数组a[],*a,a,&a,都表示a的首地址,但如果与二维数组混合使用,就显得更为复杂了。例如对于一个二维数组 a[2][4]={{1,2.3},{4... -
【补习】二维数组,指针,函数
2016-10-15 12:15:30学习链表的时候一脸懵逼,发现需要从二维数组与指针开始补习,从零开始的代码学习笔记第一篇,希望今后也能坚持下去。 ————————————————...一个没什么特别的二维数组,我的学习目的是弄清楚a[0],a[2 -
二维数组行指针的问题
2015-01-21 08:25:28*(pa+1)[1]是什么啊 为什么*(pa+1)[0]是5呢 那个方括号里的数字指什么呢 我真是彻底晕了 求指教~ #include void main() { static int a[3][4]={1,2,3,4,5,6,7,8,9,10},(*pa)[4]=a; cout*(pa+1)[1]; } ``` ... -
二维数组与一维数组在应用中的区别
2018-04-19 20:36:24下面我们将一维和二维数组将以区分:由此我们知道一维数组中arr它是一个指向整型元素的指针,而brr它是一个指向整型的一维数组的指针(数组指针),而不是指向整型元素元素地址的指针,因此我们在使用二维数组作为函数... -
菜鸟学C++——多(二)维数组与指针的区分
2018-09-03 18:22:06二维数组的地址有着丰富的等价形式,今天就让用实际的例子来将这个概念区分清楚! 首先,我们大多数人清楚的是,对于一位数组 int a[10]: a 是数组名,是数组的地址,是元素a[0]的地址; 有 a=&a[0]; ...