微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

将自动变量分配给堆栈时,编译器是否按大小对它们进行分组,然后分配连续的位置? 允许编译器将automatic variables在call stack上的位置重新排序,或者重新排列optimize使其仅保留在processor registers中

如何解决将自动变量分配给堆栈时,编译器是否按大小对它们进行分组,然后分配连续的位置? 允许编译器将automatic variables在call stack上的位置重新排序,或者重新排列optimize使其仅保留在processor registers中

我编写了一个简单的程序来了解这一点:

#include <stdio.h>
int main(void)
{
        int i = 3,*x;
        char j = 'c',*y;
        int k = 1,*z;
        x = &i;
        y = &j;
        z = &k;
        fprintf(stdout,"Address in x = %p\n",x);
        fprintf(stdout,"Address in y = %p\n",y);
        fprintf(stdout,"Address in z = %p\n",z);
        fprintf(stdout,"Subtracting x from z = %li\n",z - x);
        return 0;
}

我多次运行了该程序,并且总是得到本质上相似的输出

Address in x = 0x7ffd7e981348
Address in y = 0x7ffd7e981347
Address in z = 0x7ffd7e98134c
Subtracting x from z = 1

...尽管,源代码在两个char j之间定义了ints i and k;对于我的系统编译器组合,始终为ik分配了连续的内存位置,即分别4 bytes1 * sizeof(int)。我假设编译器按type分组,并为其类型分配连续的内存位置。但是,像这样...更改源代码并向混合添加float ...

int main(void)
{
        int i = 3,*w;
        char j = 'c',*x;
        float k = 3.145,*y;
        int l = 1,*z;
        w = &i;
        x = &j;
        y = &k;
        z = &l;
        fprintf(stdout,"Address of i = %p\n",w);
        fprintf(stdout,"Address of j = %p\n","Address of k = %p\n","Address of l = %p\n","Subtracting w from z = %li\n",z - w);
        return 0;
}

输出...

Address of i = 0x7ffe256f04cc
Address of j = 0x7ffe256f04cb
Address of k = 0x7ffe256f04d0
Address of l = 0x7ffe256f04d4
Subtracting w from z = 2

...等等,编译器不是按type分组,而是按size分组。所以,我的问题是

  1. 所有编译器是否总是将具有相同大小的变量组合在一起,然后在堆栈上分配它们的连续内存位置?
  2. 这是否也会发生在其他存储段中的所有存储类和变量/常量中?
  3. 为什么要这样做?这是某种优化吗?如果是,它如何“优化”东西?

TIA!

解决方法

您可以看到反汇编代码。那可以帮助您解决问题。

首先,函数分配给空白空间并分配值大小 如果创建3个值(x,y,z)。所以,像这样反汇编。

// create new 3 values
int x = 0
int y = 1
int z = 2

// To assembly
PUSH EBP
MOV ESP,EBP
SUB ESP,0x0C
MOV [EBP-4],0
MOV [EBP-8],1
MOV [EBP-C],2

它向您展示,为什么内存分配连续的空间。

然后优化每个编译器都不同。就像有时它删除const值,并且制定了不同的调用约定。

,

在分配(自动)变量到堆栈时,编译器是否按大小分组并分配它们的连续位置?

阅读C11标准n1570

允许编译器将automatic variablescall stack上的位置重新排序,或者重新排列optimize使其仅保留在processor registers中。

足够聪明的编译器可能会在编译时计算w = &i;(但实际上要注意ASLR)。其他编译器可能会做一些inline expansion。阅读Dragon bookPLDI会议或ACM SIGPLAN接受的最新论文。

也尝试cross-compilingArduino的C代码。您可能会感到惊讶。

IIRC,GCC的某些较早版本进行了优化遍历(在足够简单的情况下)对自动变量进行了重新排序。但是请注意Rice's theorem

如果您使用最新的GCC(在2020年10月,这意味着GCC 10),请尝试使用example.c来编译gcc -O3 -fverbose-asm -S -Wall -Wextra example.c代码,然后查看发出的example.s汇编代码

阅读this draft报告(由CHARIOTDECODER H2020项目资助),并研究MILEPOST GCCCTuning项目

还尝试在C代码上使用Frama-CClang tidy static analyzer。阅读documentation of GCC,特别是有关Invoking GCC的章节和有关Static Analyzer Options的章节。

您可以考虑研究source code的开源C编译器,例如GCCClangtinycc或{{3} }。您还可以查看nwcc项目的内部。

您可以考虑编写自己的CompCert来重组局部变量(预算超过一年的工作时间)。然后,请写一篇关于它的会议论文。您可以从这样的工作中获得博士学位。

如果可以,它究竟如何“优化”东西?

详细了解GCC plugin技术和abstract interpretation应用于Robinson arithmetic的技术。许多编译器都使用其中的一些,您的GCC插件也可能会使用它们(也许通过将其耦合到某些whole program optimization上,例如2021年的proof assistant)。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。