【问题标题】:How to create a single global char* in C++如何在 C++ 中创建单个全局 char*
【发布时间】:2016-09-12 12:55:00
【问题描述】:

我正在维护一个自定义日志系统,该系统使用宏来执行诸如将时间戳和源文件名附加到每条消息的操作。所以像:

AI_LOG("Hello %s", "World!");

可能会导致:

(16.38) HelloWorld.cpp LOG: Hello World!

目前,它在堆栈上创建一个 char* 缓冲区,将输出的初始部分放在缓冲区的开头,然后使用 snprintf 将输出复制到缓冲区的其余部分。这一切都有效......除了它在堆栈上创建缓冲区,如果我有足够深的堆栈和足够的日志语句,并且我没有保持缓冲区非常小(例如 256 个字符),我可以得到堆栈溢出。我现在需要输出更长的字符串,因此将所有这些缓冲区放在堆栈上不再适合我了。

考虑到这一背景...我想移动到一个全局 char 数组,我将把它做得很大(开始可能是 4K 字符)。但是,我的理解是,如果我只是说这样的话:

#define AI_OUTPUT_BUFFER_SIZE 4092
char AI_OUTPUT_BUFFER[AI_OUTPUT_BUFFER_SIZE];

在我的输出系统的 .h 文件中,我冒着被包含该 .h 文件的每个文件创建一个单独的缓冲区的风险。这是一个真正的问题吗?如果是这样,是否有一种好的(编译器不可知、符合 C++98、不使用 Boost)方法来获得我想要的单个缓冲区?

我倾向于在标题中这样做:

#define AI_OUTPUT_BUFFER_SIZE 4096
class AIOutputBuffer
{
public:
    static char buffer[AI_OUTPUT_BUFFER_SIZE];
};

然后在 .cpp 中我可以:

char GAIA::AIOutputBuffer::buffer[AI_OUTPUT_BUFFER_SIZE];

但这让我对链接器感到头疼……也许我还没有做对吗?

在人们提出建议之前...是的,我可能会重写它以使用字符串或流...但我真的不想重写整个系统,我也没有时间和资源来这样做.这个系统运行良好,给了我很大的灵活性——我只需要处理内存使用问题。

【问题讨论】:

  • 你为什么不使用某种没有缓冲区的调用 cout 或类似的宏?
  • 为什么要使用宏?如果编译器不会内联您的日志记录函数,那么将代码内联是一个坏主意。
  • @user4581301 如果您使用函数,您将无法访问行或文件信息。
  • 在标题中使用extern 声明并在您的源文件之一中定义它。
  • @WilliamKappler: "如果您使用函数,您将无法访问行或文件信息。" 您可以创建一个函数,将行和文件宏作为论据。

标签: c++ c arrays global-variables c++98


【解决方案1】:

来自 cmets(感谢 Dmitri!)我使用了 extern,它就像一个魅力。所以在标题中:

#define AI_OUTPUT_BUFFER_SIZE 256
extern char AI_OUTPUT_BUFFER[AI_OUTPUT_BUFFER_SIZE];

然后在.cpp中:

char AI_OUTPUT_BUFFER[AI_OUTPUT_BUFFER_SIZE];

【讨论】:

  • 警告:如果您在客户端代码中运行多个线程,则很容易让两条消息同时到达缓冲区并相互破坏。
  • 是的,到目前为止我是单线程的。如果情况发生变化……这将是众多担忧之一。 :)
【解决方案2】:

虽然您解决了问题,但还有一个更好的解决方案,它依赖于std::string 进行内存管理。此外,它在多线程环境中运行良好。

这个想法是使用老派的snprintf,但让std::string负责内存管理:

#include <string>
#include <cstdarg>

std::string va(const char* fmt, ...)
{
    // make sure your stack can handle a one-time allocation of a buffer of this size
    const auto BUFFER_SIZE = 8192;   

    // ...unless you know you're not going to call this from multiple threads; 
    // then you can make this buffer static and the size can be increased
    char buffer[BUFFER_SIZE];

    va_list args;
    va_start(args, fmt);
    std::vsnprintf(buffer, sizeof(buffer), fmt, args);
    va_end(args);
    buffer[sizeof(buffer) - 1] = '\0';

    return buffer;
}

用法:这里没什么异常:auto logMsg = va("Hello %s", "World!");

【讨论】:

  • @deviantfan 嗯,函数的返回值是(类型的)std::string
  • 呃……抱歉,我没看到:D
  • OP 显然试图避免堆栈分配(请参阅问题)-大概他在内存受限的嵌入式系统或类似系统上-在堆栈上分配 8 kb 临时缓冲区可能不是去吧,考虑到:“[如果]我没有把缓冲区保持得很小(例如 256 个字符),我可能会导致堆栈溢出”.
  • @PaulR OP 明确提到问题源于多次调用此函数并分配了大量缓冲区(如果我有足够深的堆栈和足够的日志语句);这里我们只需要分配一次,当函数返回时它就被销毁了。 8192 只是一个示例 - OP 可以将其调整为他们需要的字符串大小。最后,正如我在评论中提到的,如果 OP 绝对是肯定的,他们将永远不会使用多线程,buffer 可以设为static,这消除了任何堆栈大小限制。
  • 好点,虽然这引出了问题...这里是实际的宏:#define AI_PRINT(outputType, severity, pBrain, msg, ...) \ do { \ char buffer[AI_OUTPUT_BUFFER_SIZE]; \ AI_PRINT_BUFFER(缓冲区,AI_OUTPUT_BUFFER_SIZE,输出类型,严重性,pBrain,味精,##__VA_ARGS__); \ } while(LINE == -1) \ 像这样包裹在do循环中,我想我会得到相同的结果(即缓冲区只会在堆栈上创建一次)。我想知道编译器是否正在优化它,或者我是否已经修复了这个并且没有意识到......
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-09-23
  • 2022-01-10
相关资源
最近更新 更多