在C/C中,我们知道指针。也就是说,一个可变的内存地址。
比如,下面的代码不会编译,但其思想是:
#include <iostream>
using namespace std;
main() {
int* pi;
int i = 1;
pi = &i;
while (true) {
cout<<"i = " << i++ << "; pi = " << pi;
pi = π
}
}
地址的地址我们能走多远。“最终地址”在哪里?
int* pi = &i;
int** ppi = π
int*** pppi = &ppi;
int**** ppppi = &pppi;
.....
在这个结构中,没有理论限制,因为您需要不同变量的地址:
int* pi = &i;
int** ppi = π
int*** pppi = &ppi;
int**** ppppi = &pppi;
.....
你有一个变量i
。您有一个变量pi
,它包含i
的地址。您有一个变量ppi
,其中包含变量pi
的地址。您有一个变量pppi
,其中包含变量ppi
的地址。等等这可以无限期地持续下去,尽管在实践中当然不需要这样做。
为了好玩,这里有一个例子,可以使用模板生成长指针类型。编译器将生成具有数百级间接寻址的类型。
#include <iostream>
template<size_t N, typename T>
struct PointerVariable
{
T* myPtr;
PointerVariable<N - 1, T*> ptrToMyPtr;
PointerVariable(T* p) :
myPtr(p),
ptrToMyPtr(&myPtr)
{
// display pointer value
std::cout << "ptr:" << ((void*)myPtr) << std::endl;
}
};
template<typename T>
struct PointerVariable<0, T>
{
PointerVariable(T* p) {
int a = 3.3; // generate a compiler warning so we can see the names of types
}
};
int main()
{
int val = 0;
auto pp = PointerVariable<499, int>{ &val };
return 0;
}
在MSVC 2019上,这会产生一个编译器警告:
warning C4244: 'initializing': conversion from 'double' to 'int', possible loss of data
message : while compiling class template member function 'PointerVariable<0,T *>::PointerVariable(int ********************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************)'
当N=500时,会发生编译器错误:
fatal error C1202: recursive type or function dependency context too complex
使用显式类型,VC 2019允许1491级间接寻址:
int
******************************************************************************************************************************** // 128
******************************************************************************************************************************** // 128
******************************************************************************************************************************** // 128
******************************************************************************************************************************** // 128
******************************************************************************************************************************** // 128
******************************************************************************************************************************** // 128
******************************************************************************************************************************** // 128
******************************************************************************************************************************** // 128
******************************************************************************************************************************** // 128
******************************************************************************************************************************** // 128
******************************************************************************************************************************** // 128
**************************************************************** // 64
**************** // 16
*** // 3
x = nullptr;
再添加一个指针级别会发出:
fatal error C1026: parser stack overflow, program too complex
这是对编译器在变量存储和类型复杂性方面能力的一次练习。忽略这两个限制,例如只使用uintptr_t
而不是真正的指针类型,那么您的限制就是系统内存。
理论上没有限制,但请记住:
>
您不能合法使用pi=
读取或取消引用未初始化的指针的行为是未定义的。
读取用于指向有效内存但不再指向有效内存的指针的行为是不确定的。但它并不是没有定义的。因此,伪代码将产生无限多个悬空指针的事实是没有意义的。
顺便说一下,您可以使用模板设计一个工作示例(将所有指针保持在范围内),就像您可以使用模板实现阶乘函数一样。
有三种典型的情况适合使用智能指针: 资源所有权的共享 要编写异常安全的代码时 避免常见的错误,如资源泄漏 共享所有权是指两个或多个对象 需要同时使用第三个对象的情况。这第三个对象应该如何(或者说何时)被释放?为了确保释放的时机是正确的,每个使用这个共享资源的对象必须互相知道对方, 才能准确掌握资源的释放时间。从设计或维护的观点来看,这种耦合是不可行的。更好的方法是让这些资源所有者将资源的生存期管理
指针可以指向一份普通类型的数据,例如 int、double、char 等,也可以指向一份指针类型的数据,例如 int *、double *、char * 等。 如果一个指针指向的是另外一个指针,我们就称它为 二级指针,或者 指向指针的指针。 假设有一个 int 类型的变量 a,p1是指向 a 的指针变量,p2 又是指向 p1 的指针变量,它们的关系如下图所示: 将这种关系转换为C语言代码: 指针变
如果C中有指针(char*names[])和指向指针的指针(char**cur_name=names);有指向指针的指针吗? 或者指向指针的指针只是一个链表?也许这是个愚蠢的问题,但我想知道答案。
指针 (pointer)是一个包含内存地址的变量的通用概念。这个地址引用,或 “指向”(points at)一些其他数据。Rust 中最常见的指针是第四章介绍的 引用(reference)。引用以 符号为标志并借用了他们所指向的值。除了引用数据它们没有任何其他特殊功能。它们也没有任何额外开销,所以应用的最多。 另一方面,智能指针(smart pointers)是一类数据结构,他们的表现类似指针,但
在下面给出的代码中,我声明了一个指向int的指针,我们都知道memcpy返回一个指向目标字符串的空指针,所以如果ptr是指向int的指针,那么为什么printf(“%s”,ptr);是完全有效的,ptr毕竟不是指向char的指针。
6. 指向指针的指针与指针数组 指针可以指向基本类型,也可以指向复合类型,因此也可以指向另外一个指针变量,称为指向指针的指针。 int i; int *pi = &i; int **ppi = π 这样定义之后,表达式*ppi取pi的值,表达式**ppi取i的值。请读者自己画图理解i、pi、ppi这三个变量之间的关系。 很自然地,也可以定义指向“指向指针的指针”的指针,但是很少用到: int