【问题标题】:Gather Addresses of all Static C Strings at Compile Time在编译时收集所有静态 C 字符串的地址
【发布时间】:2018-05-23 13:06:29
【问题描述】:

假设我有一个类似的函数

void foo(const char* bar, ...)

它已经在多个地方被调用了。

是否可以在main() 中收集编译时已知的所有静态字符串的地址?

比如foo("abc"),我想在main()中能得到"abc"的地址。如果有人打电话给foo(someVariable)someVariable的地址可能不知道,所以可以忽略。

有可能吗?

【问题讨论】:

  • 请注意,"abc" 可以与(另一个)"abc" 有不同的地址。
  • 我可以接受不同的地址,即使内容完全相同。感谢您指出:)
  • 您可以创建operator ""_some_suffix 手动注册静态c-string。
  • C++ 中没有收集所有字符串的工具;这是一个实施问题。您的编译器可能有一些 API 用于此或命令行开关或pragma
  • 许多项目使用字符串数据库。这在必须以不同语言呈现文本时非常有用。

标签: c++ linux g++ c++17


【解决方案1】:

如果您同意使用注册,您可以执行类似的操作

// Would contain each registered string.
std::vector<const char*>& registered_vector()
{
    static std::vector<const char*> v;
    return v;
}

bool Register(const char* s)
{
    registered_vector().push_back(s);
    return true;
}

// Class which holds the unique pointer as buffer.
template <typename Char, Char... Cs>
struct static_string
{
    static constexpr Char s[] = {Cs..., 0};
};

template <typename Char, Char... Cs>
constexpr Char static_string<Char, Cs...>::s[];

// string literal operator templates are a GNU extension
// MACRO can replace the operator to avoid that extension.
template <typename Char, Char... Cs>
static_string<Char, Cs...> operator ""_ss()
{
    static_string<Char, Cs...> res;
    static const bool dummy = Register(res.s); // Register only once :-)
    static_cast<void>(dummy); // Avoid warning for unused variable
    return res;
}

现在,测试一下:

int main() {
    "Hello"_ss;
    "World"_ss;
    "Hello"_ss;
    "Hi"_ss;
    "42"_ss;

    for (const auto s : registered_vector()) {
        std::cout << s << std::endl;
    }
}

Demo

【讨论】:

  • 我实际上想出了一些类似但丑陋的东西(使用宏创建一个带有局部静态变量的作用域来跟踪它是否被调用过一次并且没有文字运算符模板)。您的解决方案非常优雅。利用局部静态变量只会被分配一次,因此不会再次调用 Register(),这是一个非常聪明的技巧!
  • 仅供参考,似乎 C++17 包含文字运算符模板,因为我认为 GCC 在使用时会打印警告。现在使用 -std=c++17,它不会发出这样的警告。
【解决方案2】:

是否可以在main() 中收集编译时已知的所有静态字符串的地址?

在编译时来自其他翻译单元的字符串不可用。

您可以使用readelf -W -p .rodata &lt;executable&gt; 命令从可执行文件或共享库中转储字符串文字。

【讨论】:

  • 感谢您的回答。从技术上讲,您的答案确实是正确的答案——在编译时无法这样做(所以我意识到我问了一个愚蠢的问题)。如果您不介意,我想稍微扩展一下这个问题——当调用 foo() 时,是否可以以非常快速的方式将 bar 映射到 .rodata 中其条目的相应地址(例如小于10 纳秒)?我知道我可以将 .rodata 解析为地图(例如 std::unordered_map、std::map),然后在调用 foo() 时查找地图,但它似乎很慢。
  • 实际上用 readelf more 玩了更多。似乎每个静态 C 字符串的运行时(虚拟)内存地址是 .rodata 部分的起始内存地址 + readelf -W -p .rodata 输出中所述的偏移量。我说的对吗?
  • 我还注意到一些函数名称也是 .rodata 的一部分。无论如何要过滤掉它们?提前致谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-12-21
  • 1970-01-01
  • 2015-02-13
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多