为什么示例 A 编译错误而示例 B 错误 "initializer element is not constant"

Why does Example A Compile and Example B Errors with "initializer element is not constant"

编辑: 我做了一些假设,例如静态变量将在运行时第一次调用函数时创建和初始化一次。

我现在知道情况并非如此,现在只定义一个静态变量和 return 它的指针,并有一个在程序开始时调用一次的 init 函数,所以基本上和我一样当我使用全局变量但将其置于全局范围之外时正在这样做。

原文:

我正在努力避免通常建议的全局变量,但需要一些我可以在多个函数中引用的静态数据,而不必不断地将对象传递给每个函数(尽管如果这最终不会工作我可能不得不诉诸于此)

另一个潜在的优势是,因为我使用它的大多数函数都会被经常调用,所以只为永远不会改变的数据初始化一次应该可以最大限度地减少性能损失。

我想出的方法是创建一个静态变量并初始化一次,如果我需要在单个函数中使用它我会直接在函数中初始化然后使用它,如果我需要它在多个函数中,我将在一个单独的静态函数中初始化它,其他函数可以调用指针

编辑: WINDOWS 是自定义宏

#ifndef WINDOWS
  #if defined(_WIN32) || defined(_WIN64)
    #define WINDOWS
  #endif
#endif

另外编译器是 MinGW 64bit

示例 A 编译正常但示例 B 引发错误 "initializer element is not constant"

示例 A:

static ProgramPath getPath() {
  ProgramPath result;
    // Get Executable Path    
#ifdef WINDOWS
  GetModuleFileName(NULL, result.executablePath, sizeof(result.executablePath));
#endif

  // Seperate Path and Executable into Seperate Variables
  for (unsigned int i = 0; i < sizeof result.executablePath; ++i) {
    // Convert \ to /
    if (result.executablePath[i] == 0x5c) {
      result.executablePath[i] = 0x2F;
    } else if (result.executablePath[i] == 0x0) { // 0x0 means we've reached end of string.
      unsigned int j;

      // Get Executable Name Sub String
      for (j = i - 1; j > 0; --j) {
        if (result.executablePath[j] == 0x2F) { // Stop when we reach a /
          result.executableStringLen = i - j;
          memcpy(&result.executable, &result.executablePath[j + 1], result.executableStringLen); // Copy Executable SubString into correct variable
          break;
        }
      }

      result.executableStringLen = ++j;

      // Remove Executable from Path
      for (; j < i; ++j) {
        result.executablePath[j] = 0x0;
      }
      break;
    }
  }

  CA_VERBOSE_DEBUG("Executable: %s", executable);
  CA_PRINT_DEBUG("Executable Path: %s", executablePath);

  return result;
}

static inline ProgramPath *getPathInfo() {
  static ProgramPath programPath = getPath();
  return &programPath;
}

示例 B:

static GraphicsInfo getGraphicsInfo() {
  GraphicsInfo result;

  result.internalPixelFormat = GL_BGRA;
  result.internalPixelType = GL_UNSIGNED_INT_8_8_8_8_REV;

  // Get Preferred Internal Pixel Format
  if (glGetInternalformativ) {
    const GLenum preferedInternalFormats[4] = {
      GL_RGB,
      GL_BGR,
      GL_RGBA,
      GL_BGRA
    };

    GLint test;
    // Check for Internally Supported Formats and Use Best One
    for (int i = 3; i >= 0; --i) {
      // Check for Prefered
      glGetInternalformativ(GL_RENDERBUFFER, preferedInternalFormats[i], GL_INTERNALFORMAT_PREFERRED, 1, &test);

      if (test == GL_TRUE) {
        result.internalPixelFormat = preferedInternalFormats[i];
        break;
      }
    }
  }

  return result;
}

static inline GraphicsInfo *CA_getGraphicsInfo() {
  static GraphicsInfo result = getGraphicsInfo(); // Error Occurs Here
  return &result;
}

我也试过注释掉示例 B 中的大部分代码,但无济于事

您不能从函数的 return 值初始化具有 static 存储的变量:

static inline GraphicsInfo *CA_getGraphicsInfo() {
    static GraphicsInfo result = getGraphicsInfo(); // Error Occurs Here
    return &result;
}

这 2 个代码片段处理您未提供定义的不同结构,也不应该编译,但请注意,如果未定义 WINDOWSgetPath 操作未初始化的结构 result,所以任何事情都有可能发生,包括编译器对你耍花招。

也有可能,由于两个函数都定义为static inline,编译器仅在实际尝试在调用点展开内联getPath时才报告错误,并且仍然报告内联函数定义处的位置错误。

此外,getPath还有一些问题:

  • 它在可能重叠的对象上调用 memcpy,这也会调用未定义的行为:改为使用 memmove

  • result.executableStringLen = ++j; 不正确,应删除。如果找到'/',则长度已经更新,但在搜索之前应将其初始化为result.executableStringLen = i;

  • 如果未找到 '/',您实际上清除了数组,除了第一个字节。您应该从 j = result.executableStringLen.

  • 开始