空值存储在哪里,或者根本存储在哪里?


39

我想了解空值或空引用。

例如,我有一个名为Apple的类,并创建了它的一个实例。

Apple myApple = new Apple("yummy"); // The data is stored in memory

然后我吃了那个苹果,现在它需要为空,因此我将其设置为空。

myApple = null;

打电话之后,我忘记了吃了它,现在想检查一下。

bool isEaten = (myApple == null);

通过此调用,myApple在哪里引用?null是一个特殊的指针值吗?如果是这样,如果我有1000个空对象,如果我们认为指针类型为int的话,它们会占用1000个对象存储空间还是1000个int存储空间?

Answers:


45

在您的示例中myApple具有特殊值null(通常为全零位),因此也没有引用任何内容。它最初引用的对象现在在堆上丢失了。无法检索其位置。在没有垃圾回收的系统上,这称为内存泄漏。

如果最初将1000个引用设置为null,则只有1000个引用的空间,通常为1000 * 4字节(在32位系统上,是64位的两倍)。如果这1000个引用最初指向实际对象,则您分配了每个对象大小的1000倍,外加1000个引用的空间。

在某些语言(例如C和C ++)中,即使“未初始化” ,指针也始终指向某个对象。问题是他们拥有的地址是否合法,您的程序可以访问。特殊地址零(aka null)是有意未映射到您的地址空间中的,因此在访问内存管理单元(MMU)且程序崩溃时,内存管理单元(MMU)会产生分段错误。但是由于故意不将地址0 映射到其中,因此它成为一个理想值,可用于指示指针未指向任何内容,因此其作用为null。要完成故事,请使用new或分配内存malloc(),操作系统将MMU配置为将RAM页面映射到您的地址空间,并且它们变得可用。通常,仍然存在大量未映射的地址空间,因此也会导致分段错误。


很好的解释。
NoChance 2012年

6
在“内存泄漏”部分有一点错误。在没有自动内存管理的系统中,这是内存泄漏。但是,GC并不是实现自动内存管理的唯一可能方法。C ++ std::shared_ptr<Apple>是一个既没有GC也没有Apple在置零时泄漏的示例。
MSalters 2012年

1
@MSalters- shared_ptr不仅仅是垃圾收集的基本形式吗?GC不需要存在单独的“垃圾收集器”,而仅发生垃圾收集。
恢复莫妮卡2012年

5
@Brendan:术语“垃圾收集”几乎被普遍理解为是指独立于正常代码路径而发生的非确定性收集。基于引用计数的确定性破坏是完全不同的。
梅森·惠勒

1
很好的解释。一个稍微令人误解的观点是假设内存分配映射到RAM。RAM是一种用于短期内存存储的机制,但是实际的存储机制是由OS抽象的。在Windows中(对于非零零环应用程序),内存页面已虚拟化,并且可以映射到RAM,磁盘交换文件或其他存储设备。
Simon Gillbee 2014年

13

答案取决于您使用的语言。

C / C ++

在C和C ++中,关键字为NULL,而NULL实际为0。确定“ 0x0000”永远不会成为指向对象的有效指针,因此该值被分配以指示该值不是有效的指针。但是,它是完全任意的。如果您尝试像指针一样访问它,它的行为就像指向内存中不再存在的对象的指针一样,从而导致抛出无效的指针异常。指针本身占用内存,但最多不超过整数对象。因此,如果您有1000个空指针,则相当于1000个整数。如果这些指针中的某些指向有效对象,则内存的使用量将等于1000个整数加上这些有效指针中包含的内存。请记住,在C或C ++中,并不意味着内存已被释放,因此您必须使用dealloc(C)或delete(C ++)显式删除该对象。

爪哇

与C和C ++不同,在Java中null只是关键字。与其像管理指向对象的指针那样管理null,不如在内部对其进行管理并将其视为文字。这消除了将指针绑定为整数类型的需要,并允许Java完全抽象出指针。但是,即使Java更好地将其隐藏,它们仍然是指针,这意味着1000个空指针仍然消耗1000个整数。显然,当它们指向对象(类似于C和C ++)时,这些对象会消耗内存,直到不再有指针引用它们为止,但是与C和C ++不同,垃圾收集器在下一次传递时会拾取它并释放内存,在大多数情况下,无需要求您跟踪释放了哪些对象以及哪些对象没有释放(例如,除非您有理由弱引用对象)。


9
您的区分是不正确的:实际上,在C和C ++中,空指针根本不需要指向内存地址0(尽管这是自然的实现,与Java和C#相同)。它实际上可以指向任何地方。可以将字面量0隐式转换为空指针这一事实使这有点困惑。但是为空指针存储的位模式仍然不必全为零。
Konrad Rudolph

1
不,你错了。语义是完全透明的……在程序中,空指针和宏NULL(顺便说一下,不是关键字)被视为零位。但是它们并不需要这样实现,实际上,一些晦涩的实现确实使用了非零的空指针。如果我写了,if (myptr == 0)那么即使null指针在内部由表示,编译器也将做正确的事情0xabcdef
Konrad Rudolph

3
@Neil:空指针常量(整数类型的prvalue的值为零)可以转换为空指针值。(第4.10 C ++ 11节。)不能保证空指针值的所有位都为零。0是一个空指针常量,但这并不意味着myptr == 0检查的所有位myptr是否为零。
Mat

5
@Neil:您可能想检查C常见问题解答中的条目 SO问题
hugomg

1
@Neil这就是为什么我不提NULL任何宏,而是谈论“空指针”,并明确提到“ literal-0可以隐式转换为空指针”的原因。
康拉德·鲁道夫2012年

5

指针只是一个变量,大部分是整数类型。它指定存储实际对象的内存地址。

大多数语言都允许通过以下指针变量访问对象成员:

int localInt = myApple.appleInt;

编译器知道如何访问的成员Apple。它“跟随”指向myApple的地址的指针,并检索appleInt

如果将空指针分配给指针变量,则使指针指向没有内存地址。(这使成员无法访问。)

对于每个指针,您都需要内存来保存内存地址整数值(在32位系统上为4字节,在64位系统上为8字节)。对于空指针也是如此。


我认为参考变量/对象不完全是指针。如果您打印它们,它们将包含ClassName @ Hashcode。JVM内部使用Hashtable来存储具有实际地址的Hashcode,并在必要时使用Hash算法来检索实际地址。
减去2012年

@minusSeven对于涉及整数的文字对象,这是正确的。否则,哈希表将包含指向Apple类本身中包含的其他对象的指针。
尼尔2012年

@minusSeven:我同意。指针实现的细节在很大程度上取决于语言/运行时。但我认为这些细节与特定问题无关。
斯蒂芬

4

快速示例(注意不存储变量名):

void main()
{
  int X = 3;
  int *Y = X;
  int *Z = null;
} // void main(...)


...........................
....+-----+--------+.......
....|     |   X    |.......
....+-----+--------+.......
....| 100 |   3    |<---+..
....+-----+--------+....|..
........................|..
....+-----+--------+....|..
....|     |   Y    |....|..
....+-----+--------+....|..
....| 102 |  100   +----+..
....+-----+--------+.......
...........................
....+-----+--------+.......
....|     |   z    |.......
....+-----+--------+.......
....| 104 |   0    |.......
....+-----+--------+.......
...........................

干杯。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.