【问题标题】:Assign char address to an int pointer, and consequently write an int-size piece of memory将 char 地址分配给 int 指针,然后写入 int 大小的内存块
【发布时间】:2020-12-03 14:29:19
【问题描述】:

来自Stroustrup - 编程:使用 C++ 的原则和实践一书。在§17.3中,关于内存、地址和指针应该允许char*分配给int*

char ch1 = 'a';
char ch2 = 'b';
char ch3 = 'c';
char ch4 = 'd';
int* pi = &ch3;   // point to ch3, a char-size piece of memory
*pi = 12345;      // write to an int-size piece of memory
*pi = 67890;

我们有这样的图形:

引用来源:

如果编译器允许该代码,我们就会将12345 写入从&ch3 开始的内存。这肯定会改变附近一些内存的值,例如ch2ch4,或者我们会覆盖pi 本身的一部分。

在这种情况下,下一个赋值 *pi = 67890 会将 67890 放在内存的某个完全不同的部分。


我不明白,为什么下一个作业会把它放在:内存的某个完全不同的部分?存储在int *pi 中的地址仍然是&ch3,因此分配将覆盖该地址的内容,即12345。为什么不是这样?

拜托,你能帮帮我吗?非常感谢!

【问题讨论】:

  • &ch3 导致 char* 不是 int* ,因此如果没有 reinterpret_cast 我不会这样做,那么分配是非法的。但我认为他们的观点是,如果sizeof(int) > sizeof(char) 然后写入pi 将写入ch3 分配的内存,一直到pi 本身的前几个字节(见图。因此下一个derefence 将是其他地址,因为 pi 指针本身被意外修改。
  • 这只是demons fly out of your nose 时发生的简单示例。第一个赋值(部分)破坏了pi 本身的值。
  • reinterpret_cast 是通往许多被某些人认为是……不自然的能力的途径
  • @FrançoisAndrieux 这本书使用了 C++11 和 C++14。
  • @JB-Franco 我已经删除了 C 标签。

标签: c++ pointers memory-address dereference


【解决方案1】:

您似乎跳过了您引用的部分解释:

否则我们会覆盖 pi 本身的一部分

这样想,因为ints大于chars,如果一个int*指向一个存储char的地址位置,当你尝试分配一个该位置的整数值,因为您只分配了一个字节的内存,但分配了 4 个字节的数据。也就是说,你不能将 4 个字节的数据放入一个字节中,所以其他 3 个字节会放在某个地方。

然后假设溢出的字节部分改变了存储在pi 中的值。现在下一个分配将转到一个随机内存位置。

【讨论】:

  • > 假设溢出的字节部分改变了存储在pi 中的值。 - 因此存储在其中的地址将被破坏。 > 现在下一个任务将转到一个随机存储位置。 - 如果*pi 因损坏而无法解析上述地址,则内容将插入到编译器提供的新地址中。对吗?
  • 不,不是。地址只是数字,因此打个比方,假设 pi 中的原始地址是 125 smith street。假设溢出破坏了 pi,因此将其更改为 349 smith street。如果你去新地址,你可能会到任何地方,包括城外。实际上假设pi = 8864,溢出会破坏该值,将其更改为某个随机值,例如pi = 4264。然后下次你写 pi 时你写的是修改后的地址,这是一个随机值。
  • @JB-Franco “现在下一个分配将转到随机内存位置。” C 未指定。编译器无需重新读取 pi 而是使用相同的指针值。这里有很多未定义的行为。
  • 考虑到回复必须由 OP 答案中的文字构成。问题是它如何或为什么会去一个随机位置,所以我们必须假设这是编译器重新读取 pi 的情况。
【解决方案2】:

假设内存地址布局为:

0 1 2 3 4 5 6 7

从左边开始,0、1、2 和 3 是字符。从右边开始,4、5、6 和 7 是一个 int*。 十六进制中每个字节中的值可能是:

61 62 63 64 02 00 00 00

注意前四个是 ascii 值,后四个是ch3 的地址。写*pi = 12345; 改变值是这样的:

61 62 39 30 00 00 00 00

0x39300000 是小端十六进制的 12345。

下一次写入*pi = 67890; 将从内存地址00 00 00 00 开始,而不是预期的02 00 00 00

【讨论】:

    【解决方案3】:

    首先,您必须了解一切都是数字,即charintint* 都包含数字。内存地址也是数字。假设当前示例已编译,并且我们有如下内存:

    --------------------------
    Address | Variable | Value
    --------------------------
    0x01   |    ch1       a
    0x02   |    ch2       b
    0x03   |    ch3       c
    0x04   |    ch4       d
    0x05   |    pi        &ch3 = 0x03
    

    现在让我们取消对pi 的引用并重新为ch3 分配一个新值:

    *pi = 12345;
    

    假设int 是 4 个字节。由于pi 是一个int 指针,它会将一个4 字节的值写入pi 指向的位置。现在,char 只能包含一个字节的值,所以如果我们尝试将 4 个字节写入该位置会发生什么?严格来说,这是未定义的行为,但我会尝试解释作者的意思。

    由于char 不能包含大于1 字节的值,*pi = 12345 将溢出ch3。当发生这种溢出时,4 个字节中剩余的 3 个字节可能会被写入附近的内存位置。我们附近有哪些内存位置? ch4pi 本身! ch4 也只能包含 1 个字节,这给我们留下了 2 个字节,下一个位置是 pi 本身。意思是pi 会覆盖它自己的值!

    --------------------------
    Address | Variable | Value
    --------------------------
    0x01   |    ch1       a
    0x02   |    ch2       b
    0x03   |    ch3       12  //12 ended up here
    0x04   |    ch4       34  //34 ended up here
    0x05   |    pi        &ch3 = 0x03 // 5 gets written here
    

    如您所见,pi 现在指向的内存地址肯定不是ch3

    【讨论】:

    • 很好的解释,但有一个小问题——*pi = 12345 分配了一个十进制值,而您将其分解为12345,就好像它是十六进制一样跨度>
    • @Human-Compiler 我知道,但只是提出一个观点并懒惰大声笑。
    【解决方案4】:
    char ch3 = 'c';
    int* pi = &ch3;
    

    应该允许将 char* 分配给 int*:

    不完全 - 存在对齐问题。这是未定义的行为(UB)当

    如果结果指针未正确对齐引用类型,则行为未定义。 C17dr § 6.3.2.3 7

    示例:某些处理器要求 int * 为偶数,如果 &ch3 为奇数,则存储地址可能会失败,并且取消引用地址肯定会失败:bus error


    下一个肯定是 UB,因为目标在 ch3 的内存之外。
    ch1, ch2, ch4 可能在附近并提供一些合理的未定义行为,但结果是 UB。

    // undefined behavior
    *pi = 12345;      // write to an int-size piece of memory`
    

    当代码尝试写入其边界之外 - 它是 UB,任何事情都可能发生,包括写入相邻数据。

    int *pi中存储的地址还是&ch3

    也许,也许不是。发生了 UB。

    为什么下一个作业会把它放在内存的某个完全不同的部分?

    滥用代码表明pi 本身被*pi = 12345; 覆盖。这可能会发生,也可能不会。是UB。 *pi 的后续使用只是更多的 UB。


    回想一下 UB,你可能会得到你希望的东西,但你可能不会——它不是由 C 定义的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-03-26
      • 2018-02-03
      • 1970-01-01
      • 1970-01-01
      • 2019-05-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多