【问题标题】:Why does assigning a large number in one byte work in C?为什么在 C 中在一个字节中分配一个大数?
【发布时间】:2010-11-13 00:07:11
【问题描述】:
int *ptr = malloc(sizeof(char));

*ptr = 100000;

printf("%d\n", *ptr); // 100000

不应该只为char分配足够的内存,即1字节吗?所以最大的数不应该是255吗?

它仍然如何打印100000

更新

感谢您的回答。如果它覆盖了下一个字节,那么 C 怎么知道这个数字大于一个字节,而不是只看第一个字节?

【问题讨论】:

  • 简短的回答:因为 C 甚至不能拼写“内存安全”。
  • @R.. 我希望这是你一天的亮点。
  • 关于您更新的问题,这在很大程度上没有意义,因为一旦您编写了无效的代码(即调用未定义的行为),任何事情都可能发生。您的实现中发生的情况是内存是字节大小的插槽的平面序列,并且由于您通过int 指针访问内存,因此指向的字节和随后的 3 个字节将作为int 访问。抱歉,如果我有点不耐烦,但每周我们都会看到 20 多个“为什么这有效?”关于具有未定义行为的代码的问题。
  • @R.. 当然,您必须了解每个人都必须从某个地方开始。 IMO 在 Stack Overflow 上不允许那些对善意的问题投反对票的人,仅仅是因为 他们 认为这很愚蠢。
  • 请务必注意,您分配的号码的值无关紧要。如果你写了*ptr = 1;,你也会遇到同样的问题——因为1仍然是int类型,并且仍然占据sizeof (int)字节。

标签: c


【解决方案1】:

因为 C 没有内存范围检查。它分配一个字节,然后您通过指针的分配覆盖它和接下来的三个字节。如果您在第一个 malloc 之后但在分配之前分配了另一块内存,您可能已经覆盖了堆的一部分(取决于您的 malloc 的工作方式)。

这就是为什么指针在 C 中非常危险的原因。

格式语句中的 %d(加上变量的类型)告诉编译器您正在查看一个 int,并访问所有四个字节。

请注意,如果您确实已将值分配给 char,例如字符 *ptr; *ptr = 100000;

然后使用一些编译器(并假设普通字符被视为带符号但默认)它会打印出 -96,而不是 255(或 127)。这是因为编译器不会自动将值限制为可以适合的最大值(有符号字符中为 127,无符号字符中为 255),而是溢出。大多数编译器会抱怨你试图分配一个溢出变量的常量值。

它是 -96 的原因是 100000 % 256 是 160,但作为有符号字符输出为 -(256-160)。

【讨论】:

  • @alex:实际上最后的信息是错误的;它是实现定义的,当您将一个不可表示的值分配给有符号类型时会发生什么,并且它是实现定义的纯 char 是有符号还是无符号。如果使用了unsigned char,则行为将被明确定义。
【解决方案2】:

简短的回答:您通过写入不属于您的内存来调用未定义的行为,因此您永远不知道会得到什么。它可能只是工作,它可能会崩溃,或者它可能会做任何其他事情。在这种情况下,您将int 放在ptr 引用的位置,它将int 的第一个字节写入1 字节分配的区域,并粉碎接下来三个字节中的任何内容。

当您使用%d 格式说明符读回值时,printf 从内存中读取sizeof(int) 字节以将它们打印为int。如果您只想输出一个字节的值,您需要执行以下操作:

printf("%d\n", *(char*)ptr);

也就是说,告诉编译器ptr 引用char,然后获取char 值,该值在参数列表中提升为int,随后由%d 说明符正确输出.

【讨论】:

    【解决方案3】:

    这是 3 字节溢出。像这样从stackoverflow徽标溢出,但在堆上而不是堆栈上。

    【讨论】:

    • 谁说是 3 个字节?你假设sizeof (int) == 4
    【解决方案4】:

    最简单的答案是:不能保证有效。

    基本上,您正在破坏指针周围的内存。

    【讨论】:

      【解决方案5】:

      回答您更新的问题; C“看到” *ptr 即 ptr 的内容,其中 ptr 指向一个整数。所以它从那个ptr读取一个整数。它已经忘记了你只分配了一个字符。这两个部分(分配和访问)没有被您在代码中表达的任何东西联系起来。

      【讨论】:

        【解决方案6】:

        通常,malloc 将分配比单个字节更大的内存粒度,因此您对单个字节的请求将保留一个四舍五入到最接近的 8 个字节或其他内容的内存空间。然后你写信到这个空间,愉快地过去你保留的东西。然后你取消引用指针,带回一个int,然后把它交给printf,它很高兴地把它打印出来。

        正如其他人所说,依赖这种行为是一个坏主意。准确分配你需要的东西,不要超出你的缓冲区。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2017-06-26
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-12-16
          • 2015-01-22
          • 1970-01-01
          相关资源
          最近更新 更多