我试图找出new
的工作原理:
#include <iostream>
using namespace std;
struct munch{
int x;
};
int main(){
munch *a;
//1
cout << a << endl;
cout << a->x << endl;
//1
cout << endl;
//2
a= new munch;
cout << a << endl;
cout << a->x << endl;
//2
cout << endl;
//3
a= new munch;
cout << a << endl;
cout << a->x << endl;
//3
}
1,2和3之间的区别是什么?为什么operator new
给指向struct一个新位置的指针,但是没有改变a->x
的值?但在调用new
的第一个实例之前,它的值与调用new
之后的值不同吗?
答案 0 :(得分:2)
[1]是未定义的行为,因为它试图取消引用未初始化的指针。
[2]和[3]是相同的,可能导致未定义的行为,尽管至少你会在struct中获得字段的垃圾值。
更多关于标准所说的[2]和[3]: -
C 1999标准在6.7.8 10中说,“如果没有明确初始化具有自动存储持续时间的对象,则其值是不确定的。”
此外,如果左值指定了一个自动存储持续时间的对象,该对象可能已使用寄存器存储类声明(从未使用过其地址),并且该对象未初始化(未使用初始化程序声明且未分配如果在使用之前已经执行过,那么行为是未定义的。
答案 1 :(得分:2)
在第一种情况下,您正在读取未初始化的指针,undefined behaviour(a
和a->x
未定义)。
另外两个例子中重要的是munch
是Plain Old Data。这意味着new munch
does not initialize新分配的结构的字段。因此,阅读a->x
也会导致未定义的行为。
这是C ++,您可以向munch
添加一个构造函数,该构造函数将初始化x
并使示例2和3定义明确。
答案 2 :(得分:2)
在1
中,您已声明指向munch
的指针,但尚未对其进行初始化。因此,cout << a << endl;
和cout << a->x << endl;
都是未定义的行为,因为它们尚未初始化。在2
和3
中(在代码方面无法找到两者之间的差异),第一行cout << a << endl;
现在已定义为行为,因为您已为内存地址分配a
来自a = new munch;
。但是,a->x
仍然未初始化(您从未使用a->x = 5;
之类的内容进行设置),因此在这两种情况下,cout << a->x << endl;
仍然是未定义的行为。
答案 3 :(得分:1)
首先,让我们澄清你的语义。
请注意语言中有 new-expression 和 operator new 。
定义了various forms of operator new
,如展示位置1,不可抛弃的全局版本和可替换的全局版本。
new-expression 包括调用其中一个分配函数,然后执行任何初始化。
如果第二部分失败,它也将调用相应的去初始化函数。
所以,让我们来看看你有什么:
在第一种情况下,只读取未初始化的非静态(因此不确定)指针为Undefined Behavior。
在第2和第3例中,由于munch
是POD类型,因此 new-expression 不会导致构造函数调用,这意味着所有成员都保持未初始化状态,从而读取{ {1}}是UB。
答案 4 :(得分:0)
因为x
的值是垃圾值,因为您没有在任何地方初始化它。并且a
的第一个值也是垃圾,你也没有在任何地方指出它。
new
运算符从操作系统请求一个内存段并调用类构造函数,返回指向该内存的指针并将其地址存储在变量中。
由于munch
没有构造函数,因此它永远不会初始化x
。
所有代码都有未定义的行为,所以你永远不应该这样做,使用动态内存分配的munch
结构的正确方法是
munch *a;
a = new munch;
a->x = SOME_INITIAL_VALUE;
std::cout << "address of a : " << a << std::endl;
std::cout << "value of a->x: " << a->x << std::endl;
或者在结构中添加构造函数,您可以在其中初始化x
struct munch
{
public:
munch(int initial_x = SOME_INITIAL_VALUE) : x(initial_x) {}
int x;
};
munch *a;
a = new munch;
std::cout << "address of a : " << a << std::endl;
std::cout << "value of a->x: " << a->x << std::endl;
答案 5 :(得分:0)
当你声明一个变量struct munch
时,编译器会做什么,它只需要大小相当于sizeof(munch)
的内存块,并为它指定一个你给出的名称。
但是在指针声明的情况下,编译器只是为内存位置指定一个名称,并且在您明确指定它之前不会分配任何内存(使用new
运算符)。
例如:
munch *a;
此语句只是将名称a
指定给内存位置。
因此,cout<<a
将打印该内存地址。
当你执行cout<<a->x
时,它会给你运行时错误,因为你没有分配空间。所以你现在没有任何价值。
// 2
首先,将给出a
指向的新内存位置。由于new
运算符,将分配内存。 struct
中的变量将具有已分配的内存位置的默认值。
因此,当您执行cout<<a
时,它会打印a
的新位置。
当你cout<<a->x
时,它会给出一些随机值。如果你编译&amp;一次又一次地运行程序,每次都可能得到不同的值。
// 3
与案例#2一样,同样的事情也会发生。首先,将选择新的内存位置,a
将指向该位置。然后将打印结果,但由于内存位置不同,值可能与情况#2不同。
答案 6 :(得分:0)
new运算符为堆上的实例分配内存并返回指针。对于您的代码,1。在堆栈上分配一个项目。 2和3在堆上分配项目。当我运行你提供的代码时,我在所有3个实例中获得了不同的x值(CodeLite / MingGW):
0x417d7e
1528349827
0x8f6bd8
12
0x8f6be8
14
每个新的都应该伴随删除。