为什么 MSVC 不初始化这个 const 结构?
Why doesn't MSVC initialize this const struct?
我有一些C写的代码,使用Visual Studio2015 Community时有一段不肯配合(clang没问题)。我有一个简单的结构:
/** Options for enumerating over all documents. */
typedef struct {
unsigned skip; /**< The number of initial results to skip. */
C4EnumeratorFlags flags; /**< Option flags */
} C4EnumeratorOptions;
enum {
kC4Descending = 0x01, /**< If true, iteration goes by descending document IDs. */
kC4InclusiveStart = 0x02, /**< If false, iteration starts just _after_ startDocID. */
kC4InclusiveEnd = 0x04, /**< If false, iteration stops just _before_ endDocID. */
kC4IncludeDeleted = 0x08, /**< If true, include deleted documents. */
kC4IncludeNonConflicted = 0x10, /**< If false, include _only_ documents in conflict. */
kC4IncludeBodies = 0x20 /**< If false, document bodies will not be preloaded, just
metadata (docID, revID, sequence, flags.) This is faster if you
don't need to access the revision tree or revision bodies. You
can still access all the data of the document, but it will
trigger loading the document body from the database. */
};
typedef uint16_t C4EnumeratorFlags;
而且我也有一个常量 "default" 值:
// In header
extern const C4EnumeratorOptions kC4DefaultEnumeratorOptions;
// In implementation
const C4EnumeratorOptions kC4DefaultEnumeratorOptions = {
0, // skip
kC4InclusiveStart | kC4InclusiveEnd | kC4IncludeNonConflicted | kC4IncludeBodies
};
但是,在调试时我注意到当我尝试使用默认值时初始化没有做任何事情:
// options winds up with a "skip" value of something like 117939945
// and a flags value of 59648
C4EnumeratorOptions options = kC4DefaultEnumeratorOptions;
段定义在DLL中,第二次使用在exe中。同样,这只发生在 Windows。此外,"options" 中的值是垃圾,但出于某种原因,它甚至与存储在 kC4DefaultEnumeratorOptions
中的垃圾不同。我知道 MSVC 因冷落 C 而臭名昭著,但这种初始化方式太古老了,即使 MSVC 也应该正确处理,不是吗?所以这一定是我正在做的事情,但我不知道是什么。
EDIT 该符号正在通过导出定义文件导出。我检查了 dumpbin,并在导出的符号列表
中找到了该符号
41 46 00A6EA8 kC4DefaultEnumeratorOptions = kC4DefaultEnumeratorOptions
还有一点信息,调用代码是 C++,DLL 代码是 C,我怀疑这可能在这种疯狂中发挥了作用。
@M.M 的评论帮助我找到了正确的方向。他问符号是否被导出。从技术上讲,是的,它已被导出,因为它在导出列表中,但显然我还需要导出 定义。因此,我不需要在 .def 文件中包含全局符号,而是需要在两个地方用 __declspec(dllexport)
或 __declspec(dllimport)
手动标记它,所以最后它看起来像这样:
#ifdef _MSC_VER
#ifdef CBFOREST_EXPORTS
#define CBFOREST_API __declspec(dllexport)
#else
#define CBFOREST_API __declspec(dllimport)
#endif
#endif
// ...
// Header
CBFOREST_API extern const C4EnumeratorOptions kC4DefaultEnumeratorOptions;
// Implementation
CBFOREST_API const C4EnumeratorOptions kC4DefaultEnumeratorOptions = {
0, // skip
kC4InclusiveStart | kC4InclusiveEnd | kC4IncludeNonConflicted | kC4IncludeBodies
};
我有一些C写的代码,使用Visual Studio2015 Community时有一段不肯配合(clang没问题)。我有一个简单的结构:
/** Options for enumerating over all documents. */
typedef struct {
unsigned skip; /**< The number of initial results to skip. */
C4EnumeratorFlags flags; /**< Option flags */
} C4EnumeratorOptions;
enum {
kC4Descending = 0x01, /**< If true, iteration goes by descending document IDs. */
kC4InclusiveStart = 0x02, /**< If false, iteration starts just _after_ startDocID. */
kC4InclusiveEnd = 0x04, /**< If false, iteration stops just _before_ endDocID. */
kC4IncludeDeleted = 0x08, /**< If true, include deleted documents. */
kC4IncludeNonConflicted = 0x10, /**< If false, include _only_ documents in conflict. */
kC4IncludeBodies = 0x20 /**< If false, document bodies will not be preloaded, just
metadata (docID, revID, sequence, flags.) This is faster if you
don't need to access the revision tree or revision bodies. You
can still access all the data of the document, but it will
trigger loading the document body from the database. */
};
typedef uint16_t C4EnumeratorFlags;
而且我也有一个常量 "default" 值:
// In header
extern const C4EnumeratorOptions kC4DefaultEnumeratorOptions;
// In implementation
const C4EnumeratorOptions kC4DefaultEnumeratorOptions = {
0, // skip
kC4InclusiveStart | kC4InclusiveEnd | kC4IncludeNonConflicted | kC4IncludeBodies
};
但是,在调试时我注意到当我尝试使用默认值时初始化没有做任何事情:
// options winds up with a "skip" value of something like 117939945
// and a flags value of 59648
C4EnumeratorOptions options = kC4DefaultEnumeratorOptions;
段定义在DLL中,第二次使用在exe中。同样,这只发生在 Windows。此外,"options" 中的值是垃圾,但出于某种原因,它甚至与存储在 kC4DefaultEnumeratorOptions
中的垃圾不同。我知道 MSVC 因冷落 C 而臭名昭著,但这种初始化方式太古老了,即使 MSVC 也应该正确处理,不是吗?所以这一定是我正在做的事情,但我不知道是什么。
EDIT 该符号正在通过导出定义文件导出。我检查了 dumpbin,并在导出的符号列表
中找到了该符号41 46 00A6EA8 kC4DefaultEnumeratorOptions = kC4DefaultEnumeratorOptions
还有一点信息,调用代码是 C++,DLL 代码是 C,我怀疑这可能在这种疯狂中发挥了作用。
@M.M 的评论帮助我找到了正确的方向。他问符号是否被导出。从技术上讲,是的,它已被导出,因为它在导出列表中,但显然我还需要导出 定义。因此,我不需要在 .def 文件中包含全局符号,而是需要在两个地方用 __declspec(dllexport)
或 __declspec(dllimport)
手动标记它,所以最后它看起来像这样:
#ifdef _MSC_VER
#ifdef CBFOREST_EXPORTS
#define CBFOREST_API __declspec(dllexport)
#else
#define CBFOREST_API __declspec(dllimport)
#endif
#endif
// ...
// Header
CBFOREST_API extern const C4EnumeratorOptions kC4DefaultEnumeratorOptions;
// Implementation
CBFOREST_API const C4EnumeratorOptions kC4DefaultEnumeratorOptions = {
0, // skip
kC4InclusiveStart | kC4InclusiveEnd | kC4IncludeNonConflicted | kC4IncludeBodies
};