什么是NULL值

时间:2010-04-02 06:07:46

标签: c++ c null

我想知道,当我们说某个特定的变量指针为NULL时,内存中究竟存储了什么。假设我有一个结构,比如说

typdef struct MEM_LIST MEM_INSTANCE;
struct MEM_LIST
{
      char *start_addr;
      int size;
      MEM_INSTANCE *next;
};
MEM_INSTANCE *front;

front = (MEM_INSTANCE*)malloc(sizeof(MEM_INSTANCE*));

-1)如果我使前面= NULL。实际存储在前面的不同字段中的值是什么,例如front-> size,front-> start_addr。它是0或其他东西。我对这个NULL事物知之甚少 -2)如果我做免费(前面);它释放了前面指出的记忆。那么究竟自由意味着什么呢,它会使它为NULL还是全部为0 -3)处理指针初始化和释放它们的好策略是什么。

提前致谢

11 个答案:

答案 0 :(得分:4)

有许多有用的答案可以充分解决问题。但是,NULL的覆盖范围很轻。

在现代虚拟内存体系结构中,NULL指向内存,任何引用(即尝试从该地址读取或写入内存)都会导致 segfault 异常 - 也称为访问冲突内存故障。这是一种有意识的保护机制,可以有效地检测和处理无效的内存访问:

char  *p = 0;
for (int j = 0;  j < 50000000;  ++j)
        *(p += 1000000) = 10;

此代码在每百万个内存字节写入。它不会运行很多循环 - 可能甚至不会运行一次。它将尝试访问未映射的地址,或者它将尝试修改只读存储器 - 其中存在常量数据或程序代码。 CPU将中途中断指令并将异常报告给操作系统。由于没有指定异常处理,因此默认的o / s处理是终止程序。 Linux显示Segmentation fault(由于历史原因)。 MS Windows不一致,但倾向于说access violation。受保护的虚拟内存中的任何程序都应该这样做:

char *p = NULL;
if (p [34] == 'Y')
        printf ("A miracle has occurred!\n");

这段错误。正在取消引用NULL地址附近的内存位置。

存在混淆的风险,可能从零开始的大偏移将是有效内存。三十四当然不会好,但可能会有34,000。不同的操作系统和不同的程序处理工具(链接器)保留固定数量的内存零端并安排它取消映射。它可能只有1K,尽管8K在20世纪90年代是一个受欢迎的选择。具有足够虚拟地址空间(不是内存,但潜在内存)的系统可能会留下8M或16M内存空间。一些现代操作系统随机化了保留的空间量,以及每次程序启动时代码和数据部分的randomly varying the locations

另一个极端是非虚拟内存架构。通常,这些地址提供从地址零开始到安装内存限制的有效地址。这在嵌入式处理器,许多DSP和预保护模式CPU,8位和16位处理器(如8086,68000等)中很常见。读取NULL地址不会引起任何特殊的CPU响应 - 它只是读取任何内容,这通常是中断向量。写入低内存通常会导致难以诊断的可怕后果,因为中断向量是异步使用的。

更奇怪的是使用small or medium memory model约定的奇怪命名的“实模式”x86的片段模型。这些数据地址使用DS寄存器为16位,该寄存器设置为程序的初始化数据区。解除引用NULL访问此空间的第一个字节,但MSDOS程序包含与CP / M兼容的古老运行时结构,使用o / s Fred Flintstone。没有例外,也许没有在此环境中修改NULL附近的内存的后果。这些都是在没有程序源代码的情况下发现的挑战。

虚拟内存保护是创建稳定系统和保护程序员免受攻击的巨大飞跃。正确使用的NULL可以提供重要的安全性并快速调试编程缺陷。

答案 1 :(得分:3)

分配给指针的

NULL不会更改“由它指向的字段”。

如果您提出front = NULLfront将不再指向malloc分配的结构,但将包含零(NULL为0,根据您的情况C标准)。没有任何东西会指向你分配的结构 - 这是内存泄漏。

请注意指针front)和指向(结构)之间的关键区别 - 这是一个很大的区别。

回答您的具体问题:

  1. 如果您运行front=NULLfront将不再指向MEM_INSTANCE结构,因此front->size将没有意义(它可能会导致程序崩溃)
  2. 如果执行free(front)操作系统将释放为MEM_INSTANCE结构分配给您的内存。 front现在将指向不再属于您的记忆 - 而且您无法访问它
  3. 这是一个广泛的问题 - 请问一个更具体的问题。

答案 2 :(得分:3)

指针的赋值与指针所指向的元素的赋值不同。将NULL分配给前面将使front指向任何内容,但是您分配的内存将不受影响。它不会将任何数据写入front以前指向的字段中。此外,这是内存泄漏。

调用free(front)将取消分配内存块,但不会影响front的值;换句话说,front将指向您不再拥有的内存区域,这对您来说不再有效。这也称为“悬空指针”,通常最好立即使用free(front)关注front=NULL,以便您知道front不再有效。

处理指针的一个好策略是,至少在C ++中,使用智能指针类并仅在构造函数中执行分配,并仅在析构函数中执行释放。另一个好策略是确保始终将NULL分配给刚刚释放的任何指针。在C中,您只需要确保分配与解除分配正确匹配。它还可以帮助使用并行C ++构造函数/析构函数的“name_of_object_create”和“name_of_object_destroy”函数;但是,C中没有办法确保自动销毁。

答案 3 :(得分:3)

哇,很多答案都有效地声称为指针分配NULL会将其设置为指向地址0,使值和表示混淆。它不是。设置指向值NULL或0的指针是一个抽象概念,它将指针设置为不指向任何有效对象的无效值。实际存储在内存中的二进制表示确实需要全部为0。这通常不是架构的事情,它取决于编译器。事实上,我有一个旧的DOS编译器(在x86上),它使用所有位1作为空指针。

此外,任何指针类型都允许有自己的NULL二进制表示形式,只要所有这些指针在比较时比较相等。

当然,出于实际原因,大多数时候对于NULL指针,所有位都为0,但这不是必需的。这意味着使用calloc()或memset(0)不是指针的可移植初始化。

答案 4 :(得分:2)

这取决于你的意思。 传统上,null意味着没有价值。

在C中一般为null意味着0.因此指针指向地址0.但是如果你实际上有一块内存,那么coulkd可以是任何使用它的内存。如果清除内存(比如说)0,那么如果你说那个内存包含指针,那些指针将为空。

你必须考虑一个指针是什么:它只是一个保存一些内存地址的值。因此,如果地址是0x1,则具有该值的该指针指向存储器中的第二个字节(记住传统上寻址为第一项为0,第二项为1等)。所以如果我char * p = 0x1;可以说p指向从地址0开始的内存。因为我已经将它声明为char *,我说我对内存中指向的字符串大小的值感兴趣所以0。所以* p是值内存中的第二个字节。

例如: 采取以下

struct somestruct { char  p } ;

  // this means that I've got somestruct at location null (0x00000)
  somestruct* ptrToSomeStruct = null; 

所以ptrToSomeStruct-&gt; p表示取ptrToSomeStruct指向的内容(0x00000),然后将其作为char的值,所以你读取内存中的第一个字节

现在,如果我这样宣布:

// this means that I've got somestruct on the stack and there for it's got some memory behind it.
somestruct ptrToSomeStruct; 

所以ptrToSomeStruct-&gt; p表示获取ptrToSomeStruct指向的内容(堆栈中的某个位置)然后将其作为char的值的内容,因此您将从中读取一些字节叠加。

反映以下评论: C(和simmilar laguages)程序员面临的一个关键问题是,有时指针会指向内存的错误部分,因此当你读取值时,你就会进入错误的内存部分开始,因此你发现无论如何都有错。在很多情况下,错误的地址实际上设置为0.这就像我的例子意味着去内存的开始并在那里读取东西。为了帮助编写指针中实际包含0的错误,许多操作系统/体系结构会阻止您读取或写入该内存,当您执行此操作时,程序会收到地址异常/错误。

答案 5 :(得分:2)

NULL是一个sentinel值,表示指针未指向有意义的位置。即“不要试图取消引用这一点。”

So what exactly free means here, does it make it NULL or make it all 0.

都不是。它只释放front指向的内存块。 front中的值保持不变。

答案 6 :(得分:2)

在C / C ++中NULL == 0。

int * a = NULL; int * b = 0;

存储在变量'a'和&amp;中的值'b'都是0.“NULL”没有特殊的魔力。这一天向我解释,指针突然变得有意义。

答案 7 :(得分:2)

NULL的定义通常是

#define NULL 0
or
#define NULL (void*)0

将它指定给指针只会使指针停止指向它所指向的任何内存地址,现在指向内存地址0.这通常在初始化时或在指针的内存空闲时完成,尽管它不是没必要。将指针设置为NULL会使解除分配内存或更改其指向的任何值。

调用free()(在C中)或delete(在C ++中)将释放指针指向的内存,但不会将指针设置为NULL。在指针free-d之后解除引用是未定义的行为(即正常崩溃)。因此,一个常见的习惯用法是在解除分配之后将指针设置为NULL,以便以后更容易地捕获错误的偏差。

答案 8 :(得分:1)

通常,在经典指针中,空指针指向地址0x0。它取决于体系结构和特定语言,但如果它是基本类型,那么值0将被视为NULL。

在Intel体系结构中,内存的开头(地址0)包含无法分配的保留空间。它也在任何正在运行的应用程序的边界之外。所以指向那里的指针也非常安全地意味着NULL。

答案 9 :(得分:0)

你的问题表明你根本不理解指针。

如果你放front = NULL编译器会front = 0并且前面包含实际结构的地址,那么你将失去释放它的可能性。

再次阅读“Kernighan&amp; Ritchie”。

答案 10 :(得分:0)

在C中发言:

preprocesor宏NULL是#defined(由stdio.h或stddef.h提供),值为0(或(void *)0)

-1)如果我使前面= NULL。实际存储在前面的不同字段中的值是什么,例如front-&gt; size,front-&gt; start_addr。它是0或其他东西。我对这个NULL事物知之甚少。

你将有前= 0x0。执行font-&gt; size会引发SIGSEG。

-2)如果我免费(前面);它释放了前面指出的记忆。那么究竟自由意味着什么呢,它会使它为NULL还是全部为0.

Free会将内存保留在前面作为空闲标记,因此另一个malloc / realloc调用可能会使用它。它是否将指针设置为NULL或保持其值不变,其实现依赖,但肯定不会将所有结构设置为0。

-3)处理指针初始化和释放它们的好策略是什么。

我喜欢将我的指针初始化为NULL,并在解除分配后将它们设置为NULL。