讲到第五章了,数组两个字还离不开我们的左右,数组的内容也真多,另一方面也因为数组与指针的关系的确非常密切。
通常,对于int a[8][9]这个二维数组,我们可以这样定义一个指向它的指针:
int (*p)[9];
这个声明的形式跟人们所熟悉的int *p的形式大相庭径,初学者通常会感到迷惑,不理解的地方大致有四个:
1。为什么会以这种形式声明?
2。(*p)应该如何理解?
3。为什么必须把第二维显式地声明?
4。为什么忽略第一维?
下面我们就一起逐个讨论这四个问题:
1。这种形式是C标准的声明语法规定的,由于本章不是对标准的解释,只是对标准的应用,因此笔者尽量以简洁的方式解释这个声明,详细的讨论将在第七章进行。C标准的声明包含了两部分:
声明:
声明说明符 初始化声明符表opt (opt的意思是可选)
在声明说明符里面有一项类型说明符,int就是这种类型说明符。而初始化声明符表里面的其中一种形式,就是:
直接声明符 [常量表达式opt]
(*p)[9]就是这种直接声明符加[]的形式。
2。p左边的*在这里不是取值运算符,而是一个声明符,它指出p是一个指针。而()括号是不能去掉的,如果去掉了,由于[]运算符优先级比*高,p就会先跟[]结合,这样p就变成了一个指针数组,而不是指向数组的指针。
题外话:
*p还有一种用法,就是当*是取值运算符的时候,*p是一个左值,表示一个变量,为什么*p是一个变量呢?也许有人会说,因为int
i,
*p=&i嘛,其实这是结果不是原因。严格来说,i只是一个变量名,不是变量,在编译器的符号表里面,变量名是一个符号地址,它所代表的地址值是它指向的那段内存单元的地址,真正叫变量的是那段内存单元,懂汇编的朋友能很容易地区分出来,在汇编里面,可以这样定义一个变量名:
VARW DW 10,20
VARW就是一个变量名,它在汇编里面是一个地址,代表了10所在的内存单元这个变量。由于p被初始化为&i,*p指向i所代表的那段内存单元,因此说*p是一个变量。把i称为变量是一种习惯上的统称。
3。定义一个指针的时候,首先必须定出指针的类型,由于这是一个指向数组的指针,如果数组的元素的类型定下来了,那么这个指针的类型也就定下来了。前面说过,C语言的多维数组实质上是数组的嵌套,那么所指向数组的元素必定具有数组类型,也就是说,这个数组的元素是一个具有6个int元素的数组,因此,p定义的时候,必须指定第二维的上界,这样才能把p的类型定下来。
4。有这种疑问的人已经犯了一个错误,没有分清楚什么是指针,什么是数组,以数组的思维模式来看待这个指针p。定义一个数组(非static)的时候,需要在栈中静态分配一块内存,那么就需要知道这块内存的大小,因此定义数组时需要确定各维的上界。而这里只是定义一个指针而已,对于一个指针的定义,需要知道的是它所指向对象的类型,并不需要知道对象的大小,这是多余的。因此,所有指向数组的指针的第一维被忽略。
以上介绍了如何声明一个指向二维数组的指针,类似地,对一个指向n维数组的指针也可以用同样的方法来声明,如下:
int (*p)[x2][x3]......[xn];
同样可以忽略第一维,而其它维必须指定上界。
最后再讨论一种很常见的对多维数组的错误理解,有些人常常会以为,二维数组就是二级指针,这种错误的根源,来自于可以把一个二级指针int
**p以p[i][j]这种形式使用。首先把数组称为指针就是错误的,第一章笔者已经说明了数组名是地址,不能理解为指针。第二,并非能以p[i][j]这种形式使用,那么p就是一个二维数组了,C标准对数组引用的规定,并没有指定数组引用时[]运算符的左边必须是数组名,而可以是一个表达式。第三,这是一种“巧合”,归根到底是由于C语言的数组实现是数组的嵌套同时C标准把[]运算符转换为类似*(*(a+i)+j)这样的等价表达式造成的,那两个取值运算符“恰好”可以用于一个二级指针。第四,p与p[i]并不具有数组类型,sizeof(p)和sizeof(p[i])的结果只是一个指针的大小4字节。而对于一个真正的数组,p与p[i]都是具有数组类型的地址。
实际上,int
**p只是一个指向一维指针数组的指针,而不是指向二维数组的指针。同样地,对于n级指针,都可以看作一个指向一维指针数组的指针,这个指针数组的元素都是n-1级指针。
分享到:
相关推荐
当我们谈论“数组指针”时,实际上是指一个指针变量指向数组的首元素地址。这种概念在处理大型数据集或者需要高效地遍历数组时非常有用。 数组本身在内存中是连续存储的,每个元素都有自己的地址。当我们声明一个...
在第四章“数组与指针”中,我们首先了解了数组及其简单应用。数组是一种数据结构,允许程序员存储一组具有相同类型的值。在C++中,数组的定义通常包括类型、数组名和数组的大小。例如,`int a[10];`定义了一个包含...
- 指针可以指向数组的任意位置,而数组名始终指向数组的第一个元素。 2. **灵活度**: - 指针提供了更大的灵活性,可以通过指针指向不同类型的数据结构。 - 数组则更加固定,一旦声明,其大小和类型就不可改变。...
在 Turbo C 中,可以定义指向一个由 n 个元素所组成的数组指针,例如 int (*p)[3];。这种数组指针不同于整型指针,进行地址加 1 运算时,地址值增加了 6(放大因子为 2x3=6)。 对于指向二维数组的指针,需要了解...
例如,如果有一个整型数组`int a[5]`,那么`int (*p)[5]`就是一个数组指针,它可以存储数组`a`的地址。当我们对数组指针进行解引用操作`*p`时,会得到整个数组,而不是数组的第一个元素。这意味着`*p`和`a`是等价的...
数组指针是一种特殊的指针类型,它指向的是一个数组(通常是一维数组)。这种类型的指针可以用来存储整个数组的地址,从而可以通过该指针访问到数组中的所有元素。 例如,在C语言中,可以定义一个数组指针`int (*p)...
在这种情况下,`p`不直接指向整个数组,而是指向数组中的五个指针。 二、数组元素的指针 1. 定义指针变量:可以声明一个指针变量指向数组中的元素,如`int *p=arr;`或`int *p=&a[0];`。注意,数组名`arr`在这里...
- 数组指针是指向数组的指针,它存储的是数组的首地址。例如,在二维数组中,如果定义了一个指向一维数组的指针,则可以认为这是一个数组指针,它可以指向二维数组中的每一行。 - 使用示例:`int (*ptr)[N];` 其中...
这里,pa是一个整型指针,指向数组a的第一个元素。 五、指针的地址运算 指针的地址运算是指针的基本操作之一,它可以实现指针的加减运算,比较运算等。例如,int a[10]; int *pa = a; pa = pa + 2;这里,pa的值将...
例如,定义一个整型数组`int arr[5]`,则`arr`就是指向数组第一个元素(`arr[0]`)的指针。但是,数组名`arr`不是一个普通的指针,它不能像普通指针那样进行自增操作,因此不能通过`arr++`来遍历数组。 数组指针是...
例如,对于`int arr[5]`,`arr`是一个指向整型数据的指针,它指向数组的第一个元素。这意味着,我们可以使用指针对数组进行遍历。例如,`int *ptr = arr;`然后通过`ptr++`逐个访问数组元素。 指针的运算包括指针的...
例如,如果我们有一个数组`int arr[5]`,那么`arr`就是一个指向数组第一个元素的指针,即`int* arr`。这意味着,我们可以使用指针运算符`[]`来访问数组元素,如`*(arr + 2)`等价于`arr[2]`。 字符串在C++中通常是以...
` 就定义了一个整型数组 `a`,其名称 `a` 就是一个指向数组首元素的指针。 选择题 [8.1] 若有定义:`int x,*pb;` 则在以下正确的赋值表达式是 `pb=&x`。因为 `&x` 是变量 `x` 的内存地址,所以 `pb` 将存储变量 `x...
在C++编程中,数组和指针是两个非常重要的概念,尤其在第五章"数组与指针"中,我们将深入探讨这两个主题。数组是存储同类型数据的有序集合,而指针则是用来存储变量地址的数据类型,使得我们可以直接操作内存。 5.1...
本章主要讨论了数组、指针以及字符串的基本概念和使用方法。 数组是存储相同类型元素的集合,可以通过索引来访问每个元素。例如,数组A[10][5][15]是一个三维数组,它有10个元素,每个元素本身又是一个含有5个元素...
数组名其实就是一个指向数组首元素的指针。例如,当我们声明一个整型数组`int arr[10]`,`arr`就成为指向第一个元素(`arr[0]`)的指针。我们可以使用数组指针来遍历整个数组,这在处理大型数据集时非常有用。例如:...
数组指针是指向数组的指针,它指向数组的首地址。在处理二维数组时,数组指针可以非常有用。 **示例:** ```c int (*ptr)[5]; ``` 这里,`ptr`是一个指向包含5个整数的一维数组的指针。 #### 四、实例分析 在...
表示将数组 a 的第 k+2 个元素的值赋给数组指针 p 所指向的元素。 指针数组是数组中的每个元素都为指针的数组。例如,unsigned char *p[3];其中,p 是一个包含 3 个指针元素的数组。每个指针元素可以指向不同的...
数组名在大多数情况下可以被看作指向数组第一个元素的指针。例如,`int *p = A;`在这里,`p`指向`A`数组的第一个元素。通过指针可以遍历整个数组,例如`for (int i = 0; i ; i++) printf("%d ", p[i]);`。 数组和...