请教一个很基础的变量内存分配问题

2021-08-01 18:26:29 +08:00
 yezheyu

我声明一个整型变量 a = 10

会在栈空间开辟一块内存来存 10 这个值,假设地址是 0x002

这也就意味着变量 a 的值的地址是 0x002,那么我想问下变量 a 本身存在哪?

程序是怎么知道 0x002 这个地址在命名空间中叫 a,总该有个地方存 a 吧?

是在栈区起始位置以类似 a:0x002 这种把变量名和其值的地址放在一起存起来吗?还是其它什么机制?

最近看了很多博客,都没提到这点,有老哥帮忙解释下吗?

4075 次点击
所在节点    程序员
44 条回复
happinessnch
2021-08-02 09:35:13 +08:00
- a 本身存在哪?
a 只是一个代号,不需要存储,无论是解释型还是编译型,a 作为一个代号,在解释或者编译的过程中,就会履行代号的职责,替换成代替的内容。

- a 程序怎么知道 0x002 叫 a ?
a 为什么是 0x002,这个是“程序”分配给 a 的,a 是给人读的,不是给“程序”读的,对于程序员来说 a 的可读性比 0x002 好的多,是方便程序写出规模更大的程序所需要的。

- 其值的地址放在一起存起来吗?
前面说了 a 对于“程序”来说无关紧要,根本不需要存,而 0x002 则是操作系统的虚拟地址空间里的地址,虚拟地址会映射到实际的物理内存上。
lackywind
2021-08-02 09:40:13 +08:00
直接给你上一段 C++的反汇编吧,看了应该就明白了。。。
![]( https://i.bmp.ovh/imgs/2021/08/55d10f51f1327bd9.png)
nuk
2021-08-02 09:46:37 +08:00
每个 function 都有自己的 local symbol,但是仅限栈地址,如果寄存器传值就看不到了。
nuk
2021-08-02 09:58:03 +08:00
用 readelf --dump-debug 就可以看到,这里我定义两个局部变量 x,y,调试信息里就有这两个变量相对栈指针的偏移量。
<2><4e>: Abbrev Number: 3 (DW_TAG_variable)
<4f> DW_AT_name : x
<51> DW_AT_decl_file : 1
<52> DW_AT_decl_line : 3
<53> DW_AT_type : <0x67>
<57> DW_AT_location : 2 byte block: 91 5c (DW_OP_fbreg: -36)
<2><5a>: Abbrev Number: 3 (DW_TAG_variable)
<5b> DW_AT_name : y
<5d> DW_AT_decl_file : 1
<5e> DW_AT_decl_line : 4
<5f> DW_AT_type : <0x6e>
<63> DW_AT_location : 2 byte block: 91 60 (DW_OP_fbreg: -32)
weiwenhao
2021-08-02 10:22:17 +08:00
不考虑寄存器分配的情况下, 你在函数定义了
int a = 1
int b = 2
int c = 3

实际上就是。
mov 1 => EBP+4 字节
mov 2 => EBP+8 字节
mov 3 => EBP+12 字节

在编译阶段就已经确定了,a = EBP + 4 , b = EBP + 8....了

gcc -s main.c 可以看到
iOCZ
2021-08-02 10:52:49 +08:00
其实栈底和栈顶限制了栈的区域,所谓的本地变量的地址也只是汇编中写死的栈顶-offset 值而已
mxT52CRuqR6o5
2021-08-02 11:15:30 +08:00
就比如 ebp-0x0004 这个地址就是 a 的地址,所有用到 a 的地方就都直接用 ebp-0x0004,汇编层面就没有变量 a 这个概念了
icyalala
2021-08-02 11:43:36 +08:00
一般来说,在未优化的编译下,这种变量是放在函数的栈帧 (Stack Frame) 里的。
但实际编译优化后,根据你的使用方法,甚至可以被优化为一条 lea 指令,所以不要在这里纠结了。
多试试: https://godbolt.org/z/5v9MW6d78
netwjx
2021-08-02 12:01:45 +08:00
楼主需要补课了 编译原理
Kasumi20
2021-08-02 12:06:50 +08:00
这是魔怔了,建议学一下汇编语言
dangyuluo
2021-08-02 12:39:26 +08:00
你是想知道编译器(链接器)是怎么知道 a 这个符号对应哪个内存地址的?用 objdump 看看 ELF 的符号表就知道了
Feep
2021-08-02 12:42:01 +08:00
1.变量 a 就存在以地址 0x002 为起始地址的一片内存上,这片空间的大小由声明 /定义的变量类型决定。
2.计算机执行程序不需要知道变量的名字,只需要对象的地址。编译器会把变量名和地址操作翻译成汇编指令,实际上经过编译后的汇编代码已经没有变量名信息了。
3.不知道怎么解释了……因为内存的栈区不存变量名。
发现有什么不对的地方请老哥们帮忙纠正。
FrankFang128
2021-08-02 12:56:38 +08:00
看一下《编译原理》比较好,a 在 AST 里存在,但最后执行的时候可能有 a,也可能没有。
zxCoder
2021-08-02 15:20:09 +08:00
反对楼上动不动就让人看编译原理的。。。。

这玩意跟编译原理没啥关系,这个问题在初学 c/c++这种语言的时候很常见

特别是如果先学了 java,思想更难掰过来,不过仔细思考一下应该就能理解了
3dwelcome
2021-08-02 16:03:36 +08:00
我怎么感觉,局部变量都是每一次进入函数体后,动态赋值的呢。

也就程序编译后,有一个只读数据区,专门存不变的数值,比如楼主的 10 。

运行时,程序把 10 从只读数据区取出来,动态复制到 0x002 这个位置。而 0x002 这种地址,每次调用函数都会变,gcc 是通过_alloc 函数,动态分配内存的(通过修改 ESP 偏移)。
3dwelcome
2021-08-02 16:14:31 +08:00
试了 28 楼的在线编译器,怎么和我本地的 clang 结果不一样呢?

我的只要函数体内局部变量稍大,有栈大小渗出风险,都会智能通过调用_alloc 来二次优化栈空间分配函数。

而在线编译器就是暴力偏移 stack pointer,哪怕局部变量大的离谱,明显运行结果会导致栈渗出。
libook
2021-08-02 17:08:46 +08:00
我觉得这个就是编译原理的知识范畴,我自己也没有完整学过编译原理,但是之前学 Rust 的时候为了理解 stack 和 heap 的作用专门去挑了编译原理对应的章节去学习,你可以看 B 站的这个公开课,只看 9.2 章节应该就能解决你的疑惑了 https://www.bilibili.com/video/BV11t411V74n?p=41

简单来讲就是编译完成后,机器码中可能就已经用 0x002 这个地址来替代 a 这个标识符了。
然后每个作用域都有个基地址,0x002 是在这个基地址基础上的相对地址,这样你不同作用域下的 a 实际上就在不同的地址上。
再然后值可能存在 stack 上,也可能存在 heap 上,这个取决于你目前用的编译器的设计,一种简单的设计是:stack 中 0x002 这个相对地址上存储的值是 heap 上的相对起始地址、长度,然后在 heap 的这个区域里存储 0x002 的值,可以很方便地修改;当然如果这是个常量,那么 stack 上 0x002 这个相对地址可能就直接存的值。
kop1989
2021-08-02 18:02:28 +08:00
这个 a,是你起的名字,也是为了给你看的。
对于计算机而言,他不需要知道 a 叫什么。

编译的时候已经把“变量 a 等于 10”这个逻辑替换成了“栈内存第零位地址是 0xxx”(这个 0xxx 对应的是常量 10 ),任何相同类型的 10 都是这个地址。

所以你后续的比如 a++,就会翻译为“栈内存的第 0 位的地址从 0xxx 改为 0xxy”。
kop1989
2021-08-02 18:08:29 +08:00
换句话说,相同的变量的名字,其实体现在程序里就是一个相同的位置。
这个位置未见得一定在栈。

举个尴尬的例子,你点外卖的时候,你会选择地址为“公司”、“家庭”等等 tag 。
但对于外卖小哥而言,他分辨的是你住在 xx 区 xx 栋。至于说你管这个叫“公司”还是“家”,对他没有意义。而且这个 tag 对最终的外卖送达也没有任何影响。

程序也是如此,变量名只是一个 tag,并不影响最终的程序逻辑。
xylxAdai
2021-08-02 18:15:30 +08:00
一般在汇编里面直接就变成相对地址了。
在 debug 版本存在符号表的可执行文件中,可能还存在了 a 这个符号,这个符号一般在符号表内,可以自己看看这个可执行文件的内容

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/793017

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX