指向无效位置的指针

时间:2010-10-23 02:26:03

标签: c pointers

#include <stdio.h>

int main(void)
{ 
   int x = 1000;
   char *ptr = &x;
   printf("%d\n",*ptr);
   return 0;
 }

Output: -24 /*In gcc 4.4.3 in Ubuntu 10.04 OS*/ 使用warning: initialization from incompatible pointer type

我认为如果指针的基本类型是int类型,那么它将从它指向的位置检索4个字节。然后O / P将是1000.但是当我将基类型更改为char然后当我取消引用它时,它将从它指向的位置检索1个字节。但答案是-24。 当我改变程序如下时

#include <stdio.h>

    int main(void)
    { 
       int x = 1000;
       float *ptr = &x;
       printf("%f\n",*ptr);
       return 0;
     }

输出变为0.000000并带有相同的警告。当我解除指针时,它将从它指向的位置检索4个字节。但是O / P是如何0.000000。我有点困惑。你们可以解释一下。我可以解释它。我C编程新手,所以在提出问题时有任何错误,请原谅我。感谢名单

3 个答案:

答案 0 :(得分:3)

取消引用字符指针可以获得内部表示形式1000的第一个字符大小的块作为整数。然后将该字符提升为int(根据varidac参数的规则),并解释为整数。

在你的机器上,使用该编译器,结果是-24。

字符大小的块可能是一个8位字节。

整数可能由4或8个字节表示。

内部表示可能是 2s-complement,并且可能以小端顺序存储。

您是否开始明白为什么结果不易预测?


使用浮点指针做同样的事情会返回一个float大小的内存而不是char,现在你可能有真正的(嘿!)麻烦,因为我们现在还不知道,如果float表示甚至适合int表示,那么您可能正在访问未初始化的内存。

在任何情况下,当您取消引用float*时,它将该内存解释为float(内部结构比字符或甚至2s补码整数更复杂),并将其提升到double(再次对varidac参数的规则)然后尝试将该表示解释为整数(如果float 适合intdouble可能,因此您将double的 part 解释为int!)。啊。

这种情况甚至比上一次更糟糕,因为它涉及IEEE浮点表示标准,并且两次机会大小都不匹配。

所以这里的教训是:

不要那样做。

辅助课程,仅适用于专家

仍然不这样做。

因为如果你想要执行所有这些愚蠢的重新解释,你希望对它们进行精确而明确的控制。

答案 1 :(得分:1)

十六进制中的

10000x3E8如果您的系统将int存储为小端32位值,则x会像这样存储在内存中:

+------+------+------+------+
| 0xE8 | 0x03 | 0x00 | 0x00 |
+------+------+------+------+
    ^
    |      ---increasing addresses--->
   &x

对于第一种情况:

如果您的系统有一个8位char,当您将&x分配给char *ptr然后取消引用它时,您会获得值{{1解释为0xE8

如果您的系统将char类型视为已签名,如果已签名的表示形式为2的补码,则char将被解释为{ {1}}。

对于第二种情况:

如果您的系统有一个小端32位0xE8,当您将-24分配给float然后取消引用它时,您将获得该值将&x的位模式解释为float *ptr

如果您的系统以IEEE 754格式表示0x000003E8,则最高有效位表示符号(0表示它为正),接下来的8个最高有效位表示指数( 0表示数字为零或非规范化 - 非常小,其余23位表示“有效数”或“尾数”(0x3E8或1000)。这实际上具有值1000 * 2 -149 ,大约是1.4013 * 10 -42 - 您可以使用float而不是{{{{}}来查看此值1 {} float格式字符串。

答案 2 :(得分:-1)

您的问题的核心是您尝试将字符(*ptr)打印为整数(%d)。

因为您使用printf将整数指定为%d,所以将从堆栈中删除4个字节。

但是,因为您将char传递给printf,所以只传递了1个字节。 <{1}}检索的剩余3个字节将是随机堆栈数据,可以是任何内容。

结果,你得到-24或其他一些不一致的值。