C++ 双指针有什么真正的用途?

C++ 双指针有什么真正的用途?,c++,pointers,C++,Pointers,我一直在寻找答案,但找不到任何我真正“得到”的东西 我对C++非常陌生,无法用双、三指针等来理解我的头脑。他们的目的是什么 可以有人启发我< /P> < P>使用指针,例如,如果你想通过引用传递一个方法指针。 老实说,在写得好的C++中,你应该很少看到库代码之外的 T**。事实上,你拥有的明星越多,你就越接近胜利 这并不是说永远不需要指向指针的指针;您可能需要构造指向指针的指针,其原因与构造指向任何其他类型对象的指针的原因相同 特别是,我可能希望在数据结构或算法实现中看到这样的东西,当您在动态分

我一直在寻找答案,但找不到任何我真正“得到”的东西

我对C++非常陌生,无法用双、三指针等来理解我的头脑。他们的目的是什么


可以有人启发我< /P> < P>使用指针,例如,如果你想通过引用传递一个方法指针。

老实说,在写得好的C++中,你应该很少看到库代码之外的<代码> T**<代码>。事实上,你拥有的明星越多,你就越接近胜利

这并不是说永远不需要指向指针的指针;您可能需要构造指向指针的指针,其原因与构造指向任何其他类型对象的指针的原因相同

特别是,我可能希望在数据结构或算法实现中看到这样的东西,当您在动态分配的节点周围移动时,也许

但是,一般来说,在这种情况下,如果需要传递指针的引用,您只需要传递(即
T*&
),而不是在指针上加倍,即使这样也应该相当罕见

在堆栈溢出中,您将看到人们使用指向动态分配数据指针数组的指针做可怕的事情,试图实现他们所能想到的效率最低的“2D向量”。请不要受他们的启发


综上所述,你的直觉不是没有优点的。

< P>它在C++中的使用较少。然而,在C语言中,它可能非常有用。假设您有一个函数,它将malloc一些随机内存量,并用一些东西填充内存。必须调用一个函数来获得需要分配的大小,然后调用另一个将填充内存的函数,这将是一种痛苦。相反,您可以使用双指针。双指针允许函数设置指向内存位置的指针。它还有一些其他用途,但这是我能想到的最好的东西

int func(char** mem){
    *mem = malloc(50);
    return 50;
}

int main(){
    char* mem = NULL;
    int size = func(&mem);
    free(mem);
}

您应该/必须了解指针指向指针的一个重要原因。。。就是有时候你必须通过一些API(比如WindowsAPI)与其他语言(比如C)进行交互


这些API通常具有具有返回指针的输出参数的函数。然而,这些其他语言通常没有引用或不兼容(与C++兼容)的引用。这是一个指针指向指针的情况。C++中的

,如果要将指针作为OUT或ION/OUT参数传递,则可以通过引用传递:

int x;
void f(int *&p) { p = &x; }
但是,引用不能(“合法地”)为
nullptr
,因此,如果指针是可选的,则需要指向指针的指针:

void g(int **p) { if (p) *p = &x; }
当然,从C++17开始,您就有了
std::optional
,但是,“双指针”已经是C/C++的惯用代码几十年了,所以应该没问题。另外,用法也不太好,您可以:

void h(std::optional<int*> &p) { if (p) *p = &x) }
这本身就不太好

此外,有些人可能会认为在呼叫站点阅读
g
更好:

f(p);
g(&p); // `&` indicates that `p` might change, to some folks
双指针有什么真正的用途

下面是一个实际的例子。假设您有一个函数,并且希望向其发送一个字符串参数数组(可能您有一个要向其传递参数的DLL)。这可以是这样的:

#include <iostream>

void printParams(const char **params, int size)
{
    for (int i = 0; i < size; ++i)
    {
        std::cout << params[i] << std::endl;
    }
}

int main() 
{
    const char *params[] = { "param1", "param2", "param3" };
    printParams(params, 3);

    return 0;
}
#包括
无效打印参数(常量字符**参数,整数大小)
{
对于(int i=0;istd::cout在许多情况下,
Foo*&
Foo**
的替代品。在这两种情况下,您都有一个地址可以修改的指针

假设您有一个抽象的非值类型,需要返回它,但返回值被错误代码占用:

error_code get_foo( Foo** ppfoo )

现在,可变的函数参数很少有用,因此更改最外面的指针
ppFoo
指向的位置的功能很少有用。但是,指针是可以为空的——因此,如果
get\u foo
的参数是可选的,指针就像可选引用一样

在这种情况下,返回值是一个原始指针。如果它返回一个拥有的资源,它通常应该是一个
std::unique\u ptr*
——处于该间接级别的智能指针

相反,如果它返回一个指向它不共享所有权的对象的指针,那么原始指针更有意义

除了这些“粗略输出参数”之外,
Foo**
还有其他用途。如果您有一个多态的非值类型,则不拥有的句柄是
Foo*
,这与您想要一个
int*
的原因相同,您想要一个
Foo**

这会让你问“为什么你要一个代码< int */COD>?”在现代C++ >代码> int */COD>是一个不可拥有的可改变的引用,引用到<代码> int <代码>。(结构中的引用会在赋值和复制方面产生混淆的语义,特别是与非引用混合时)

有时您可以将
int*
替换为
std::reference\u wrapper
,当然
std::optional
,但请注意,它将是简单
int*
的2倍

因此,使用
int*
是有正当理由的。一旦你拥有了它,当你想要一个指向非值类型的指针时,你就可以合法地使用
Foo**
。你甚至可以通过拥有一个连续的
int*
数组来操作
int**

获得三星级程序员的资格越来越难。现在你需要一个合理的理由(比如)通过间接方式传递
Foo**
。通常在你达到这一点之前,你应该考虑抽象和/或简化你的代码结构

所有这些都忽略了最常见的原因:与C API交互。C没有唯一的
,它没有
span
。它倾向于使用基元类型而不是结构,因为结构需要基于函数的访问
error_code get_foo( Foo** ppfoo )
error_code get_foo( Foo*& pfoo_out )
int x = 123;
int* y = &x;
*y = 456;
int** z = &y;
**z = 789;
struct node { struct node *next; ... };
struct node *first;
void link(struct node *new_node, struct node **list)
{
    new_node->next = *list;
    *list = new_node;
}

void unlink(struct node **prev_ptr)
{
    *prev_ptr = (*prev_ptr)->next;
}
struct node *new_node = (struct node *)malloc(sizeof *new_node);
link(new_node, &first);
T *a[N]; // for any type T
foo( a );
void foo( T **a ) { ... }
void foo( T **ptr )
{
  *ptr = new_value();
}

void bar( void )
{
  T *val;
  foo( &val );
}
T **arr;

try
{
  arr = new T *[rows];
  for ( size_t i = 0; i < rows; i++ )
    arr[i] = new T [size_for_row(i)];
}
catch ( std::bad_alloc& e )
{
  ...
}
int main(int argc, char *argv[])
{
   ...
}
int main(int argc, char **argv)
{
   ...
}