我搜索了很多,但是没有找到我真正理解的答案。
我对c++非常非常新,并且无法理解使用双重、三重指针等的用途。它们有什么意义吗?
有人可以给我启示吗?
T**
,除了库代码。事实上,星号越多,你就越接近赢得某种奖项。T*&
),而不是加倍使用指针,甚至那也应该相当罕见。了解指向指针的重要原因之一是,有时您需要通过某些API(例如Windows API)与其他语言(如C语言)进行接口。
这些API通常具有返回指针的输出参数。但是,那些其他语言通常没有引用或与C ++兼容的引用。这就是需要使用指向指针的情况。
在C++中很少使用它。然而,在C语言中,它可以非常有用。假设你有一个函数将分配一些随机大小的内存并填充该内存。调用一个函数获取需要分配的大小,再调用另一个函数填充内存会很麻烦。相反,您可以使用一个双指针。双指针允许函数将指针设置为内存位置。还有一些其他用途,但这是我能想到的最好的事情。
int func(char** mem){
*mem = malloc(50);
return 50;
}
int main(){
char* mem = NULL;
int size = func(&mem);
free(mem);
}
posix_memalign()
。 - Davislorint x = 123;
x
是一个类型为 int
的变量。它的值是 123
。
int* y = &x;
y
是一个类型为int*
的变量。 x
是一个类型为int
的变量。 因此,&x
是类型为int*
的值。因此我们可以将&x
存储在y
中。
*y = 456;
y
评估为变量 y
的内容。这是一个类型为 int*
的值。对类型为 int*
的值应用 *
将给出类型为 int
的变量。因此,我们可以将456分配给它。那么*y
是什么?它是x
的别名。因此,我们刚刚将456分配给了x
。
int** z = &y;
z
是什么?它是一个类型为int**
的变量。 &y
是什么?由于y
是一个类型为int*
的变量,&y
必须是一个类型为int**
的值。因此,我们可以将其赋值给z
。
**z = 789;
**z
?从内往外看。 z
求值为一个 int**
类型的变量。因此 *z
是一个 int*
类型的变量。它是 y
的别名。因此,这与 *y
是相同的,而我们已经知道它是 x
的别名。双指针就是指向指针的指针。常见用途是用于字符字符串数组。想象一下几乎每个C/C++程序中的第一个函数:
int main(int argc, char *argv[])
{
...
}
这也可以写成
int main(int argc, char **argv)
{
...
}
我认为关于指针的很多抨击都是因为人们从未真正理解它们。人们不理解的东西会被嘲笑。所以它们变得“太难”和“太危险”。我猜,即使是一些所谓有学问的人也提倡智能指针等概念,这些想法也是可以预料的。但实际上,它们是非常强大的编程工具。老实说,指针是编程的魔法,毕竟,它们只是一个数字。
Foo*&
是 Foo**
的替代品。在这两种情况下,您都有一个指针,其地址可以被修改。error_code get_foo( Foo** ppfoo )
或者
error_code get_foo( Foo*& pfoo_out )
Foo**
。通常在达到这一点之前,您应该考虑抽象和/或简化代码结构。unique_ptr
,也没有span
。它倾向于使用原始类型而不是结构,因为结构需要笨拙的基于函数的访问(没有运算符重载)。*
。std::unique_ptr<Foo>*
有什么用处?为什么你还在谈论返回它,而你已经将返回类型与错误代码占据了? - Mikhailerror_code get_foo( std::unique_ptr<Foo>* here )
通过指针参数“返回”其“返回值”,而实际的返回值由 error_code
承载。这种模式在 COM 和 IDL 等内容中得到了正式化,其中不使用异常,语义返回值始终为指针,而语法返回值为错误代码。 - Yakk - Adam Nevraumont使用它是为了拥有一个指向指针的指针,例如,如果您想通过引用传递指向方法的指针。
foo(type*&)
。 - NathanOliverint 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) }
这在调用时有点丑陋,除非你已经有了一个std::optional
或者:
void u(std::optional<std::reference_wrapper<int*>> p) { if (p) p->get() = &x; }
这本身并不是很好。
此外,有人可能会认为在调用站点上阅读g
更好:
f(p);
g(&p); // `&` indicates that `p` might change, to some folks
C/C++
,它只是一种常见的表示方式,表示“C和/或C ++”。此外,问题是“它们的意义是什么”,而不是“我应该使用它们吗”。 - srdjan.veljkovicstd::optional
? - Mikhail#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;
}
const char
指针组成的数组,每个指针都指向以空字符结尾的 C 字符串的开头。编译器将会将你的数组降解为函数参数指针,因此你得到的是 const char **
,即指向第一个 const char
指针数组的指针。由于在此时数组大小已经丢失,因此你需要将其作为第二个参数传递。std::vector<std::string>
- spectrasstruct node { struct node *next; ... };
对于链表节点,以及
struct node *first;
指向第一个元素。所有操作函数都采用struct node **
,因为我可以保证即使列表为空,该指针也是非NULL
的,而且我不需要任何特殊情况来进行插入和删除:
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;
}
如果要在列表开头插入元素,只需传递一个指向first
指针的指针,即使first
的值为NULL
,它也会执行正确的操作。
struct node *new_node = (struct node *)malloc(sizeof *new_node);
link(new_node, &first);
struct list { struct node * head; };
来表达呢?然后使用 struct list *
。更好的是,如果你在某个时刻需要存储更多的东西(例如:列表长度或尾指针),你不必改变所有的签名。 - spectras
argv
,因为它树立了一个非常糟糕的榜样。 - Davislor