3

我有以下(不完整的)功能:

/* Populates char* name with the named location of the ith (flat) element
 * of an array with ndim dimensions where the length of each dimension 
 * is already stored in the int* dim.
 * 
 * name: a pointer to where the name should be populated
 * n: the base name of the array
 * dim: an int[] containing the length of each dimension
 * ndim: length of the dim array
 * i: name of the iteration variable being used
 **/
void populateName(char *name, const char *n, int *dim, int ndim, const char *i) {
  strcpy(name, n);
  char *loc = (char*)(name + strlen(n));
  char *curr;
  for (int k = 0; k < ndim; k++) {
    ...
    sprintf(loc, "[%s]", curr);
    loc += strlen(loc);
  }
}

for 循环中的“...”应该包含什么?例如,使用以下命令调用 populateName():

int dim[2] = {3, 4};
char name[1024];
populateName(name, "x", dim, 2, "i");

应该导致类似:

name = "x[i / 3][i % 4]"

或用于访问数组中第 i 个位置的其他有效名称,定义为:

int x[3][4];

上下文:我正在编写一个 C 程序,它生成 C 程序,这些程序根据用户定义的数据类型和 IDL 中编写的规则过滤大量数据。

编辑:返回包含数组中位置/坐标的元组的python函数可能会让我朝着正确的方向前进。特别是以下数组应使每个元素与其在数组中的平面位置相对应(在此处使用 pylab):

In [14]: x
Out[14]: 
array([[[ 0,  1,  2],
    [ 3,  4,  5]],

   [[ 6,  7,  8],
    [ 9, 10, 11]],

   [[12, 13, 14],
    [15, 16, 17]]])

In [15]: x.flat.copy()
Out[15]: 
array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16, 17])
4

1 回答 1

3

解决此类问题的一个好方法是尝试一些示例。考虑下图,它显示了 3D 数组的内存布局x[2][3][5]

描述

我们如何将偏移量 14 转换为位置x[0][2][4]?嗯,首先,我们看到每个x[i]都有 15 (3*5) 个块,所以首先我们通过计算整数除法 14/15 = 0 来确定 14 属于哪个块。所以,偏移量 14 位于内部的某个位置x[0]

我们现在可以应用相同的方法。x[i][j]保存 5 个块,所以偏移量 14 属于块号 14/5 = 2。事实上,正确的计算是 (14/5)%3,我们将看到偏移量 18。最后,x[i][j][k]保存单个块,所以最后一个指数由 14%5 给出。可以这样想:我们正在解释这些内存块,就好像它们在每一步都有不同的大小。首先,我们假设所有内容都分为 15 个元素的块。然后,我们假设所有内容都分为 5 个元素的块。

您可以使用此示例并查看偏移量 18 映射到,x[1][0][3]因为 18/15 = 1; (18/5)%3 = 0,18%5 = 3。

可以看出,一般情况是对于维度n来说,我们把内存布局理解成j块组织的,其中j每个维度的乘积都大于n,所以我们要索引位置(i/j)%n

这是我的实现:

void populateName(char *name, const char *n, int *dim, int ndim, const char *i) {
  strcpy(name, n);
  char *loc = (char*)(name + strlen(n));
  int j;
  int *mul = malloc(sizeof(int)*ndim);
  mul[ndim-1] = 1;
  /* Compute cumulative multipliers array */
  for (j = ndim-2; j >= 0; j--) {
    mul[j] = mul[j+1] * dim[j+1];
  } 
  for (j = 0; j < ndim; j++) {
    loc += sprintf(loc, "[(%s/%d)%%%d]", i, mul[j], dim[j]);
  }
  free(mul);
}

如您所见,它使用了一个累积的乘数数组,其中mul[i]包含大于 的每个维度的乘积i

顺便说一句,你不需要curr;因为sprintf返回打印的字符数,我们只需要移动loc相同的数量。strlen它比在 之后重复调用更有效sprintf

我没有太多时间来测试这个,但是通过我展示的示例,我得到了这个:

x[(i/15)%2][(i/5)%3][(i/1)%5]

这看起来是正确的。这是一个示例程序:

int main()
{
    int dims[] = { 2, 3, 5, 7, 9 };
    char name[1024];
    populateName(name, "x", dims, 5, "i");
    printf("%s\n", name);
    return 0;
}

这打印:

x[(i/945)%2][(i/315)%3][(i/63)%5][(i/9)%7][(i/1)%9]

读取任意 n 维数组变得更加棘手,但原理始终相同。

于 2013-10-18T09:06:03.177 回答