如何在C中取消声明(删除)变量?

24 c

就像我们使用宏一样:

#undef SOMEMACRO 
Run Code Online (Sandbox Code Playgroud)

我们还可以取消声明删除 C中的变量,以便我们可以节省大量内存吗?

我知道malloc()free(),但我想完全删除变量,以便如果我使用printf("%d", a);我应该得到错误

test.c:4:14: error: ‘a’ undeclared (first use in this function)
Run Code Online (Sandbox Code Playgroud)

tas*_*oor 38

不,但您可以创建小的最小范围来实现此目的,因为当范围退出时,所有范围的局部变量都会被销毁.像这样的东西:

void foo() {
    // some codes
    // ...
    {    // create an extra minimum scope where a is needed
        int a;
    }
    // a doesn't exist here
}
Run Code Online (Sandbox Code Playgroud)

  • @MikeNakis - 你曾经使用过嵌入式系统吗?我曾经有一个只有128字节堆栈的控制器和一个在功能之间非常严重地重叠堆栈变量的编译器.当由于函数指针使用而无法正确找出重叠的堆栈帧时,它会导致运行时错误.美好时光 :) (16认同)
  • @MikeNakis:"*编译器从未这样做[重用堆栈空间],不要这样做,并且很可能永远不会*"我的GCC确实重用了这里描述的堆栈:/sf/answers/193188411/刚试过这个:`void foo(void){{char a [1024*1024*4] = {0}; } {char b [1024*1024*4] = {0}; ``void void foo(void){char a [1024*1024*4] = {0}; char b [1024*1024*4] = {0}; }`没有. (11认同)
  • 编译器不需要有限的重用内存寄存器范围.它只是使用流程图来推断非重叠的生命周期.它甚至可以在不同的时间将相同的变量放在不同的位置/寄存器中(至少在不采用地址时).由于编译器在优化过程中经常使用[SSA表单](https://en.wikipedia.org/wiki/Static_single_assignment_form),因此解耦存储位置和变量对编译器来说非常自然. (11认同)
  • @MikeNakis为范围不重叠的变量重用堆栈空间不需要调整堆栈指针.它基本上将堆栈框架的那部分视为"联合". (9认同)
  • 实际上,如果您以这种方式定义不在范围内重叠的变量,这可能允许编译器重用堆栈变量空间.虽然编译器很可能进行这样的优化,即使你不这样做. (7认同)
  • @Mike Nakis是的,他们通常会在函数入口处分配堆栈.但是显示变量的确切范围可能有助于编译器优化寄存器的使用.因此,如果变量仅在有限范围内可见,并且它不是易失性的,则编译器将尝试将其保留在寄存器中,而不是为其分配堆栈. (2认同)
  • @MikeNakis:为了补充alk的答案,gcc 4.7因引入更严格的临时使用堆栈空间的重用策略而闻名.在发布时依赖于未定义的行为打破了很多程序.我同意节约不一定"大",但确实减少了框架尺寸. (2认同)
  • @MikeNakis:GCC,Clang和Visual Studio都这样做.我已经挖出了由此引起的编码错误.近年来,由于在移动平台上节省内存的重要性日益增加,他们一直在努力做到这一点.它们都没有调整堆栈指针:它们只是为不同的变量使用相同的空间,具体取决于您在函数中的位置. (2认同)

imm*_*tal 23

这不是问题的直接答案,但它可能带来一些顺序和理解为什么这个问题没有正确的答案以及为什么在C中"删除"变量是不可能的.

点#1什么是变量?

变量是程序员为内存空间分配名称的一种方式.这很重要,因为这意味着变量不必占用任何实际空间!只要编译器有办法跟踪有问题的内存,定义的变量就可以通过多种方式进行转换,根本不占用任何空间.考虑:const int i = 10;编译器可以轻松地选择将所有实例替换i为立即值.i在这种情况下会占用0个数据存储器(取决于架构,它可能会增加代码大小).或者,编译器可以将值存储在寄存器中,并且不再使用堆栈或堆空间."取消定义"主要存在于代码中的标签并不一定是在运行时中没有意义.

点#2变量存储在哪里?

在#1点之后你已经明白这不是一个容易回答的问题,因为编译器可以在不破坏逻辑的情况下做任何想做的事情,但一般来说,变量存储在堆栈中.堆栈的工作原理对您的问题非常重要.当一个函数被调用时,机器获取CPU指令指针和当前堆栈指针的当前位置,并将它们推入堆栈,将堆栈指针替换为堆栈上的下一个位置.然后它跳转到被调用函数的代码中.

该函数知道它有多少变量以及它们需要多少空间,因此它移动帧指针以捕获可占用所有函数变量的帧,然后只使用堆栈.为简化起见,函数从一开始就为其所有变量捕获足够的空间,并且每个变量都具有从函数堆栈帧*开头的明确定义的偏移量.变量也一个接一个地存储.虽然你可以在这个动作之后操纵帧指针,但是它太昂贵而且大多没有意义 - 运行代码只使用最后一个堆栈帧,并且如果需要可以占用所有剩余堆栈(堆栈在线程启动时分配)所以"释放"变量带来的好处很少.从堆栈帧的中间释放变量将需要碎片整理操作,这将非常耗费CPU并且无法恢复几个字节的存储器.

第3点:让编译器完成它的工作

这里的最后一个问题是一个简单的事实,即编译器可以在优化程序方面做得比你可能做得好得多.鉴于需要,编译器可以检测变量范围和重叠内存,这些内存无法同时访问以减少程序内存消耗(-O3编译标志).你不需要"释放"变量,因为无论如何编译器都可以在你不知情的情况下做到这一点.

这是为了补充我之前所说的关于变量太小而无关紧要的事实以及没有机制来实现你所要求的事实.


*支持动态大小的数组的语言可能会改变堆栈帧,以便仅在计算数组大小后为该数组分配空间.

  • 第3点特别相关; 一旦不再需要,编译器会定期回收堆栈空间中的其他变量,就像它们对寄存器一样; 这实际上是你在进行逆向工程时必须注意的事情 - 即使你理解了堆栈上给定位置所指的局部变量,一旦被覆盖它也可能是一个完全不同的变量. (3认同)

Mik*_*kis 16

在C和绝大多数编程语言中都无法做到这一点,当然在我所知道的所有编程语言中都是如此.

你不会拯救"大量的记忆".如果你做了这样的事情你将节省的内存量将是微不足道的.小.不值得谈论.

以这种方式促进变量清除的机制可能比你要清除的变量占用更多的内存.

回收单个变量代码的代码调用也会占用比变量本身更多的空间.

因此,如果有一个魔术方法purge()可以清除变量,那么不仅实现的purge()内存大于你希望通过清除程序中的变量来回收的任何内存量,而且在int a; purge(a);调用中purge()会占用比a自身更多的空间..

那是因为你所谈论的变量非常小.printf("%d", a);您提供的示例显示您正在考虑以某种方式回收单个int变量占用的内存.即使有办法做到这一点,你也可以保存4字节的顺序.这些变量占用的内存总量非常小,因为它是程序员通过手工输入声明声明的变量数量的直接函数.在你声明一些int占据一定内存量的变量之前,在键盘上输入数字需要多年才能做任何事情,然后无意识地声明变量.

  • 也可以在Python,Matlab等中使用...否则,这个答案相当远,甚至没有错误的领域. (3认同)

Ded*_*tor 5

好吧,你可以使用blocks({ })并尽可能晚地定义一个变量来限制它存在的范围.

但除非该变量的地址被占用,这样做有没有对生成的代码影响的所有,因为它必须保持变量的值范围的编译器的确定并不显著影响.

如果采用变量的地址,则逃逸分析的失败(主要是由于内联障碍,如单独的编译或允许语义插入)可能使编译器认为它必须保持活着直到块中的后期而不是严格的必要.这很少有意义(不要担心少数几个ints,而且通常只需要几行代码来保持它的存活时间是微不足道的),但最好还是记住它可能很重要的罕见情况.