n 级指针的动态解除引用
Dynamic dereference of a n-level pointer
假设一个n维数组作为模板参数传递并且应该被遍历以将其保存到文件中。首先,我想找出数组包含的元素的大小。为此,我尝试取消引用指针,直到我在 [0][0][0]...[0] 处获得第一个元素。但是我在这个阶段已经失败了:
/**
* @brief save a n-dimensional array to file
*
* @param arr: the n-level-pointer to the data to be saved
* @param dimensions: pointer to array where dimensions of <arr> are stored
* @param n: number of levels / dimensions of <arr>
*/
template <typename T>
void save_array(T arr, unsigned int* dimensions, unsigned int n){
// how to put this in a loop ??
auto deref1 = *arr;
auto deref2 = *deref1;
auto deref3 = *deref2;
// do this n times, then derefn is equivalent to arr[0]...[0], 42 should be printed
std::cout << derefn << std::endl;
/* further code */
}
/*
* test call
*/
int main(){
unsigned int dim[4] = {50, 60, 80, 50}
uint8_t**** arr = new uint8_t***[50];
/* further initialization of arr, omitted here */
arr[0][0][0][0] = 42;
save_array(arr, dim, 4);
}
当我从内存的角度考虑这个问题时,我想执行给定地址的 n 间接加载。
昨天看到一个相关的问题:
Declaring dynamic Multi-Dimensional pointer
这对我也有很大帮助。一条评论指出这是不可能的,因为在编译时必须知道所有表达式的类型。在我的例子中,实际上什么都知道,save_array
的所有调用者在传递之前都会对 n
进行硬编码。所以我认为这可能只是在正确的地方定义我还不能定义的东西。
我知道我正在用 C++ 编写 C 风格的代码,并且可以选择使用 类 等来实现这一点,但我的问题是:是否有可能通过一个实现 n 级指针取消引用迭代或递归方法?谢谢!
为什么不使用像树这样具有多个子节点的数据结构。
假设您需要存储n维数组值,创建一个指向第一维的节点。假设您的第一个维度长度为 5,那么您有 5 个子节点,如果您的第二个维度大小为 10。那么对于这 5 个节点中的每一个,您都有 10 个子节点,依此类推....
类似的东西,
struct node{
int index;
int dimension;
vector<node*> children;
}
遍历树会更容易,也更干净。
首先:你真的需要一个锯齿状的数组吗?你想要某种稀疏数组吗?因为否则,您能否将您的 n 维结构扁平化为一个长数组?这不仅会导致代码更简单,而且很可能也会更高效。
话虽这么说:肯定可以做到。例如,只需使用递归模板并依靠重载来剥离间接级别,直到你到达底部:
template <typename T>
void save_array(T* arr, unsigned int* dimensions)
{
for (unsigned int i = 0U; i < *dimensions; ++i)
std::cout << ' ' << *arr++;
std::cout << std::endl;
}
template <typename T>
void save_array(T** arr, unsigned int* dimensions)
{
for (unsigned int i = 0U; i < *dimensions; ++i)
save_array(*arr, dimensions + 1);
}
您甚至不需要显式指定间接寻址的数量 n
,因为该数量由指针类型隐式指定。
您也可以对 allocate/deallocate 数组执行基本相同的技巧:
template <typename T>
struct array_builder;
template <typename T>
struct array_builder<T*>
{
T* allocate(unsigned int* dimensions) const
{
return new T[*dimensions];
}
};
template <typename T>
struct array_builder<T**> : private array_builder<T*>
{
T** allocate(unsigned int* dimensions) const
{
T** array = new T*[*dimensions];
for (unsigned int i = 0U; i < *dimensions; ++i)
array[i] = array_builder<T*>::allocate(dimensions + 1);
return array;
}
};
就这样,您需要部分特化,因为使用重载的方法仅在可以从参数推断出类型时才有效。由于函数不能部分特化,因此您必须将其包装在 class 这样的模板中。用法:
unsigned int dim[4] = { 50, 60, 80, 50 };
auto arr = array_builder<std::uint8_t****>{}.allocate(dim);
arr[0][0][0][0] = 42;
save_array(arr, dim);
希望我没有漏掉任何东西;公开这么多的间接访问会很快造成巨大的混乱,这就是为什么我强烈建议不要在真实代码中这样做,除非绝对不可避免。此外,到处都是 new
的这种原始用法一点也不好。理想情况下,您将使用 std::unique_ptr
。或者,更好的是,按照评论中的建议嵌套 std::vectors
…
假设一个n维数组作为模板参数传递并且应该被遍历以将其保存到文件中。首先,我想找出数组包含的元素的大小。为此,我尝试取消引用指针,直到我在 [0][0][0]...[0] 处获得第一个元素。但是我在这个阶段已经失败了:
/**
* @brief save a n-dimensional array to file
*
* @param arr: the n-level-pointer to the data to be saved
* @param dimensions: pointer to array where dimensions of <arr> are stored
* @param n: number of levels / dimensions of <arr>
*/
template <typename T>
void save_array(T arr, unsigned int* dimensions, unsigned int n){
// how to put this in a loop ??
auto deref1 = *arr;
auto deref2 = *deref1;
auto deref3 = *deref2;
// do this n times, then derefn is equivalent to arr[0]...[0], 42 should be printed
std::cout << derefn << std::endl;
/* further code */
}
/*
* test call
*/
int main(){
unsigned int dim[4] = {50, 60, 80, 50}
uint8_t**** arr = new uint8_t***[50];
/* further initialization of arr, omitted here */
arr[0][0][0][0] = 42;
save_array(arr, dim, 4);
}
当我从内存的角度考虑这个问题时,我想执行给定地址的 n 间接加载。
昨天看到一个相关的问题: Declaring dynamic Multi-Dimensional pointer
这对我也有很大帮助。一条评论指出这是不可能的,因为在编译时必须知道所有表达式的类型。在我的例子中,实际上什么都知道,save_array
的所有调用者在传递之前都会对 n
进行硬编码。所以我认为这可能只是在正确的地方定义我还不能定义的东西。
我知道我正在用 C++ 编写 C 风格的代码,并且可以选择使用 类 等来实现这一点,但我的问题是:是否有可能通过一个实现 n 级指针取消引用迭代或递归方法?谢谢!
为什么不使用像树这样具有多个子节点的数据结构。
假设您需要存储n维数组值,创建一个指向第一维的节点。假设您的第一个维度长度为 5,那么您有 5 个子节点,如果您的第二个维度大小为 10。那么对于这 5 个节点中的每一个,您都有 10 个子节点,依此类推....
类似的东西,
struct node{
int index;
int dimension;
vector<node*> children;
}
遍历树会更容易,也更干净。
首先:你真的需要一个锯齿状的数组吗?你想要某种稀疏数组吗?因为否则,您能否将您的 n 维结构扁平化为一个长数组?这不仅会导致代码更简单,而且很可能也会更高效。
话虽这么说:肯定可以做到。例如,只需使用递归模板并依靠重载来剥离间接级别,直到你到达底部:
template <typename T>
void save_array(T* arr, unsigned int* dimensions)
{
for (unsigned int i = 0U; i < *dimensions; ++i)
std::cout << ' ' << *arr++;
std::cout << std::endl;
}
template <typename T>
void save_array(T** arr, unsigned int* dimensions)
{
for (unsigned int i = 0U; i < *dimensions; ++i)
save_array(*arr, dimensions + 1);
}
您甚至不需要显式指定间接寻址的数量 n
,因为该数量由指针类型隐式指定。
您也可以对 allocate/deallocate 数组执行基本相同的技巧:
template <typename T>
struct array_builder;
template <typename T>
struct array_builder<T*>
{
T* allocate(unsigned int* dimensions) const
{
return new T[*dimensions];
}
};
template <typename T>
struct array_builder<T**> : private array_builder<T*>
{
T** allocate(unsigned int* dimensions) const
{
T** array = new T*[*dimensions];
for (unsigned int i = 0U; i < *dimensions; ++i)
array[i] = array_builder<T*>::allocate(dimensions + 1);
return array;
}
};
就这样,您需要部分特化,因为使用重载的方法仅在可以从参数推断出类型时才有效。由于函数不能部分特化,因此您必须将其包装在 class 这样的模板中。用法:
unsigned int dim[4] = { 50, 60, 80, 50 };
auto arr = array_builder<std::uint8_t****>{}.allocate(dim);
arr[0][0][0][0] = 42;
save_array(arr, dim);
希望我没有漏掉任何东西;公开这么多的间接访问会很快造成巨大的混乱,这就是为什么我强烈建议不要在真实代码中这样做,除非绝对不可避免。此外,到处都是 new
的这种原始用法一点也不好。理想情况下,您将使用 std::unique_ptr
。或者,更好的是,按照评论中的建议嵌套 std::vectors
…