我有一个问题要求我找到两个数组元素地址之间的字节偏移量:
double myArray[5][7];
如果C以列主要顺序存储数据,
&myArray[3][2]
的{{1}}的偏移量(以字节为单位)将为:
在列主要顺序中,我认为元素将如下布局:
&myArray[0][0]
所以在我看来,以字节为单位的偏移量是计算[0] [0]和[3] [2]之间的跳跃次数,以及8次的跳跃次数,因为它是一个双精度数组。然而,令我困惑的是,它正在使用&运营商。这会以某种方式改变答案,因为它是在两个地址之间询问还是过程仍然相同?我认为它是一样的,但我不是百分之百确定。
如果我的想法是正确的,那么这将是8 * 15字节?
答案 0 :(得分:3)
2d阵列的内存布局将是一块连续的内存。(基于你的问题)
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]);
(一个差距= 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
是3 x 5
的{{1}}数组; 更准确地说,int
是一个数组 三个元素对象,每个对象都是五个x
s 的数组。在里面 表达式int
,相当于x[i]
,(*((x)+(i)))
是第一个 转换为指向初始数组为五个整数的指针。那么x
就是 根据{{1}}的类型进行调整,这在概念上是必需的 将i
乘以指针所指向的对象的大小, 即一个由五个int对象组成的数组。结果被添加和 间接应用于产生五个整数的数组。用于 表达式x
,该数组又转换为指针 到i
的第一个,所以x[i][j]
产生一个int。
所以我们可以说int
x[i][j]
double myArray[5][7];
和myArray[3][2]
不属于同一个数组。
现在我们已经完成了 - 让我们进入其他方面:
来自标准§6.5.6.9
当减去两个指针时,两者都应指向。的元素 相同的数组对象,或一个超过数组对象的最后一个元素; 结果是两个数组的下标的差异 元件。
但是myArray[0][0]
和myArray[3]
表示两个不同的数组。这意味着myArray[0]
和myArrayp[3][2]
都属于不同的数组。而且他们不是最后一个元素。 因此标准不会定义减法myArray[0][0]
的行为。
† Eric(Eric Postpischil)指出了这个想法。
答案 1 :(得分:0)
在行主要遍历中,声明为array[height][width]
,用法为array[row][column]
。在行专业中,步进到下一个数字会为您提供下一列,除非您超出宽度并且"包裹"到下一行。每行向您的索引添加width
,每列添加1,使行成为" major"索引。
为了得到列主要的等价物,你假设下一个值是下一行,当行超过高度时,它会包裹"包裹"到下一栏。这由index = column * height + row
描述。
因此,对于 height 5的数组array[5][7]
,索引[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: %d\n", off, off/sizeof(double));
return 0;
}
运行此程序会产生104或13倍的地址偏移量。
编辑:抱歉错误答案答案 2 :(得分:0)
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]
在double
之后开始7 myArray[0]
。
现在我们可以看到myArray[3][2]
在double
之后是3个数组(7个double
)和2个元素(myArray[0][0]
)。如果double
是8个字节,那么这个距离是3•7•8 + 2•8 = 184个字节。
令我惊讶的是,我在C标准中找不到指定 n 元素数组的大小等于 n 乘以一个元素大小的文本。直观地说,它是“显而易见的” - 直到我们认为没有平坦地址空间的架构中的实现可能会有一些问题需要它以复杂的方式访问数组。因此,我们不知道7 double
数组的大小是多少,因此我们无法计算myArray[3][2]
与myArray[0][0]
的距离。
我不知道任何C实现,其中 n 元素数组的大小不是 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
,而不是数组。
鉴于此,人们可能会问{1 {}}如何发挥作用。是不是也减去了指向不同数组中元素的指针?但是,字符类型很特殊。 C标准说字符指针可用于访问表示对象的字节。 (从技术上讲,我没有看到标准说这些指针可以被减去 - 它只是说指向对象的指针可以转换为指向字符类型的指针,然后连续递增以指向对象的剩余字节。但是,我认为这里的意图是指向对象字节的字符指针,就好像对象的字节是数组一样。)