代码之家  ›  专栏  ›  技术社区  ›  Nick Van Brunt

C中的“register”关键字?

  •  244
  • Nick Van Brunt  · 技术社区  · 17 年前

    register C语言中的关键词do?我已经读到它用于优化,但在任何标准中都没有明确定义。它是否仍然相关?如果相关,您将在何时使用它?

    17 回复  |  直到 11 年前
        1
  •  366
  •   Qix - MONICA WAS MISTREATED    11 年前

    这是对编译器的一个提示,该变量将被大量使用,如果可能,建议将其保存在处理器寄存器中。

    大多数现代编译器都是自动完成的,并且比我们人类更善于选择它们。

        2
  •  74
  •   twk Mark Adler    15 年前

    我感到惊讶的是,并没有人提到不能获取寄存器变量的地址,即使编译器决定将变量保留在内存中而不是寄存器中。

    register 您将一无所获(无论如何,编译器将自行决定将变量放在何处),并将丢失 & 操作员-没有理由使用它。

        3
  •  36
  •   Andrew Barnett    17 年前

    它告诉编译器尝试使用CPU寄存器而不是RAM来存储变量。寄存器位于CPU中,访问速度比RAM快得多。但这只是对编译器的一个建议,可能无法贯彻到底。

        4
  •  23
  •   bwDraco    11 年前

    我知道这个问题是关于C,但是C++的同样问题被关闭成这个问题的精确副本。因此,这个答案可能不适用于C。


    C++11标准的最新草案, N3485 ,在7.1.1/3中这样说:

    A. register 说明符是对实现的一个提示,即这样声明的变量将被大量使用。[ 提示可以忽略,并且在大多数实现中,如果获取变量的地址,它将被忽略。此用途已被弃用。。。 尾注 ]

    在C)中,标准没有声明不能获取声明的变量的地址 登记 关键字以允许获取地址。

        5
  •  17
  •   Paul Tomblin    17 年前

    这至少有15年没有关系了,因为优化器对此做出了比您更好的决策。即使在相关的情况下,它在具有大量寄存器的CPU体系结构(如SPARC或M68000)上的意义也要比在具有少量寄存器的Intel上的意义大得多,而这些寄存器中的大部分是编译器为其自身目的保留的。

        6
  •  14
  •   Rupert    16 年前

    现代编译器可以在各种情况下利用这一点,并且在复杂代码中对编译器有很大帮助——在简单代码中,编译器可以自己解决这一问题。

    否则,它没有任何用途,也不用于寄存器分配。只要编译器足够现代化,指定它通常不会导致性能下降。

        7
  •  14
  •   Keith Thompson    9 年前

    我已经读到它用于优化,但在任何标准中都没有明确定义。

    事实上 由C标准明确定义。引用 N1570 draft

    具有存储类的对象的标识符声明 register 建议访问对象的速度应尽可能快 尽可能的。这些建议的有效程度是: 实现定义。

    一元 & 运算符不能应用于使用定义的对象 登记 不能在外部声明中使用。

    还有一些其他(相当模糊的)规则是特定于 -限定对象:

    • 登记 具有未定义的行为。
      使用定义数组对象是合法的 登记 ,但您无法对此类对象执行任何有用的操作(为数组编制索引需要获取其初始元素的地址)。
    • 这个 _Alignas
    • va_start 宏是 登记 -如果已限定,则行为未定义。

    顾名思义 起初的 登记 要求将对象存储在CPU寄存器中。但随着优化编译器方面的改进,这一点变得不再那么有用了。现代版本的C标准不涉及CPU寄存器,因为它们不再(需要)假设存在这样的事情(有些体系结构不使用寄存器)。普遍的看法是 对对象的声明更可能 恶化 生成的代码,因为它会干扰编译器自己的寄存器分配。在某些情况下,它可能仍然有用(比如,如果您确实知道访问变量的频率,并且您的知识比现代优化编译器所能理解的要好)。

    主要有形影响 登记

        8
  •  12
  •   Orion    9 年前

    故事时间!

    C、 作为一种语言,它是计算机的抽象。它允许你做一些事情,就像计算机做的那样,那就是操纵内存,做数学,打印东西等等。

    是汇编语言。汇编语言是CPU读取的语言,如果您使用汇编语言,您可以根据CPU进行操作。CPU做什么?基本上,它从内存中读取数据,进行数学运算,然后写入内存。CPU不仅仅对内存中的数字进行数学运算。首先,您必须在CPU内部将一个数字从一个内存移动到另一个内存,称为 登记 . 一旦您完成了对该号码所需的操作,就可以将其移回正常的系统内存。为什么要使用系统内存呢?登记册数量有限。在现代处理器中,您只能获得大约100个字节,而较旧的流行处理器的限制更为惊人(6502有3个8位寄存器供您免费使用)。因此,您的平均数学运算如下所示:

    load first number from memory
    load second number from memory
    add the two
    store answer into memory
    

    当你声明一个变量时 register

        9
  •  5
  •   dirkgently    17 年前

    您正在使用编译器复杂的图形着色算法。这用于寄存器分配。嗯,主要是。它作为编译器的提示——没错。但是不能完全忽略它,因为不允许您获取寄存器变量的地址(请记住,现在由您决定的编译器将尝试采取不同的操作)。这在某种程度上是告诉你不要使用它。

    但是,正如我所说,不推荐并不意味着你不能使用它。

        10
  •  5
  •   Rob    9 年前

    只是一个小演示(没有任何现实世界的目的)进行比较:当删除 register 在每个变量之前,这段代码在我的i7(GCC)上需要3.41秒, 具有 登记

    #include <stdio.h>
    
    int main(int argc, char** argv) {
    
         register int numIterations = 20000;    
    
         register int i=0;
         unsigned long val=0;
    
        for (i; i<numIterations+1; i++)
        {
            register int j=0;
            for (j;j<i;j++) 
            {
                val=j+i;
            }
        }
        printf("%d", val);
        return 0;
    }
    
        11
  •  4
  •   Daniel B    8 年前

    #include <stdlib.h>
    #include <stdio.h>
    #include <inttypes.h>
    #include <sys/neutrino.h>
    #include <sys/syspage.h>
    
    int main(int argc, char *argv[]) {
        uint64_t cps, cycle1, cycle2, ncycles;
        double sec;
        register int a=0, b = 1, c = 3, i;
    
        cycle1 = ClockCycles();
    
        for(i = 0; i < 100000000; i++)
            a = ((a + b + c) * c) / 2;
    
        cycle2 = ClockCycles();
        ncycles = cycle2 - cycle1;
        printf("%lld cycles elapsed\n", ncycles);
    
        cps = SYSPAGE_ENTRY(qtime) -> cycles_per_sec;
        printf("This system has %lld cycles per second\n", cps);
        sec = (double)ncycles/cps;
        printf("The cycles in seconds is %f\n", sec);
    
        return EXIT_SUCCESS;
    }
    

    我得到了以下结果:

    -&燃气轮机;以秒为单位的循环数约为0.244600

    现在没有寄存器int:

    int a=0, b = 1, c = 3, i;
    

    我得到:

    -&燃气轮机;经过1421694077个周期

    -&燃气轮机;该系统每秒有330083000个循环

    -&燃气轮机;以秒为单位的循环数约为0.430700

        12
  •  3
  •   user5536632 user5536632    10 年前

    如今,优化器在确定更可能保存在寄存器中的变量方面比程序员效率更高,而且优化器并不总是考虑程序员的提示。

    许多人错误地建议不要使用register关键字。

    让我们看看为什么!

    建议其他人不要使用寄存器的人错误地将此视为另一个论点。

    然而,知道不能获取寄存器变量的地址这一简单事实,使编译器(及其优化器)知道不能通过指针间接修改该变量的值。

    进行您自己的测试,您将在最内部的循环中获得显著的性能改进。

    c_register_side_effect_performance_boost

        13
  •  2
  •   billjamesdev    17 年前

    寄存器将通知编译器,编码人员认为该变量将被写入/读取到足以证明其存储在可供变量使用的少数寄存器之一中的合理性。从寄存器读取/写入通常更快,并且可能需要更小的操作码集。

    现在,这不是很有用,因为大多数编译器的优化器比您更擅长于确定是否应该为该变量使用寄存器以及使用多长时间。

        14
  •  2
  •   Lewis Kelsey    6 年前

    gcc 9.3 asm输出,不使用优化标志(本答案中的所有内容均指不使用优化标志的标准编译):

    #include <stdio.h>
    int main(void) {
      int i = 3;
      i++;
      printf("%d", i);
      return 0;
    }
    
    .LC0:
            .string "%d"
    main:
            push    rbp
            mov     rbp, rsp
            sub     rsp, 16
            mov     DWORD PTR [rbp-4], 3
            add     DWORD PTR [rbp-4], 1
            mov     eax, DWORD PTR [rbp-4]
            mov     esi, eax
            mov     edi, OFFSET FLAT:.LC0
            mov     eax, 0
            call    printf
            mov     eax, 0
            leave 
            ret
    
    #include <stdio.h>
    int main(void) {
      register int i = 3;
      i++;
      printf("%d", i);
      return 0;
    }
    
    .LC0:
            .string "%d"
    main:
            push    rbp
            mov     rbp, rsp
            push    rbx
            sub     rsp, 8
            mov     ebx, 3
            add     ebx, 1
            mov     esi, ebx
            mov     edi, OFFSET FLAT:.LC0
            mov     eax, 0
            call    printf
            add     rsp, 8
            pop     rbx
            pop     rbp
            ret
    

    ebx 用于计算,这意味着需要将其推送到堆栈并在函数结束时还原,因为它是被调用方保存的。 register 产生更多的代码行和1个内存写入和1个内存读取(尽管实际上,如果在中进行计算,这可以优化为0 R/Ws) esi ,这就是使用C++时发生的情况 const register ).不使用 登记 登记 没有此要求,无法指向。 const 基本上与 volatile 和使用 将覆盖文件和块范围内的常量优化以及 登记 将产生相同的输出,因为const在块范围内对C不做任何操作,所以只有 登记 优化适用。

    当啷一声, 登记 被忽略,但

        15
  •  1
  •   TWA    17 年前

        16
  •  1
  •   sourcenouveau    15 年前

    微软的Visual C++编译器忽略了 register 启用全局寄存器分配优化(/Oe编译器标志)时的关键字。

    register Keyword 在MSDN上。

        17
  •  1
  •   Sanjeev Kumar    13 年前

    Register关键字告诉编译器将特定变量存储在CPU寄存器中,以便可以快速访问该变量。从程序员的角度来看,register关键字用于程序中大量使用的变量,因此编译器可以加速代码。尽管这取决于编译器是否将变量保留在CPU寄存器或主内存中。

        18
  •  0
  •   flying-high    13 年前

    寄存器指示编译器通过将特定变量存储在寄存器中然后存储在内存中来优化此代码。这是对编译器的要求,编译器可能会或可能不会考虑这个请求。 例如:一个循环。

    还有一件事是,如果将变量声明为寄存器,则无法获取其地址,因为它未存储在内存中。它在CPU寄存器中获得分配。