我想知道,当我们说某个特定的变量指针为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)处理指针初始化和释放它们的好策略是什么。
提前致谢
答案 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 = NULL
,front
将不再指向malloc
分配的结构,但将包含零(NULL
为0,根据您的情况C标准)。没有任何东西会指向你分配的结构 - 这是内存泄漏。
请注意指针(front
)和指向(结构)之间的关键区别 - 这是一个很大的区别。
回答您的具体问题:
front=NULL
,front
将不再指向MEM_INSTANCE结构,因此front->size
将没有意义(它可能会导致程序崩溃)free(front)
操作系统将释放为MEM_INSTANCE结构分配给您的内存。 front
现在将指向不再属于您的记忆 - 而且您无法访问它答案 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。