编译器如何知道是否在堆或堆栈上分配了某些内容,例如,如果我在函数中创建了一个变量并返回了变量的地址,编译器会警告我“函数返回局部变量的地址” :
#include <stdio.h>
int* something() {
int z = 21;
return &z;
}
int main() {
int *d = something();
return 0;
}
我理解为什么这是一个警告,因为当函数退出时,堆栈框架不再存在,如果你有一个指向该内存的指针并且你改变了它的值,你将导致分段错误。我想知道编译器将如何知道该变量是否通过分配内存。 malloc,或者它如何判断它是否是堆栈中的局部变量?
答案 0 :(得分:2)
编译器构建一个语法树,从中可以分析源代码的每个部分。
它构建一个symbol table,它与每个符号相关联定义了一些信息。这在许多方面都是必需的:
一旦你有这个符号表,很容易知道你是否试图返回一个局部变量的地址,因为你最终有一个像
这样的结构ReturnStatement
+ UnaryOperator (&)
+ Identifier (z)
因此编译器可以轻松检查标识符是否是本地堆栈变量。
请注意,这些信息在理论上可以按照任务传播,但在实践中我并不认为许多编译器会这样做,例如,如果你这样做
int* something() {
int z = 21;
int* pz = &z;
return pz;
}
警告消失了。通过静态代码流分析,您可以证明pz
只能引用局部变量,但在实践中并不会发生。
答案 1 :(得分:2)
你问题中的例子很容易理解。
int* something() {
int z = 21;
return &z;
}
return
语句中的表达式。它采用标识符z
。z
的位置。哦,这是一个局部变量。并非所有情况都会像这一样容易,如果编写足够奇怪的代码,很可能会欺骗编译器给出误报或否定。
如果你对这种东西感兴趣,你可能会喜欢看CppCon'15中给出的一些谈话,其中C ++代码的静态分析是一个大问题。一些非凡的会谈:
答案 2 :(得分:1)
我想知道编译器将如何知道该变量是什么 通过分配内存。 malloc,或它如何判断它是否是本地的 堆栈上的变量?
编译器必须分析所有代码并从中生成机器代码。
当需要调用函数时,编译器必须推送堆栈上的参数(或为它们保留寄存器),更新堆栈指针,查看是否存在局部变量,初始化堆栈上的参数并更新堆栈指针再次。
很明显,编译器知道在堆栈上推送的局部变量。
答案 3 :(得分:1)
编译器知道什么内存块保存当前堆栈。每次调用一个函数时,它都会创建一个新的堆栈并移动前一帧并适当地堆栈指针,这有效地为它提供了内存中当前堆栈的起点和终点。检查你是否试图返回一个指向即将被释放的内存的指针,这个设置相对简单。