3

来自Stroustrup - 编程:使用 C++ 的原则和实践一书。在§17.3中,关于内存、地址和指针应该允许将 a 分配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;

从图形上看,我们有这样的东西:

char 地址到 int 指针

引用来源:

如果编译器允许该代码,我们就会12345&ch3. 那肯定会改变附近一些内存的值,例如ch2or ch4,或者我们会覆盖pi它自己的一部分。

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


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

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

4

4 回答 4

3
char ch3 = 'c';
int* pi = &ch3;

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

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

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

示例:某些处理器需要int *偶数,如果&ch3是奇数,则存储地址可能会失败,并且取消引用地址肯定会失败:总线错误


下一个肯定是 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 定义的。

于 2020-08-13T18:01:45.227 回答
2

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

或者我们会覆盖 pi 本身的一部分

可以这样想,因为ints 大于chars,如果 aint*指向存储 a 的地址位置,char当您尝试将整数值分配给该位置时,将会出现内存溢出,因为您只有一个字节的内存已分配,但分配了 4 个字节的数据。即您不能将 4 个字节的数据放入一个字节中,因此其他 3 个字节将放在某个地方。

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

于 2020-08-13T17:28:01.163 回答
2

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

--------------------------
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

于 2020-08-13T17:50:31.093 回答
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 0002 00 00 00与人们预期的不同。

于 2020-08-13T17:45:17.200 回答