Ahm*_*otb 11 c gcc operating-system memory-management
我一直在youtube上学习这门课程,它正在讨论一些程序员如何利用那些知识来记忆如何做聪明的东西......讲座中的一个例子是那样的
#include <stdio.h>
void makeArray();
void printArray();
int main(){
makeArray();
printArray();
return 0;
}
void makeArray(){
int array[10];
int i;
for(i=0;i<10;i++)
array[i]=i;
}
void printArray(){
int array[10];
int i;
for(i=0;i<10;i++)
printf("%d\n",array[i]);
}
Run Code Online (Sandbox Code Playgroud)
这个想法只要两个函数在堆栈段上具有相同的激活记录大小就可以工作并打印0到9之间的数字......但实际上它打印的内容类似于
134520820
-1079626712
0
1
2
3
4
5
6
7
Run Code Online (Sandbox Code Playgroud)
乞讨总会有那两个值...任何人都可以解释一下??? 我在linux中使用gcc
准确的讲座网址从5:15开始
pax*_*blo 23
对不起,但对于那段代码并没有什么聪明,使用它的人非常愚蠢.
附录:
或者,有时候,有时候,非常聪明.观看了在问题更新中链接的视频,这不是一些违反规则的流氓代码猴子.这家伙明白他做得很好.
它需要深入了解生成的底层代码,并且如果您的环境发生变化(如编译器,体系结构等),可能很容易中断(如此处所述和所见).
但是,只要你掌握了这些知识,你就可以逃脱它.这不是我建议除了退伍军人以外的任何人,但我可以看到它在非常有限的情况下占有一席之地,说实话我毫无疑问偶尔会有点......务实......比我应该有的在我自己的职业生涯中:-)
现在回到你的常规节目......
它在架构,编译器,编译器版本之间是不可移植的,甚至可能在编译器的同一版本中的优化级别,以及未定义的行为(读取未初始化的变量).
如果您想了解它,最好的办法是检查编译器输出的汇编代码.
但总的来说,最好的办法就是忘记它并编写标准代码.
例如,此脚本显示gcc如何在不同的优化级别具有不同的行为:
pax> gcc -o qq qq.c ; ./qq
0
1
2
3
4
5
6
7
8
9
pax> gcc -O3 -o qq qq.c ; ./qq
1628373048
1629343944
1629097166
2280872
2281480
0
0
0
1629542238
1629542245
Run Code Online (Sandbox Code Playgroud)
在gcc的高优化级别(我称之为疯狂的优化级别),这就是makeArray功能.基本上可以看出阵列没有被使用,因此优化了它的初始化.
_makeArray:
pushl %ebp ; stack frame setup
movl %esp, %ebp
; heavily optimised function
popl %ebp ; stack frame tear-down
ret ; and return
Run Code Online (Sandbox Code Playgroud)
我实际上有点惊讶gcc甚至在那里留下了函数存根.
更新:正如Nicholas Knight在评论中指出的那样,该函数仍然存在,因为它必须对链接器可见 - 使得函数静态导致gcc也删除存根.
如果你在下面的优化级别0检查汇编代码,它会给出一个线索(这不是实际原因 - 见下文).检查下面的代码,你会发现两个函数的堆栈框架设置是不同的,尽管它们传入的参数完全相同,并且相同的局部变量:
subl $48, %esp ; in makeArray
subl $56, %esp ; in printArray
Run Code Online (Sandbox Code Playgroud)
这是因为printArray分配了一些额外的空间来存储printf格式字符串的地址和数组元素的地址,每个字节有四个字节,这占据了8个字节(两个32位值)的差异.
这是你的阵列printArray()被两个值关闭的最可能的解释.
这是优化级别0的两个功能,供您享受:-)
_makeArray:
pushl %ebp ; stack fram setup
movl %esp, %ebp
subl $48, %esp
movl $0, -4(%ebp) ; i = 0
jmp L4 ; start loop
L5:
movl -4(%ebp), %edx
movl -4(%ebp), %eax
movl %eax, -44(%ebp,%edx,4) ; array[i] = i
addl $1, -4(%ebp) ; i++
L4:
cmpl $9, -4(%ebp) ; for all i up to and including 9
jle L5 ; continue loop
leave
ret
.section .rdata,"dr"
LC0:
.ascii "%d\12\0" ; format string for printf
.text
_printArray:
pushl %ebp ; stack frame setup
movl %esp, %ebp
subl $56, %esp
movl $0, -4(%ebp) ; i = 0
jmp L8 ; start loop
L9:
movl -4(%ebp), %eax ; get i
movl -44(%ebp,%eax,4), %eax ; get array[i]
movl %eax, 4(%esp) ; store array[i] for printf
movl $LC0, (%esp) ; store format string
call _printf ; make the call
addl $1, -4(%ebp) ; i++
L8:
cmpl $9, -4(%ebp) ; for all i up to and including 9
jle L9 ; continue loop
leave
ret
Run Code Online (Sandbox Code Playgroud)
更新:罗迪在评论中指出.这不是您的具体问题的原因,因为在这种情况下,数组实际上是在内存中的同一位置(%ebp-44与%ebp被跨越两个调用相同).我试图指出的是,具有相同参数列表和相同本地参数的两个函数不一定最终具有相同的堆栈帧布局.
所需要的只是printArray交换其局部变量的位置(包括开发人员未明确创建的任何临时变量),你会遇到这个问题.
| 归档时间: |
|
| 查看次数: |
1132 次 |
| 最近记录: |