指针、& 符号和指针?
Pointers, ampersands and pointers again?
我只是无法理解指针 - 我应该何时以及如何使用指针。无论我阅读了多少关于它的视频和文献,我就是不明白我应该何时以及如何使用它。明明我就是那么厚实...
让我们看看这个例子:
int main()
{
int* ptr = new int(10);
std::cout << ptr << std::endl;
std::cout << *ptr << std::endl;
std::cout << &ptr << std::endl;
}
这将 return 我们这样的值:
000001A68ABB5250
10
000000455ADCF828
这些输出是什么意思?第一个和第三个看起来像是内存中的地址编号,但它们不同,我不明白它们应该做什么?第二个是我赋的值,但是为什么要用指针符号来获取呢?
让我们看看这个:
int main()
{
int ptr = 10;
std::cout << ptr << std::endl;
//std::cout << *ptr << std::endl; //gives "Error(active) E0075 operand of '*' must be a pointer but has type 'int'"
std::cout << &ptr << std::endl;
}
其中分别有returns:
10
000000D9242FF5F4
上面的代码与此示例代码有何不同?为什么我不必使用指针来获取值? & 号向我们展示了内存地址,对吗?
基本上有人可以用最愚蠢、最容易理解的方式向我解释指针的意义是什么以及为什么我们在 C++ 中需要它们。
如果我们“绘制”第一个示例,它将是这样的:
+------+ +-----+ +--------------+
| &ptr | --> | ptr | --> | *ptr int(10) |
+------+ +-----+ +--------------+
所以 ptr
指向一个 int
值(初始化为 10 10
)。 &ptr
指向变量 ptr
.
在第二个示例中,变量名称 ptr
具有误导性,因为它不是指针。这是一个普通的 int
变量。
画出来就会像
+------+ +-------------+
| &ptr | --> | ptr int(10) |
+------+ +-------------+
当我第一次开始编程时,指针是我刚刚接受它们工作的东西之一,并最终积累了足够的经验 learn/understand 它们。
在我的一篇 类 中,一位教授说当你想修改一个对象而不是复制它时最好使用指针。按值传递给函数会创建在其中传递的对象的副本。
首先要理解的是符号*
和&
的含义取决于上下文。
在int* ptr
中,*
表示“这是一个指针”。
但除此之外(例如在 cout << *ptr
中),*
表示“取消引用”指针。 IE。给定一个指向 X 的指针,*
returns X,不管它是什么。
&
(例如在 cout << &ptr
中)“获取地址”。给定 X,它会为您提供指向 X 的指针。
&
和*
(第二种意义上)是相反的运算,它们相互抵消。
int* ptr = new int(10);
有点 类似于 int x = 10; int *ptr = &x;
,这反过来意味着 int x = 10; int *ptr; ptr = &x;
(不是 *ptr = &x
,因为 *
在这里意味着不同的东西,见上文)。
因此,给定 int x = 10;
,&x
是一个“指向 x 的指针”(打印它会给您一个地址)。在你执行 int *ptr = &x
之后,ptr
也是一个“指向 x 的指针”,因此打印它会显示相同的地址。
*ptr
给你 x
,就像 *&x
(这意味着 *(&x)
)一样,因为 *
和 &
取消了每个其他的。
指针可以指向其他指针。 &ptr
给你一个“指向 ptr
的指针”,或“指向 x 的指针”,其类型为 int **
。 **&ptr
因此 return x
的值。
我不是一个很好的解释者,但总体概述是:
声明和定义后int* ptr = new int(10);
当你使用ptr时,它是int*
类型的名称,如int x = 5
,当你使用x时,它是int类型。
ptr
是一个指针所以,它是一个存储在地址中的变量,所以当你打印它时,它就是一个地址。
&ptr
将是指针的地址,是的,指针也有地址。基本上,它是 int**
类型,所以打印时也是一个地址。
*ptr
取消对指针的引用,因此它将获得存储在指针指向的内存位置中的值 10。可以把它想象成,每个地址都指向一个存储值的内存位置。
如果它只是普通的 int ptr = 10
,您不能取消引用它:应用 *ptr
因为它是 int
类型而不是 int*
的指针。
但是,如果您将 &ptr
应用于它,它将获得 int ptr
的地址,该地址本质上是 int*
.
类型
例如,如果您这样做:int x = 5; int *ptr = &x;
,
如果您取消引用 ptr
,并执行:cout << *ptr
,它将打印 5.
我第一次也遇到了困难。没关系。我花了一个月左右的时间来解决这个问题。事实上,我仍然有一些事情让我对指针感到困惑。哦,好吧,我不太会解释,大概和你看的差不多吧……祝你好运。
简介:
int* ptr = new int(10);
ptr(int)就是你在右边分配的 int 的内存地址(一个数字)。
std::cout << ptr << std::endl;
然后是打印出新int的内存地址。这是因为 ptr 只是一个数字 恰好是一个内存地址。
std::cout << *ptr << std::endl;
这里你告诉C++访问ptr所在编号的内存地址。因为ptr是int的内存地址,所以会得到int的值。
std::cout << &ptr << std::endl;
最后,你告诉C++给你ptr的内存地址。如果您使用 *&ptr,那么您将得到与您的第一个打印语句相同的结果。
就后半部分而言,int在这里只是一个值,而不是指针。当 C++ 说您不能使用 * 时,它可以防止您不小心将 int 视为内存地址。但是,如果您真的想将 int 视为内存地址,
int* x = 10
将允许 *x
编译。现在因为这是在内存地址 10 访问一个 int(这不是你程序的一部分)你的 OS 几乎肯定会杀死你的程序。
真正有趣的问题是 'why pointers?' 如果你明白了,那么剩下的可能会随之而来。
旁注,C++ 有很多结构,它们构成了很多这样的东西 'hidden',但是 我描述的内容仍在幕后进行
有几种场景可以使用指针
#1 传大事
想象一下你有这个
struct Biggy{
int Widle[500];
char Froodle[500];
}
这是一个大对象,复制起来很昂贵,你有一个对 Biggy 对象进行操作的函数,比如说它比较 Widle 和 Froodle 数组。
bool AreTheSame(Biggy b) {....}
c 和 c++ 的工作方式是参数按值传递,这意味着它们从调用者复制到被调用函数
所以当我这样做时
Biggy b = MakeAndFillBiggy(); // we will come back to this function
bool same = AReTheSame(b);
复制所有 1000 个字节,这很昂贵
但如果我有
bool AreTheSame(Biggy *b) {....}
那我可以
bool same = AretheTheSame(&b);
现在我只是传递一个指针。那是 4 或 8 个字节而不是 1000。
#2 允许函数修改其参数
正如指出的那样
bool same = AreTheSame(b);
将 b 的副本传递给函数,这很昂贵,但是 如果函数想要更改 b,它也不能。它只有一个副本。
想象一下,我们有一个函数 MakeTheSame(Biggy b);
,它查看两个数组是否相同,如果不相同,它会改变它们,太棒了。但是这样没用,因为改的是副本,不是原件。
我们又可以做到
void MakeTheSame(Biggy *b) {..}
...
Biggy b = MakeAndFillBiggy(); // we will come back to this function
bool same = MakeTheSame(&b);
现在我们将指针传递给调用者 'b'
请注意,这对任何大小的对象都很有用。
你可能看过这个
int num;
scanf("%d", &num);
我们必须将指针传递给 num 以便 scanf 可以修改它。
#3 获取和使用动态内存
您有一个应用程序可以保留参加锦标赛的足球队名单。
struct Team{
string name;
string town;
string captain;
}
容纳它的数组需要多大?你事先不知道会有多少支球队参赛。
你可以
Team teams[100];
但这有点浪费。因此,您改为使用动态内存,每次获得新时间时,您都会在堆上分配一个新的 Team 对象。
Team *team = new Team();
'new' 从池中动态分配内存,它对于 Team 对象来说足够大,并且会被初始化。它 returns 指向这个新内存的指针。
现在您可以拥有适量的团队对象。
让我们回到这个
Biggy b = MakeAndFillBiggy(); // we will come back to this function
现在我们来做
Biggy *MakeAndFillBiggy(){
Biggy * b = new Biggy();
... fill arrays somehow
return b;
}
现在我们做
Biggy *b = MakeAndFillBiggy();
C++ 好东西
C+ 有一个叫做引用的东西。它们的行为类似于指针(传递成本低,允许函数更改其参数)但在语法上看起来像非指针。
例如
void MakeTheSame(Biggy &b) {..}
这表示 MakeTheSame 将引用作为参数。现在我们可以做
Biggy b;
MakeTheSame(b);
引用比指针有一些优势,但它们做的事情完全一样
C++ 有 'smart pointers'
我们函数的问题
Biggy *MakeAndFillBiggy(){
Biggy * b = new Biggy();
... fill arrays somehow
return b;
}
Biggy *b = MakeAndFillBiggy();
是它返回一个指向堆上对象的指针。当我们完成它时,我们必须将它的内存释放回池中,以便它可以被重新使用。
所以某处一定有
delete b;
问题是,在复杂的代码中,当你完成 b 时,可能很难知道。输入 'smart pointers' 他们会检测你何时使用完内存并为你释放它
所以你会看到
std::shared_ptr<Biggy> MakeAndFillBiggy(){
std::shared_ptr<Biggy> p = std::make_shared<Biggy>();
... fill arrays somehow
return b;
}
std::shared_ptr<Biggy> b = MakeAndFillBiggy();
或更简洁,因为编译器很聪明
std::shared_ptr<Biggy> MakeAndFillBiggy(){
auto p = std::make_shared<Biggy>();
... fill arrays somehow
return b;
}
auto b = MakeAndFillBiggy();
在引擎盖下发生了完全相同的事情,只是在顶部添加了一些胶水以跟踪何时不再需要该指针
里面还有ptr = new xxx
和一个*p
我只是无法理解指针 - 我应该何时以及如何使用指针。无论我阅读了多少关于它的视频和文献,我就是不明白我应该何时以及如何使用它。明明我就是那么厚实...
让我们看看这个例子:
int main()
{
int* ptr = new int(10);
std::cout << ptr << std::endl;
std::cout << *ptr << std::endl;
std::cout << &ptr << std::endl;
}
这将 return 我们这样的值:
000001A68ABB5250
10
000000455ADCF828
这些输出是什么意思?第一个和第三个看起来像是内存中的地址编号,但它们不同,我不明白它们应该做什么?第二个是我赋的值,但是为什么要用指针符号来获取呢?
让我们看看这个:
int main()
{
int ptr = 10;
std::cout << ptr << std::endl;
//std::cout << *ptr << std::endl; //gives "Error(active) E0075 operand of '*' must be a pointer but has type 'int'"
std::cout << &ptr << std::endl;
}
其中分别有returns:
10
000000D9242FF5F4
上面的代码与此示例代码有何不同?为什么我不必使用指针来获取值? & 号向我们展示了内存地址,对吗?
基本上有人可以用最愚蠢、最容易理解的方式向我解释指针的意义是什么以及为什么我们在 C++ 中需要它们。
如果我们“绘制”第一个示例,它将是这样的:
+------+ +-----+ +--------------+ | &ptr | --> | ptr | --> | *ptr int(10) | +------+ +-----+ +--------------+
所以 ptr
指向一个 int
值(初始化为 10 10
)。 &ptr
指向变量 ptr
.
在第二个示例中,变量名称 ptr
具有误导性,因为它不是指针。这是一个普通的 int
变量。
画出来就会像
+------+ +-------------+ | &ptr | --> | ptr int(10) | +------+ +-------------+
当我第一次开始编程时,指针是我刚刚接受它们工作的东西之一,并最终积累了足够的经验 learn/understand 它们。
在我的一篇 类 中,一位教授说当你想修改一个对象而不是复制它时最好使用指针。按值传递给函数会创建在其中传递的对象的副本。
首先要理解的是符号*
和&
的含义取决于上下文。
在int* ptr
中,*
表示“这是一个指针”。
但除此之外(例如在 cout << *ptr
中),*
表示“取消引用”指针。 IE。给定一个指向 X 的指针,*
returns X,不管它是什么。
&
(例如在 cout << &ptr
中)“获取地址”。给定 X,它会为您提供指向 X 的指针。
&
和*
(第二种意义上)是相反的运算,它们相互抵消。
int* ptr = new int(10);
有点 类似于 int x = 10; int *ptr = &x;
,这反过来意味着 int x = 10; int *ptr; ptr = &x;
(不是 *ptr = &x
,因为 *
在这里意味着不同的东西,见上文)。
因此,给定 int x = 10;
,&x
是一个“指向 x 的指针”(打印它会给您一个地址)。在你执行 int *ptr = &x
之后,ptr
也是一个“指向 x 的指针”,因此打印它会显示相同的地址。
*ptr
给你 x
,就像 *&x
(这意味着 *(&x)
)一样,因为 *
和 &
取消了每个其他的。
指针可以指向其他指针。 &ptr
给你一个“指向 ptr
的指针”,或“指向 x 的指针”,其类型为 int **
。 **&ptr
因此 return x
的值。
我不是一个很好的解释者,但总体概述是:
声明和定义后int* ptr = new int(10);
当你使用ptr时,它是int*
类型的名称,如int x = 5
,当你使用x时,它是int类型。
ptr
是一个指针所以,它是一个存储在地址中的变量,所以当你打印它时,它就是一个地址。
&ptr
将是指针的地址,是的,指针也有地址。基本上,它是 int**
类型,所以打印时也是一个地址。
*ptr
取消对指针的引用,因此它将获得存储在指针指向的内存位置中的值 10。可以把它想象成,每个地址都指向一个存储值的内存位置。
如果它只是普通的 int ptr = 10
,您不能取消引用它:应用 *ptr
因为它是 int
类型而不是 int*
的指针。
但是,如果您将 &ptr
应用于它,它将获得 int ptr
的地址,该地址本质上是 int*
.
例如,如果您这样做:int x = 5; int *ptr = &x;
,
如果您取消引用 ptr
,并执行:cout << *ptr
,它将打印 5.
我第一次也遇到了困难。没关系。我花了一个月左右的时间来解决这个问题。事实上,我仍然有一些事情让我对指针感到困惑。哦,好吧,我不太会解释,大概和你看的差不多吧……祝你好运。
简介:
int* ptr = new int(10);
ptr(int)就是你在右边分配的 int 的内存地址(一个数字)。
std::cout << ptr << std::endl;
然后是打印出新int的内存地址。这是因为 ptr 只是一个数字 恰好是一个内存地址。
std::cout << *ptr << std::endl;
这里你告诉C++访问ptr所在编号的内存地址。因为ptr是int的内存地址,所以会得到int的值。
std::cout << &ptr << std::endl;
最后,你告诉C++给你ptr的内存地址。如果您使用 *&ptr,那么您将得到与您的第一个打印语句相同的结果。
就后半部分而言,int在这里只是一个值,而不是指针。当 C++ 说您不能使用 * 时,它可以防止您不小心将 int 视为内存地址。但是,如果您真的想将 int 视为内存地址,
int* x = 10
将允许 *x
编译。现在因为这是在内存地址 10 访问一个 int(这不是你程序的一部分)你的 OS 几乎肯定会杀死你的程序。
真正有趣的问题是 'why pointers?' 如果你明白了,那么剩下的可能会随之而来。
旁注,C++ 有很多结构,它们构成了很多这样的东西 'hidden',但是 我描述的内容仍在幕后进行
有几种场景可以使用指针
#1 传大事
想象一下你有这个
struct Biggy{
int Widle[500];
char Froodle[500];
}
这是一个大对象,复制起来很昂贵,你有一个对 Biggy 对象进行操作的函数,比如说它比较 Widle 和 Froodle 数组。
bool AreTheSame(Biggy b) {....}
c 和 c++ 的工作方式是参数按值传递,这意味着它们从调用者复制到被调用函数
所以当我这样做时
Biggy b = MakeAndFillBiggy(); // we will come back to this function
bool same = AReTheSame(b);
复制所有 1000 个字节,这很昂贵
但如果我有
bool AreTheSame(Biggy *b) {....}
那我可以
bool same = AretheTheSame(&b);
现在我只是传递一个指针。那是 4 或 8 个字节而不是 1000。
#2 允许函数修改其参数
正如指出的那样
bool same = AreTheSame(b);
将 b 的副本传递给函数,这很昂贵,但是 如果函数想要更改 b,它也不能。它只有一个副本。
想象一下,我们有一个函数 MakeTheSame(Biggy b);
,它查看两个数组是否相同,如果不相同,它会改变它们,太棒了。但是这样没用,因为改的是副本,不是原件。
我们又可以做到
void MakeTheSame(Biggy *b) {..}
...
Biggy b = MakeAndFillBiggy(); // we will come back to this function
bool same = MakeTheSame(&b);
现在我们将指针传递给调用者 'b'
请注意,这对任何大小的对象都很有用。
你可能看过这个
int num;
scanf("%d", &num);
我们必须将指针传递给 num 以便 scanf 可以修改它。
#3 获取和使用动态内存
您有一个应用程序可以保留参加锦标赛的足球队名单。
struct Team{
string name;
string town;
string captain;
}
容纳它的数组需要多大?你事先不知道会有多少支球队参赛。
你可以
Team teams[100];
但这有点浪费。因此,您改为使用动态内存,每次获得新时间时,您都会在堆上分配一个新的 Team 对象。
Team *team = new Team();
'new' 从池中动态分配内存,它对于 Team 对象来说足够大,并且会被初始化。它 returns 指向这个新内存的指针。
现在您可以拥有适量的团队对象。
让我们回到这个
Biggy b = MakeAndFillBiggy(); // we will come back to this function
现在我们来做
Biggy *MakeAndFillBiggy(){
Biggy * b = new Biggy();
... fill arrays somehow
return b;
}
现在我们做
Biggy *b = MakeAndFillBiggy();
C++ 好东西
C+ 有一个叫做引用的东西。它们的行为类似于指针(传递成本低,允许函数更改其参数)但在语法上看起来像非指针。
例如
void MakeTheSame(Biggy &b) {..}
这表示 MakeTheSame 将引用作为参数。现在我们可以做
Biggy b;
MakeTheSame(b);
引用比指针有一些优势,但它们做的事情完全一样
C++ 有 'smart pointers'
我们函数的问题
Biggy *MakeAndFillBiggy(){
Biggy * b = new Biggy();
... fill arrays somehow
return b;
}
Biggy *b = MakeAndFillBiggy();
是它返回一个指向堆上对象的指针。当我们完成它时,我们必须将它的内存释放回池中,以便它可以被重新使用。
所以某处一定有
delete b;
问题是,在复杂的代码中,当你完成 b 时,可能很难知道。输入 'smart pointers' 他们会检测你何时使用完内存并为你释放它
所以你会看到
std::shared_ptr<Biggy> MakeAndFillBiggy(){
std::shared_ptr<Biggy> p = std::make_shared<Biggy>();
... fill arrays somehow
return b;
}
std::shared_ptr<Biggy> b = MakeAndFillBiggy();
或更简洁,因为编译器很聪明
std::shared_ptr<Biggy> MakeAndFillBiggy(){
auto p = std::make_shared<Biggy>();
... fill arrays somehow
return b;
}
auto b = MakeAndFillBiggy();
在引擎盖下发生了完全相同的事情,只是在顶部添加了一些胶水以跟踪何时不再需要该指针
里面还有ptr = new xxx
和一个*p