编译器如何知道堆栈或堆上是否分配了某些内容?
How does a compiler know if something is allocated on the stack or heap?
编译器如何知道堆或堆栈上是否分配了某些内容,例如,如果我在函数中创建了一个变量并返回了该变量的地址,编译器会警告我 "function returns address of a local variable":
#include <stdio.h>
int* something() {
int z = 21;
return &z;
}
int main() {
int *d = something();
return 0;
}
我明白为什么这是一个警告,因为当函数退出时,堆栈帧不再存在,如果您有指向该内存的指针并更改它的值,则会导致分段错误。我想知道编译器如何知道该变量是否正在通过分配内存。 malloc,或者它如何判断它是否是堆栈上的局部变量?
编译器构建语法树,从中可以分析源代码的每一部分。
它构建了一个 symbol table 关联到每个符号定义的一些信息。这在很多方面都是必需的:
- 查找未声明的标识符
- 检查类型是否可转换
- 等等
一旦你有了这个符号 table 就很容易知道你是否正在尝试 return 局部变量的地址,因为你最终有一个像
这样的结构
ReturnStatement
+ UnaryOperator (&)
+ Identifier (z)
因此编译器可以轻松检查标识符是否为局部堆栈变量。
请注意,这些信息在理论上可以随着赋值传播,但实际上我认为很多编译器都不会这样做,例如,如果您这样做了
int* something() {
int z = 21;
int* pz = &z;
return pz;
}
警告消失。通过静态代码流分析,您可以证明 pz
只能引用局部变量,但实际上不会发生。
What I wonder is how the compiler will know if that variable is
allocating memory via. malloc, or how it can tell if it's a local
variable on the stack?
编译器必须分析所有代码并从中生成机器码。
当需要调用函数时,编译器必须将参数压入堆栈(或为它们保留寄存器),更新堆栈指针,查看是否有局部变量,初始化堆栈中的那些并更新堆栈指针。
很明显,编译器知道局部变量被压入到堆栈中。
你问题中的例子很容易理解。
int* something() {
int z = 21;
return &z;
}
- 查看
return
语句中的表达式。它采用标识符的地址 z
.
- 找出
z
的声明位置。哦,是局部变量。
并非所有情况都像这个一样简单,如果您编写的代码足够奇怪,您很可能会欺骗编译器给出误报或误报。
如果您对这类内容感兴趣,您可能会喜欢观看 CppCon'15 上的一些演讲,其中对 C++ 代码的静态分析很重要。一些精彩的演讲:
编译器知道哪个内存块保存着当前堆栈。每次调用一个函数时,它都会创建一个新堆栈并适当地移动前一帧和堆栈指针,这有效地为其提供了内存中当前堆栈的起点和终点。考虑到该设置,检查您是否正在尝试 return 指向即将释放的内存的指针相对简单。
编译器如何知道堆或堆栈上是否分配了某些内容,例如,如果我在函数中创建了一个变量并返回了该变量的地址,编译器会警告我 "function returns address of a local variable":
#include <stdio.h>
int* something() {
int z = 21;
return &z;
}
int main() {
int *d = something();
return 0;
}
我明白为什么这是一个警告,因为当函数退出时,堆栈帧不再存在,如果您有指向该内存的指针并更改它的值,则会导致分段错误。我想知道编译器如何知道该变量是否正在通过分配内存。 malloc,或者它如何判断它是否是堆栈上的局部变量?
编译器构建语法树,从中可以分析源代码的每一部分。
它构建了一个 symbol table 关联到每个符号定义的一些信息。这在很多方面都是必需的:
- 查找未声明的标识符
- 检查类型是否可转换
- 等等
一旦你有了这个符号 table 就很容易知道你是否正在尝试 return 局部变量的地址,因为你最终有一个像
这样的结构ReturnStatement
+ UnaryOperator (&)
+ Identifier (z)
因此编译器可以轻松检查标识符是否为局部堆栈变量。
请注意,这些信息在理论上可以随着赋值传播,但实际上我认为很多编译器都不会这样做,例如,如果您这样做了
int* something() {
int z = 21;
int* pz = &z;
return pz;
}
警告消失。通过静态代码流分析,您可以证明 pz
只能引用局部变量,但实际上不会发生。
What I wonder is how the compiler will know if that variable is allocating memory via. malloc, or how it can tell if it's a local variable on the stack?
编译器必须分析所有代码并从中生成机器码。
当需要调用函数时,编译器必须将参数压入堆栈(或为它们保留寄存器),更新堆栈指针,查看是否有局部变量,初始化堆栈中的那些并更新堆栈指针。
很明显,编译器知道局部变量被压入到堆栈中。
你问题中的例子很容易理解。
int* something() {
int z = 21;
return &z;
}
- 查看
return
语句中的表达式。它采用标识符的地址z
. - 找出
z
的声明位置。哦,是局部变量。
并非所有情况都像这个一样简单,如果您编写的代码足够奇怪,您很可能会欺骗编译器给出误报或误报。
如果您对这类内容感兴趣,您可能会喜欢观看 CppCon'15 上的一些演讲,其中对 C++ 代码的静态分析很重要。一些精彩的演讲:
编译器知道哪个内存块保存着当前堆栈。每次调用一个函数时,它都会创建一个新堆栈并适当地移动前一帧和堆栈指针,这有效地为其提供了内存中当前堆栈的起点和终点。考虑到该设置,检查您是否正在尝试 return 指向即将释放的内存的指针相对简单。