这两个程序有什么区别:
#include <stdio.h>
int main( void )
{
int* ptr;
*ptr = 600;
printf("ptr = %d\n", *ptr );
return 0;
}
和
#include <stdio.h>
int main( void )
{
int* ptr = (int*) 600;
printf("ptr = %d\n", *ptr );
return 0;
}
为什么我收到的第一个ptr = 600
,而在第二个,我收到一个错误,说该程序已停止工作?
答案 0 :(得分:1)
您的程序都会调用undefined behavior。
第一个,写一个单位指针。
第二个,从可能无效的内存位置读取。 FWIW,将int
转换为指针类型也是实现定义的行为。
解决方案:
在取消引用之前,将正确的内存分配给ptr
。
答案 1 :(得分:1)
这两个程序有什么区别:
第一篇:
int* ptr; // Uninitialized pointer
*ptr = 600; // Dereferencing causes undefined behaviour
第二部分:
int* ptr = (int*) 600; //600 - A memory location which you may not have access to.
printf("ptr = %d\n", *ptr ); // Dereferencing causes undefined behaviour
结论
没有区别。两者迟早都会崩溃。
答案 2 :(得分:1)
在第一个示例中,您将600
写入ptr
个点,这是未定义的,因为指针从未分配给任何东西。它可能似乎可以工作,因为它正在写600
某处然后再读取值。您很幸运,访问某处并未导致分段错误。
在第二个示例中,您明确将地址 600
分配给变量ptr
。这对于内存地址来说是一个相当低的值,这几乎肯定不是你应该尝试读取的内存中的某个地方。当你取消引用它(带有printf
的行)时,你的程序“停止工作”,因为它试图从不应该的地方读取内存。
简而言之,两者都无效。但是第一个失败的方式更具欺骗性。
内存是一个奇怪而奇妙的事情,因为从根本上说,一切都只是二进制数据的字节。从内存中读取任何内容时,编译器需要一些概念:
第二个示例int *ptr = (int *)600
中的行将变量ptr
声明为指向int
的指针。指针ptr
允许您在解除引用时从内存中的任意位置读取(使用*ptr
)。请注意如何指定上述三个要求。 ptr
的值是从哪里开始阅读。另外两个要求由其类型int *
给出,其中包括它指向的事物的类型:读取sizeof(int)
个字节并将读取的内容解释为有符号整数int
。
(“如何解释它”有点可选。例如,void *
指定“从哪里开始阅读”,但明确没有给出如何解释那里的概念,只留下它作为字节的字节二进制数据。任何使用void *
的接口都可能有一些其他机制来指定“读取多少”,例如size_t
类型的另一个参数。)
初始化(int *)600
包含类型转换。此强制编译器将600
视为int
所在的内存地址,使其成为兼容的值以分配给int *ptr
。对这样的地址进行硬编码几乎肯定是在寻找麻烦,除非你真的知道你正在做什么(为你正在编写的机器设置内存映射等)并且有充分的理由这样做(例如内存映射I / O等)。简而言之,您没有提供有用的地址。
作为一个类比,想象一下你需要买东西,并知道一个卖它的地方。你派朋友出去为你买。但你的朋友不是很聪明。他会像你告诉他的那样做,但你必须提供简短而简单的指示。你告诉他去一个特定的地址买四个他们卖的东西。什么可能出错?在你的情况下,你的朋友因擅自入侵而被捕。这些说明完全取决于您是否提供了正确的地址。在第二个例子中,他不被允许去地址600,并且不再试图到达那里。
这还取决于您所考虑的商店是否存在。在您的两个示例中,ptr
都具有无意义的值。与您的第一个例子类似,您根本不提供任何地址。你的朋友去了他想到的最后一个地方,向那里的任何人提供了一些东西(数字600),然后把它拿回来。如果它有效,那只是因为你很幸运他去了哪里,他遇到的那个人是合作的。
“修复”代码的方法是让你的指针实际指向有意义的东西。在这两个示例中,您实际上没有类型int
的变量,您可以在内存中的任何位置使用它。您可以将int
分配为局部变量(在堆栈上),并使ptr
指向它,例如:
int x; // now x is an int that actually exists
int *ptr = &x; // initialize ptr to point to x
然后,对*ptr
的任何分配都会写入x
,例如:
*ptr = 600; // now x == 600
使用指针更有用的是使用动态分配的堆内存,如:
int *ptr;
ptr = malloc(sizeof(*ptr));
if ( ptr != NULL ) {
*ptr = 600;
}
这会在堆内存中分配int
,使ptr
指向它,然后(如果已成功分配)将600
写入其中。像这个malloc()
调用的堆分配似乎是你的第一个例子所缺少的。