发出解除引用指针的问题

时间:2016-04-25 15:30:32

标签: c pointers

这两个程序有什么区别:

#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,而在第二个,我收到一个错误,说该程序已停止工作?

3 个答案:

答案 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()调用的堆分配似乎是你的第一个例子所缺少的。