我有一个问题,要求我在两个数组元素地址之间查找字节中的偏移:
double myArray[5][7];
如果c在列中存储数据,则
&myArray[0][0]
的&myArray[3][2]
的偏移量(以字节为单位)为:
按列主要顺序,我认为元素将被布局:
[0][0] -- [1][0] -- [2][0] -- [3][0] -- ..... -- [3][2]
因此,在我看来,要偏移字节是计算[0] [0]和[3] [2]之间的跳跃次数,而time则是8个,因为它是双打的数组。但是,让我感到困惑的是,它要求使用&操作员。这会以某种方式改变答案,因为它在两个地址之间提出要求,还是该过程仍然相同?我认为是一样的,但我不是100%确定的。
如果我的想法是正确的,那么这是8*15字节?记忆。(基于您的问题)
int x[2][3] = {{0,1,2},{3,4,5}};
将在(您的问题)中布置
--+--+--+--+--+--+
0| 3| 1| 4|2 |5 |
--+--+--+--+--+--+
但是在C
中,这是像
--+--+--+--+--+--+
0| 1| 2| 3|4 |5 |
--+--+--+--+--+--+
现在您是绝对正确的,您可以考虑在[0][0]
和[3][2]
之间跳跃,但是有一种更好的方法可以做到这一点,而无需考虑所有这些,您可以确定它们的偏移将是他们的地址差异。
您可以简单地获取他们的地址并减去它们。
ptrdiff_t ans = &a[3][2]-&a[0][0]
;(这基本上是两个元素之间的差距)
产生答案。printf("answer = %td",ans*sizeof(a[0][0]);
(一个GAP = sizeof(a[0][0])
)[在您的情况下double
]
甚至更好的方法是
ptrdiff_t ans = (char*)&a[3][2] - (char*)&a[0][0];//number of bytes between them.
我会说明为什么char*
在这里很重要:
(这还不够一般)(char*)&a[0][0]
和 &a[0][0]
都包含相同的东西 value-wise 。
,但在指针算术中很重要。(解释不同)。
不使用铸件时,解释是数组元素的数据类型。这意味着现在考虑double
s的差异。当您施放它时,它将结果吐出或差异 char
-s。
为什么这有效?因为所有数据存储器均为byte
可寻址,并且char
为单个字节。
这比预期的要多,首先让我们看看C
中的数组是什么?†
C实际上没有多维数组。在C
中,它被实现为数组数组。是的,这些多维数组元素以行订单存储。
要澄清一下,我们可以研究标准§6.5.2.1
的示例考虑声明定义的数组对象
int x[3][5];
此处
x
是int
s的3 x 5
数组;更准确地说,x
是 三个元素对象,每个对象是五个int
S 的数组。在里面 表达式x[i]
,相当于(*((x)+(i)))
,x
是第一个 转换为指针转换为五个ints 的初始数组。那么i
是 根据x
的类型调整,从概念上讲 将i
乘以指针指向对象的大小, 即五个int对象的数组。添加了结果, 间接方向用于产生五个int的阵列。使用时 表达式x[i][j]
,该数组依次转换为指针 到int
s的第一个,因此x[i][j]
产生int。
所以我们可以说double myArray[5][7];
myArray[3][2]
和myArray[0][0]
不是同一数组的一部分。
现在我们在这里完成了 - 让我们进入其他事情:
来自标准§6.5.6.9
减去两个指针时,均应指向 相同的数组对象或超过数组对象的最后一个元素; 结果是两个数组的下标的差 元素。
但是,这里的myArray[3]
和myArray[0]
表示两个不同的数组。这意味着myArrayp[3][2]
和myArray[0][0]
都属于不同的数组。他们不是最后一个元素。因此,减法&myArray[3][2] - &myArray[0][0]
的行为将不由标准定义。
† eric(Eric Postpischil)指出了这个想法。
在行遍历中,声明为 array[height][width]
,用法为 array[row][column]
。在Row-Major中,除非超过下一行的宽度和"包裹",否则步入下一个数字会为您提供下一个列。每行将width
添加到您的索引,每列添加1,使行是"主要"索引。
为了获得列等效,您假设下一个值是下一个行,当行超过高度时,它将其"包装"到下一个列。这是由index = column * height + row
。
so,对于高度5的数组array[5][7]
5,索引[3][2]
得出2*5 + 3 = 13
。
让我们使用一些代码进行验证。您只需切换索引的顺序即可获得列的行为。
#include <stdio.h>
int main() {
double array[7][5];
void *root = &array[0][0];
void *addr = &array[2][3];
size_t off = addr - root;
printf("memory offset: %d number offset: %dn", off, off/sizeof(double));
return 0;
}
运行此程序的地址偏移量为104或13双。
编辑:对不起,错误的答案
简单答案
C没有多维数组,因此我们必须将double myArray[5][7]
解释为一维数组的一维数组。在double myArray[5][7]
中,myArray
是5个元素的数组。这些元素中的每一个都是7 double
的数组。
因此,我们可以看到myArray[0][0]
和myArray[0][1]
都是myArray[0]
的成员,并且它们是相邻的成员。因此,元素继续进行[0][0]
,[0][1]
,[0][2]
等。
当我们考虑myArray[1]
时,我们会看到它是在myArray[0]
之后的。由于myArray[0]
是7 double
的数组,myArray[1]
在myArray[0]
之后启动7 double
。
现在,我们可以看到myArray[3][2]
是3个数组(7 double
)和myArray[0][0]
后的2个元素(double
)。如果double
为8个字节,则此距离为3•7•8 2•8 = 184字节。
正确答案
令我惊讶的是,我找不到C标准中的文本,这些文本指定 n 元素数组的大小等于 n 乘以一个元素的大小。直觉上,这是"显而易见的" - 直到我们认为没有平坦地址空间的体系结构中的实现可能会有一些问题,需要它以复杂的方式访问阵列。因此,我们不知道7 double
的数组的大小是多少,因此我们无法计算myArray[3][2]
距myArray[0][0]
的距离。
我不知道有任何C实现,其中 n 元素数组的大小不是一个元素的大小,因此计算将起作用在所有正常的C实现中,但我不认为一定是根据C标准。
计算程序中的距离
已经建议使用(char *) &myArray[3][2] - (char *) &myArray[0][0]
计算地址。尽管这并不是严格符合c,但它将在常见的C实现中起作用。它是通过将地址转换为char
的指针来起作用的。然后减去这两个指针,然后以char
的单位(为字节)给出它们之间的距离。
使用uintptr_t
是另一个选择,但是我将省略对此的讨论及其警告,因为此答案已经太长了。
计算距离的错误方法
人们可能会认为&myArray[3][2]
是double
的指针,&myArray[0][0]
是double
的指针,因此&myArray[3][2] - &myArray[0][0]
是它们之间的距离,以double
的单位测量。但是,标准要求减去指针必须指向同一数组对象的元素或一个超过最后一个元素的元素。(也为此,一个对象可以充当一个元素的数组。)但是,myArray[3][2]
和myArray[0][0]
不在同一数组中。myArray[3][2]
在myArray[3]
中,myArray[0][0]
在myArray[0]
中。此外,它们都不是myArray
的元素,因为其元素是数组,但是myArray[3][2]
和myArray[0][0]
是double
,而不是数组。
鉴于此,人们可能会问如何期望(char *) &myArray[3][2] - (char *) &myArray[0][0]
工作。是否也将指针减去不同阵列中的元素?但是,角色类型是特殊的。C标准说,字符指针可用于访问代表对象的字节。(从技术上讲,我看不到标准说这些指针可以减去这些指针 - 只能说将对象的指针转换为指针转换为角色类型,然后连续递增以指向对象的其余字节。但是,我认为这里的目的是指向对象字节的字符指针,就像对象的字节是一个数组。)