C、C++ 和 Java 中的提升/重新排序:变量声明必须始终位于上下文的顶部吗?

2024-02-20

我读了一些关于提升和重新排序 https://stackoverflow.com/questions/11430095/are-hoisting-and-reordering-the-same-thing,所以看来Java VM可能会选择提升一些表达式。我还阅读了有关 Javascript 中函数声明提升的内容。

第一个问题:有人可以确认 C、C++ 和 Java 中是否通常存在提升吗?或者它们都依赖于编译器/优化吗?

我读过很多示例 C 代码,它们总是将变量声明放在顶部,在任何变量之前assert or 边界条件。我认为完成所有操作会更快一些asserts and 边界情况在变量声明之前,因为函数可能会终止。

主要问题:变量声明必须始终位于上下文的顶部吗? (这里有提升在起作用吗?)或者编译器是否通过检查这些独立的来自动优化代码asserts and 边界情况首先(在不相关的变量声明之前)?

这是一个相关的例子:

void MergeSort(struct node** headRef) {
    struct node* a;
    struct node* b;
    if ((*headRef == NULL) || ((*headRef)->next == NULL)) {
        return;
    }
    FrontBackSplit(*headRef, &a, &b);
    MergeSort(&a);
    MergeSort(&b);
    *headRef = SortedMerge(a, b);
}

如上所示,边界情况不依赖于变量“a”和“b”。因此,将边界情况放在变量声明之上会使速度稍微快一些吗?


Updates:

上面的例子并不像我希望的那么好,因为变量“a”和“b”只是被声明,没有在那里初始化。编译器会忽略声明,直到我们真正需要使用它们。

我检查了 GNU GCC 程序集的变量声明和初始化,这些程序集具有不同的执行顺序。编译器没有改变我的独立顺序asserts and 边界情况. 所以,重新排序这些asserts and 边界情况确实更改组件,从而改变机器运行它们的方式。

我认为差异很小,大多数人从不关心这一点。


编译器可以根据需要重新排序/修改您的代码,只要修改后的代码与按顺序执行的原始代码等效即可。所以吊装是允许的,但不是必需的。这是一种优化,并且完全是编译器特定的。

C++ 中的变量声明可以在您希望的任何位置。在 C 中,它们过去必须位于上下文的顶部,但是当引入 c99 标准时,规则被放宽,现在它们可以位于任何您想要的位置,类似于 C++。尽管如此,许多 C 程序员仍然坚持将它们放在上下文中的顶部。

在您的示例中,编译器可以自由地将 if 语句移至顶部,但我认为不会。这些变量只是在堆栈上声明且未初始化的指针,声明它们的成本是最小的,而且在函数开头而不是在断言之后创建它们可能更有效。

例如,如果您的声明会涉及任何副作用

struct node *a = some_function();

那么编译器可以重新排序的内容将受到限制。

Edit:

我用这个短程序在实践中检查了 GCC 的循环提升:

#include <stdio.h>
int main(int argc, char **argv) {
    int dummy = 2 * argc;
    int i = 1;
    while (i<=10 && dummy != 4)
        printf("%d\n", i++);
    return 0;
}

我用这个命令编译它:

gcc -std=c99 -pedantic test.c -S -o test.asm

这是输出:

    .file   "test.c"
    .def    ___main;    .scl    2;  .type   32; .endef
    .section .rdata,"dr"
LC0:
    .ascii "%d\12\0"
    .text
    .globl  _main
    .def    _main;  .scl    2;  .type   32; .endef
_main:
LFB7:
    .cfi_startproc
    pushl   %ebp
    .cfi_def_cfa_offset 8
    .cfi_offset 5, -8
    movl    %esp, %ebp
    .cfi_def_cfa_register 5
    andl    $-16, %esp
    subl    $32, %esp
    call    ___main
    movl    8(%ebp), %eax
    addl    %eax, %eax
    movl    %eax, 24(%esp)
    movl    $1, 28(%esp)
    jmp L2
L4:
    movl    28(%esp), %eax
    leal    1(%eax), %edx
    movl    %edx, 28(%esp)
    movl    %eax, 4(%esp)
    movl    $LC0, (%esp)
    call    _printf
L2:
    cmpl    $10, 28(%esp)
    jg  L3
    cmpl    $4, 24(%esp)
    jne L4
L3:
    movl    $0, %eax
    leave
    .cfi_restore 5
    .cfi_def_cfa 4, 4
    ret
    .cfi_endproc
LFE7:
    .ident  "GCC: (GNU) 4.8.2"
    .def    _printf;    .scl    2;  .type   32; .endef

然后我用这个命令编译它:

gcc -std=c99 -pedantic test.c -O3 -S -o test.asm

这是输出:

    .file   "test.c"
    .def    ___main;    .scl    2;  .type   32; .endef
    .section .rdata,"dr"
LC0:
    .ascii "%d\12\0"
    .section    .text.startup,"x"
    .p2align 4,,15
    .globl  _main
    .def    _main;  .scl    2;  .type   32; .endef
_main:
LFB7:
    .cfi_startproc
    pushl   %ebp
    .cfi_def_cfa_offset 8
    .cfi_offset 5, -8
    movl    %esp, %ebp
    .cfi_def_cfa_register 5
    pushl   %ebx
    andl    $-16, %esp
    subl    $16, %esp
    .cfi_offset 3, -12
    call    ___main
    movl    8(%ebp), %eax
    leal    (%eax,%eax), %edx
    movl    $1, %eax
    cmpl    $4, %edx
    jne L8
    jmp L6
    .p2align 4,,7
L12:
    movl    %ebx, %eax
L8:
    leal    1(%eax), %ebx
    movl    %eax, 4(%esp)
    movl    $LC0, (%esp)
    call    _printf
    cmpl    $11, %ebx
    jne L12
L6:
    xorl    %eax, %eax
    movl    -4(%ebp), %ebx
    leave
    .cfi_restore 5
    .cfi_restore 3
    .cfi_def_cfa 4, 4
    ret
    .cfi_endproc
LFE7:
    .ident  "GCC: (GNU) 4.8.2"
    .def    _printf;    .scl    2;  .type   32; .endef

基本上,打开优化后,原始代码将转换为如下所示:

#include <stdio.h>
int main(int argc, char **argv) {
    int dummy = 2 * argc;
    int i = 1;
    if (dummy != 4)
        while (i<=10)
            printf("%d\n", i++);
    return 0;
}

所以,正如你所看到的,C 中确实存在提升。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C、C++ 和 Java 中的提升/重新排序:变量声明必须始终位于上下文的顶部吗? 的相关文章

随机推荐

  • 如何在 svelte 中访问 websocket?

    我正在尝试将 svelte 连接到 fastapi 后端 但使用 svelte 套件时出现此错误 我明白了 WebSocket is not defined ReferenceError WebSocket is not defined e
  • cherrypy.HTTPRedirect 使用绝对路径重定向到 IP 而不是主机名

    我在 nginx 后面运行 CherryPy 并需要处理重定向 在我的在 127 0 0 1 8080 上运行的开发机器上 这会正确重定向到 127 0 0 1 8080 login 然而 当通过 nginx 在cherrypy mydom
  • 将数据块读入matlab数组

    我有一个如下所示的数据文件 3 1 0 1 4 1 7 2 1 2 1 5 1 1 1 2 1 1 1 2 对于每一行 第一个整数表示该行中浮点数的数量 现在我想将所有数据加载到一个matlab数组中 并忽略第一列 也就是说 我想得到一个像
  • 原则:多个(whereIn OR whereIn)查询?

    我在用 Doctrine 编写一个相当简单的查询时遇到了麻烦 我有两个数组 countries cities 我需要检查数据库记录值是否与其中的任何一个匹配 我正在寻找类似的东西 gt whereIn country city countr
  • 当文件在其他进程中使用时删除该文件

    In DocumentViewerControl在 WPF 中 我想在关闭事件处理程序中关闭控件后删除该文件 我试图删除它 但 VS 告诉我它被另一个进程使用 我怎样才能删除它 TextBlock tb TextBlock e TabIte
  • Akka设计原则

    在开发一个相当大的 Akka 应用程序时 我在使用普通方法和非 Akka 类时遇到了一个非常简单的结构 但在使用 Akka 时实际上很难确定 这就是为什么我来这里问你什么建议是解决此问题的最佳方法 所以问题是这样的 我有一个父角色 我们称他
  • 在没有 Adob​​e Reader 的情况下将 pdf 文件显示到浏览器中

    我需要在没有 Adob e Reader 的情况下在浏览器中打开 pdf 文件 是否有 PHP 的 API 可以在浏览器中打开 PDF 文件 Regards No PHP 在服务器上运行 它可以访问的唯一 浏览器 API 是 HTTP 如果
  • 如何在非 root 运行的容器中安装新包?

    我需要在正在运行的容器中安装简单的包 例如curl 但我遇到了这个错误 apk add curl ERROR Unable to lock database Permission denied ERROR Failed to open ap
  • 将 System.out 和 System.err 重定向到 slf4j

    我需要重定向System out err println输出到 slf4j 我知道这不是正确进行日志记录的方法但有一个外部库 它记录到System out 您可以使用slf4j 上的系统输出 http projects lidalia or
  • 否定打字稿类型?

    我想在打字稿中创建一个简单的 NOT 运算符 您可以将所有基元组合到某种类型 A 的联合中 这些基元不是第二种类型 B 的联合的基元成员 这可以使用条件类型来完成 例如 如果您有类型 type A a b c type B c d e 然后
  • 如何从 ByteBuffer 获取短[]

    我在 Android 项目中使用 JNI 代码 其中 JNI 本机函数需要一个短 参数 但是 原始数据存储为 ByteBuffer 我正在尝试按如下方式转换数据格式 ByteBuffer rgbBuf ByteBuffer allocate
  • 同一库的多个版本

    我有项目 A 它曾经有模块 A1 使用 dagger v 1 2 2 现在我想添加到项目 A 模块 A2 它依赖于 dagger v 2 0 但我不能 因为这两个匕首库是冲突的 我可以在不同的 android 模块中以某种方式处理多个版本的
  • 使用 Ruby 正则表达式使用常规双引号转义 '“'

    我的文本包含这些奇特的双引号 我想使用 Ruby gsub 和正则表达式将它们替换为常规双引号 这是一个例子以及我到目前为止所拥有的 sentence This is a quote Hey guys I couldn t figure o
  • (Android Xamarin) 获取资源字符串值而不是 int

    我刚刚开始使用 VS2012 使用 Xamarin 创建一个简单的 Android 应用程序 我知道有一种类型的资源仅用于字符串 在我的资源文件夹中 我有一个如下所示的 xml 文件
  • 当启用面板向左滚动时,如何显示我的页面在左侧部分可见?

    IN this link http view jquerymobile com 1 3 2 dist demos widgets panels 我可以发现有一个覆盖选项 我可以在其中隐藏或显示右侧或左侧的面板 但我想在面板中显示面板 90
  • 在没有 .kv 文件的情况下使用屏幕管理器时出现一些问题

    我正在尝试使用 Pthon 和 kivy 制作应用程序 我正在研究其中的登录 注册部分 我试图为每个屏幕 登录和注册 创建一个屏幕 并将它们与 ScreenManager 连接 但没有 kv 文件 如果不可能 我想在 hte kv 文件中写
  • 如何在 emberjs 上使用单向绑定?

    我开始使用 ember 但我无法理解的一件事是如何使用单向绑定 请考虑以下代码 HTML JS var App Ember Application create Ap
  • 如何在 ASP.NET MVC 中获取其他用户的个人资料?

    我想在用户登录时设置带有时区的 cookie AccountController LogOn 似乎是执行此操作的最佳位置 但是 我还无法在那里读取用户的个人资料 因为我猜您只有在该方法完成时才能访问个人资料 因此 此代码返回一个空字符串 D
  • 如何使用pandas/python处理excel文件头

    我正在尝试阅读https www whatdotheyknow com request 193811 response 480664 attach 3 GCSE 20IGCSE 20results 20v3 xlsx https www w
  • C、C++ 和 Java 中的提升/重新排序:变量声明必须始终位于上下文的顶部吗?

    我读了一些关于提升和重新排序 https stackoverflow com questions 11430095 are hoisting and reordering the same thing 所以看来Java VM可能会选择提升一