在C语言中模拟访问二维数组的宏

11

OpenCL只使用C99规范提供对单维数组的访问。我的问题涉及到二维数组,我在主机端使用了二维数组。

为了避免通过计算索引来降低代码可读性,我想使用一个C宏来获取元素A[i][j]。不幸的是,我不太擅长这个,并且在C方面缺乏经验。我认为我大概知道如何实现,但如果有人能指出问题,那将不胜感激。

宏可能是这样的:

#define 2d_access(u, y, x) (u[y][x])

该宏的参数为矩阵u、行y和列x,其返回值为u[y][x]的值。

该矩阵是静态分配的,因此该宏将具有WIDTH组件。

#define 2d_access(u, y, x) (u[y * WIDTH] + x])
3个回答

8

由于目前为止所有的答案都依赖于固定宽度,因此这里提供一种适用于任意宽度列的(笨重的)解决方案:

#define matrix_type(t) struct { size_t width; t array[]; }

#define matrix_alloc(t, w, h) malloc(offsetof(matrix_type(t), array[(w) * (h)]))

#define matrix_init(m, t, w, h) \
  matrix_type(t) *m = matrix_alloc(t, w, h); \
  if(!m) matrix_alloc_error(); else m->width = (w);

#define matrix_index(m, w, h) m->array[m->width * (w) + (h)]

// redefine if you want to handle malloc errors
#define matrix_alloc_error()

使用free释放数组即可。

当然,您也可以添加高度字段,并进行边界检查等其他操作。您甚至可以将它们编写为实际函数,或者使用宏自动声明struct类型,这样您就不必为每个东西都使用匿名struct类型。如果您需要将其放在堆栈上,则可以使用alloca,但代价是可移植性。

如果您有恒定的矩阵大小,可以使用一些强制转换技巧来实现“本地”二维索引(通过[]运算符):

#define CAT_(x, y) x##y
#define CAT(x, y) CAT_(x, y)

#define MANGLE(x) CAT(x, _hidden_do_not_use_0xdeadbeef_)

#define matrix_init(m, t, w, h) \
  t MANGLE(m)[(w) * (h)]; \
  t (*m)[(w)] = (void *)MANGLE(m);

// because of the funky typing, `m[0][1]` does what you'd expect it to.

请注意,与其他解决方案不同的是,这个解决方案创建了第二个变量,可能不太干净,但我认为我使用了一种相当清晰的混淆方法,因此在实践中不会妨碍。

感谢您没有使用硬编码的解决方案来污染互联网。 - mcandre

6

更好的做法是为您正在使用的每个数组定义一个宏,这样您可以使其看起来完全像2D数组访问。因此,给定数组A,您应该定义:

#define A(r, c) (A[(r)*WIDTH + (c)])

注意替换值周围的括号。这处理了替换是表达式的情况,比如 A(i + 1, j)。如果没有括号,这将扩展为 A[i + 1*WIDTH + j],这不是你想要的:
A[i + 1*WIDTH + j] = A[WIDTH + i + j] != A[(i + 1)*WIDTH + j]

为了避免第二个参数出现相同的问题,两个参数都被括在替换文本中的括号中。

1
快速问题,宏名称和扩展中使用相同的名称“A”是否安全?从快速测试来看,似乎没问题。 - Hemmer

2

不需要批评,因为你已经提供了解决方案:

#define access_2d(u, y, x) (u[(y) * WIDTH + (x)])

好的,也许我的想法有所不同,但我会将其定义为

// x before y
#define access_2d(u, x, y) (u[(y) * WIDTH + (x)])

那并不是更好,只是一种偏好。

只有在所有数组都具有恒定的宽度时,这才有效,这可能会破坏使用二维数组的初衷。 - Chris Lutz
如果不同的数组具有不同的宽度,将它们封装到一个包含指针和宽度的“结构体”中,并更改宏以使其在该“结构体”上运行可能是值得的。 - Matteo Italia
@Matteo - 这实际上就是我刚刚写的答案。 - Chris Lutz
我假设WIDTH是一个常量。看起来是这样的。 - Rudy Velthuis
@Chris:为什么固定宽度的二维数组是无意义的?我认为,具有固定宽度和高度的二维数组(例如几何变换的4x4矩阵)可以根据问题而定,甚至可能很有用。 - Rudy Velthuis
1
@Rudy - 我想这并不是毫无意义的。只是对于固定宽度,您可以通过 [] 运算符和 type arr[w * h] = {0}; type (*ptr)[w] = (void *)arr; 来实现二维索引,尽管看起来有些复杂,但可以通过宏来隐藏。 - Chris Lutz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接