当前位置: 首页 > 知识库问答 >
问题:

cpp中的全局常量对象,无运行时初始化

吕高昂
2023-03-14

如何在cpp中声明全局常量对象,以便将其关联的数据完全存储在cpp中。rodata,而不是在运行时初始化期间创建的、不必要复制的对象?

例如,如果我制作类型为的globals

  • 常量d::字符串
  • constd::array

测试表明这些将编译成。bss,因此需要运行时初始化,尽管它是编译时已知的常量数据。。。它需要知道初始化它们的目的,因此它也不必要地复制数据,使用额外的内存。

如何获得驻留在. rodata中的实际const对象,而无需任何运行时初始化?

由于C标准可能对此不够具体,如果您需要一些特定于编译器的特性,请使用g和/或clang支持的一些特性。

注意:如果您的答案是某个boost或某个特定的库,请解释库是如何实现这一点的。我想了解这是如何实现的。

下面的注释可以忽略,但可以包括在内,因为我从人们那里得到的最初反应是“不可能,常量字符串或常量数组不需要运行时初始化或重复数据”。

这里有一个例子和一些测试:

测验cpp

#include <iostream>
#include <fstream>
#include <string>
#include <array>
#include <map>

std::string str {"here"};
const std::string cstr {"there"};
std::array<std::string, 3> arr {"eight", "six", "seven"};
const std::array<const std::string, 4> carrc {"five", "nine", "oh", "three"};

const std::map<std::string, const std::string> cmapc = {
    {"a", "apple"},
    {"b", "bananna"},
    {"c", "carrot"},
};

void show_info(const char *name, const void *a, const void *b)
{
    std::cout << name << "\t" << a << " " << b << std::endl;
}

int main(int argc, char **argv) {
#define INFO(x) show_info(#x, &x, x.data())
    INFO(str);
    INFO(cstr);

    INFO(arr);
    INFO(arr[1]);
    INFO(carrc);
    INFO(carrc[1]);

    std::cout << "cmapc" << "\t" << (void *)&cmapc << std::endl;
    INFO(cmapc.at("a"));


    std::ifstream infile("/proc/self/maps");
    std::string line;
    while(std::getline(infile, line)) {
        std::cout << line << std::endl;
    }

    return 0;
}

编译并检查对象的放置位置

$ g++ -std=c++17 -o test test.cpp
$ readelf -W -S test | grep -E "(.rodata|.data|.bss)"
  [16] .rodata           PROGBITS        0000000000005ad0 005ad0 0000e9 00   A  0   0  8
  [24] .data             PROGBITS        0000000000209000 009000 000018 00  WA  0   0  8
  [25] .bss              NOBITS          0000000000209020 009018 000290 00  WA  0   0 32
$ readelf -s test | grep OBJ | grep -E "[^_](str|arr|map)"
    37: 00000000002091e0    32 OBJECT  LOCAL  DEFAULT   25 _ZL4cstr
    38: 0000000000209200   128 OBJECT  LOCAL  DEFAULT   25 _ZL5carrc
    39: 0000000000209280    48 OBJECT  LOCAL  DEFAULT   25 _ZL5cmapc
    88: 0000000000209160    96 OBJECT  GLOBAL DEFAULT   25 _Z3arrB5cxx11
   105: 0000000000209140    32 OBJECT  GLOBAL DEFAULT   25 _Z3strB5cxx11

您也可以运行程序并查看输出。

或者查看gdb中的运行时初始化

$ gdb -q ./test
Reading symbols from ./test...(no debugging symbols found)...done.
(gdb) b _start
Breakpoint 1 at 0x2180
(gdb) r
Starting program: /tmp/test

Breakpoint 1, 0x0000000008002180 in _start ()
(gdb) x/4gx &str
0x8209140 <_Z3strB5cxx11>:      0x0000000000000000      0x0000000000000000
0x8209150 <_Z3strB5cxx11+16>:   0x0000000000000000      0x0000000000000000
(gdb) b main
Breakpoint 2 at 0x800230f
(gdb) c
Continuing.

Breakpoint 2, 0x000000000800230f in main ()
(gdb) x/4gx &str
0x8209140 <_Z3strB5cxx11>:      0x0000000008209150      0x0000000000000004
0x8209150 <_Z3strB5cxx11+16>:   0x0000000065726568      0x0000000000000000

共有1个答案

司马建柏
2023-03-14

通过定义支持对象的构造函数,可以在编译时定义对象。请注意,对象的成员也应该是const

在下面的示例中,我们在编译时创建一个对象foo。我们可以使用static\u assert函数验证这一点。另一方面,foo2是在运行时创建的。不幸的是,对于你的问题没有灵丹妙药。使用constexpr无法确保始终以编译时初始化结束,因为这只会在可能的情况下发生,如果不发生,编译器不会抱怨。

struct Foo {
  const int elem1;
  const char elem2;
  constexpr Foo(int a, char b);
};

constexpr Foo::Foo(int a, char b) :
  elem1(a), elem2(b)
  {
  }

constexpr Foo foo(1, 'a');

static_assert(foo.elem1 == 1);
static_assert(foo.elem2 == 'a');

int main()
{
  Foo foo2(2, 'b');
}
 类似资料:
  • 问题内容: 有什么办法可以使以下内容在JavaScript中起作用? 在当前形式下,此代码显然会引发引用错误,因为未引用。但是, 是否 有任何方法可以使对象文字的属性值取决于先前声明的其他属性? 问题答案: 好吧,我唯一能告诉你的就是getter:: 这是ECMAScript第5版规范引入的语法扩展,大多数现代浏览器(包括IE9)都支持该语法。

  • 当我在Inno Setup脚本的< code>[Code]部分(Pascal脚本)声明一个全局变量时,它是否自动初始化为零/空值?还是必须显式初始化它(例如在< code>InitializeSetup事件函数中)? 根据我的经验,变量是零初始化的。不过,我不确定我是否只是运气不好。 我已经完成了10.000次测试代码迭代。全局变量(或各种类型、整数、字符串、指针)始终为零初始化。

  • 我正在使用tomee服务器运行我的javaEE应用程序。我写了一个过滤器,它注入一个对象。但是,该对象似乎没有被实例化: 以下是我的代码: Filter.java Faculty.java 我得到了一个NPE。以下是堆栈跟踪: 下面是正在使用的工件的版本: 我已经尝试在META-INF和WEB-INF中包含beans.xml,但我仍然看到NPE.Can有人让我知道我做错了什么?

  • 问题内容: 我在用Java工作。 我通常会这样设置一些对象: 问题是:在此示例中是否等于,按原样我可以假定对未初始化的对象进行空检查将是准确的? 问题答案: 正确,未显式初始化的引用类型的静态成员和实例成员都由Java 设置为。相同的规则适用于数组成员。 根据Java语言规范的第4.12.5节: 变量的初始值 程序中的每个变量在使用值之前都必须具有一个值: 每个类变量,实例变量或数组组件在创建时均

  • 问题内容: 我正在使用0.3.1-pre Node.js 这样做: 结果是 但是,在键入“全局”后在控制台中按[Tab]时。给出函数列表,包括parseInt。 那么parseInt是否是 全局 名称空间的成员? 问题答案: 显然,该对象不是浏览器中的全局对象。(根据#nodejs @ freenode中的micheil)实际上仅在内部使用。关于全球关闭的信息。 并且和所有的好友都在自己的全局。不

  • 场景2:我们希望横向扩展规则执行服务器。假设我们有一个公开REST-API的规则引擎服务器(kie-server)。我们是否可以在loadbalancer后面运行多个实例以使其水平伸缩?还有其他方法可以实现可伸缩性吗?