小甲鱼s1e39测试题第6题,求大佬解答一下
求大佬帮忙解答一下,忙活半天总算是弄出小甲鱼讲的结果a=1,b=2了但是!必须得打印地址之后才能出现在这个结果
如果注释掉打印地址语句,就会变回a=0,b=2了
printf应该不影响内存分配吧,求大佬解答 你不贴代码,怎么讨论问题?难道,需要我自己手敲全部代码然后再陪你讨论? 建议 图文并茂。
还好是代码短。
应该是你自己运行了2段不同的代码,跟注不注释 打印地址那行没关系。
本帖最后由 jackz007 于 2022-10-17 23:00 编辑
char a = 0 , b = 0 ;
int * p = (unsigned int *) & b ;
* p = 258 ;
p 是整型指针,此操作会向变量 b 开始的内存写入整型数 258, 258 十六进制形式是 0x102,我们的电脑都是 Intel 架构,CPU 采用 little endian 模式,整型数 0x102 的 4 个字节在内存中是按:02 01 00 00 的先后顺序排列的,p 指向了 b,那么,b 对应的一个字节是 02 , a 和 b 相邻,如果 a 所占用的内存在 b 的后面,就应该对应字节 01,于是,就打印出了 2 和 1。
如果打印的是 2 和 0,那么,说明 a 对应的内存并不在 b 的后面,而是在 b 的前面,具体在前在后,主要和编译器有关。所以,如果打印的是 2 和 0,那么,只要调整一下 a、b 的定义顺序就可以了:
char b = 0 , a = 0 ;// 把 b 放前面,a 放后面试试
这个代码之所以需要打印 & a 和 & b 的地址,就是为了判断它们俩在内存中的位置到底谁前谁后,从楼主贴出的图片中可以看到,在输出是 2 和 1 的图片下方就是两个变量的内存地址,他们之间显然存在着 b 在前(0x7ffc9607d9b6),a 在后(0x7ffc9607d9b7),地址相差 1 个字节的关系。 本帖最后由 jackz007 于 2022-10-17 23:03 编辑
ba21 发表于 2022-10-17 20:35
建议 图文并茂。
还好是代码短。
应该是你自己运行了2段不同的代码,跟注不注释 打印地址那行没关系。
老大,你是什么编译器,char 型变量也讲究 4 字节对齐呀,楼主要的效果在你那里恐怕是无法看到了,你的 b 和 a 的内存地址居然相差了 4 个字节,完全的不可思议!
看看楼主展示的图片,b(0x7ffc9607d9b6)、a (0x7ffc9607d9b7)内存地址必须只相差一个字节,而且,它们的内存位置必须是 b 在前,a 在后才能看到效果。 jackz007 发表于 2022-10-17 20:27
你不贴代码,怎么讨论问题?难道,需要我自己手敲全部代码然后再陪你讨论?
抱歉抱歉,第一次用不知道怎么贴,我就粘图片了 本帖最后由 Xianjing. 于 2022-10-18 17:35 编辑
ba21 发表于 2022-10-17 20:35
建议 图文并茂。
还好是代码短。
应该是你自己运行了2段不同的代码,跟注不注释 打印地址那行没关系。
我两段代码并没有重新敲啊,就是加了两个斜杠注释掉那句话,结果就变了,所以很不解,还有就是不理解你的这个char为什么占用了四个字节 jackz007 发表于 2022-10-17 20:48
p 是整型指针,此操作会向变量 b 开始的内存写入整型数 258, 258 十六进制形式是 0x102,我们的 ...
我没有注释那一张打印了a的地址,显示的就是a在b之后一个字节的位置,这个时候打印出来就是1,2,但是如果注释掉,就只加了两个斜杠,结果就变了,很不理解,不过小甲鱼的答案原理我能看懂,很感谢你的耐心解答 Xianjing. 发表于 2022-10-18 17:31
我两段代码并没有重新敲啊,就是加了两个斜杠注释掉那句话,结果就变了,所以很不解,还有就是不理解你 ...
char 哪里占了4个字节了?你从哪里看出来的? 本帖最后由 jackz007 于 2022-10-18 19:12 编辑
Xianjing. 发表于 2022-10-18 17:33
我没有注释那一张打印了a的地址,显示的就是a在b之后一个字节的位置,这个时候打印出来就是1,2,但是如 ...
原因终于被我找到了,看看两个可执行代码的 main() 函数反汇编
【有打印地址版本】:
00401350/$55 push ebp
00401351|.89E5 mov ebp, esp
00401353|.83E4 F0 and esp, FFFFFFF0
00401356|.83EC 20 sub esp, 20
00401359|.E8 22060000 call 00401980
0040135E|.C64424 1B 00 mov byte ptr , 0 ; char a = 0
00401363|?C64424 1A 00 mov byte ptr , 0 ; char b = 0
00401368|?8D4424 1A lea eax, dword ptr ; eax = & b
0040136C|?894424 1C mov dword ptr , eax ; p = & b
00401370|?8B4424 1C mov eax, dword ptr ; eax = & b
00401374|?C700 02010000 mov dword ptr , 102 ; * p = 258
0040137A|.0FB64424 1A movzx eax, byte ptr ; eax = a
0040137F|?0FBED0 movsx edx, al ; edx = a
00401382|?0FB64424 1B movzx eax, byte ptr ; eax = b
00401387|?0FBEC0 movsx eax, al ; eax = b
0040138A|?895424 08 mov dword ptr , edx ; edx = b = 2 进堆栈 - 准备打印
0040138E|?894424 04 mov dword ptr , eax ; eax = a = 1 进堆栈 - 准备打印
00401392|?C70424 24304000 mov dword ptr , 00403024 ;ASCII "%d %d"
00401399 ?E8 52080000 call <jmp.&msvcrt.printf> ; 调用 printf()
0040139E .8D4424 1A lea eax, dword ptr
004013A2 ?894424 08 mov dword ptr , eax
004013A6 ?8D4424 1B lea eax, dword ptr
004013AA ?894424 04 mov dword ptr , eax
004013AE .C70424 2B304000 mov dword ptr , 0040302B ;ASCII "%p %p"
004013B5 .E8 36080000 call <jmp.&msvcrt.printf>
004013BA ?B8 00000000 mov eax, 0
004013BF ?C9 leave
004013C0 >C3 retn
【无打印地址版本】:
00401350/$55 push ebp
00401351|.89E5 mov ebp, esp
00401353|.83E4 F0 and esp, FFFFFFF0
00401356|.83EC 20 sub esp, 20
00401359|.E8 02060000 call 00401960
0040135E|.C64424 1F 00 mov byte ptr , 0 ; char a = 0
00401363|.C64424 17 00 mov byte ptr , 0 ; char b = 0
00401368|.8D4424 17 lea eax, dword ptr ; eax = & b
0040136C|.894424 18 mov dword ptr , eax ; p = & b
00401370|.8B4424 18 mov eax, dword ptr ; eax = & b
00401374|.C700 02010000 mov dword ptr , 102 ; * p = 258
0040137A|.0FB64424 17 movzx eax, byte ptr ; eax = b
0040137F|.0FBED0 movsx edx, al ; edx = b
00401382|.0FBE4424 1F movsx eax, byte ptr ; eax = a
00401387|.895424 08 mov dword ptr , edx ; b 进堆栈 - 准备调用 printf()
0040138B|.894424 04 mov dword ptr , eax ; a 进堆栈 - 准备调用 printf()
0040138F|.C70424 24304000 mov dword ptr , 00403024 ; |ASCII "%d %d"
00401396|.E8 35080000 call <jmp.&msvcrt.printf> ; \printf
0040139B|.B8 00000000 mov eax, 0
004013A0|.C9 leave
004013A1\.C3 retn
主要聚焦下面的不同点:
【有打印地址版本】:
0040135E|.C64424 1B 00 mov byte ptr , 0 ; char a = 0
00401363|?C64424 1A 00 mov byte ptr , 0 ; char b = 0
【无打印地址版本】:
0040135E|.C64424 1F 00 mov byte ptr , 0 ; char a = 0
00401363|.C64424 17 00 mov byte ptr , 0 ; char b = 0
有打印地址版本的 a、b 内存地址符合预期,b 在前,a 在后,存储地址相差 1 个字节;无打印地址版本 a、b 内存地址不符合预期,虽然 b 在前,a 在后,符合预期,但是,存储地址却相差了 8 个字节,这样,当 * p = 258 覆盖内存的时候,只能覆盖 b,根本不法覆盖到 a,所以,打印 a、b 的时候,b 是覆盖后的值,a 是原始值,所以,就看到了 2 和 0。
之所以有这种不同的结果,应该和编译器对局部变量的内存分配策略有关,我使用 gcc,和楼主在 Linux 系统下所使用的编译器同源,所以,可以重现楼主在 Linux 系统下看到的相同情况。
基于以上分析结论,只要把 a、b 为 2 个 char 型变量的定义改为一个 2 元素 char 型数组即可完美解决问题
#include <stdio.h>
int main(void)
{
char b = {0} ;
int * p = (int *) b ;
* p = 258 ;
printf("%d %d\n" , b , b) ;
// printf("%p %p\n", & a , & b) ;
}
楼主不妨一试
jackz007 发表于 2022-10-18 18:32
原因终于被我找到了,看看两个可执行代码的 main() 函数反汇编
【有打印地址版本】:
感谢感谢!!! 本帖最后由 jackz007 于 2022-10-19 09:04 编辑
ba21 发表于 2022-10-18 17:45
char 哪里占了4个字节了?你从哪里看出来的?
你贴的图片上有啊,a ( 0018ff44),b (0018ff40) jackz007 发表于 2022-10-19 08:40
你贴的图片上有啊,a ( 0018ff44),b (0018ff40)
32位地址不应该是4个字节吗?
64位地址不应该是8个字节吗?
那何来char占用了4个字节? 本帖最后由 jackz007 于 2022-10-19 17:43 编辑
ba21 发表于 2022-10-19 17:34
32位地址不应该是4个字节吗?
64位地址不应该是8个字节吗?
那何来char占用了4个字节?
看看 3 楼你自己贴的图片,难道不是你自己的代码用 "%p" 打印的 a 和 b 的内存地址吗?
再说了,难道 64 位的操作系统就不能编译和运行 32 位的程序? jackz007 发表于 2022-10-19 17:40
难道不是你自己的代码用 "%p" 打印的 a 和 b 的内存地址吗?
再说了,难道 64 位的操 ...
那打印地址 4个字节有何问题? 按你们说的应该是要打印1个字节才合理? 本帖最后由 jackz007 于 2022-10-19 17:52 编辑
ba21 发表于 2022-10-19 17:44
那打印地址 4个字节有何问题? 按你们说的应该是要打印1个字节才合理?
我说的是 a 和 b 的内存地址相差了 4 个字节很意外,这个距离本来应该是 1 个字节的,并不是说指针长度是 4 个字节有什么不对。 jackz007 发表于 2022-10-19 17:49
我说的是 a 和 b 的内存地址相差了 4 个字节很意外,这个距离本来应该是 1 个字节的,并不是说 ...
编译器内存优化相关。
内存对齐知识。 如果注释掉打印地址语句,就会变回a=0,b=2了
页:
[1]