我正在把一些代码集成到我的库中。它是一个复杂的数据结构,对速度进行了很好的优化,所以我尽量不对其进行太多的修改。集成过程进行得很好,实际上几乎已经完成(它编译)。有一件事还在困扰着我。我多次收到C4200警告:
warning C4200: nonstandard extension used : zero-sized array in struct/union
Cannot generate copy-ctor or copy-assignment operator when UDT contains a zero-sized array代码工作,但这个警告让我感到毛骨悚然(特别是复制器的部分)。出现THe警告是因为这样声明的结构:
#pragma pack( push )
#pragma pack( 1 )
// String
struct MY_TREEDATSTR
{
BYTE btLen;
DWORD dwModOff;
BYTE btPat[0];
};
typedef MY_TREEDATSTR TREEDATSTR;
typedef MY_TREEDATSTR *PTREEDATSTR;
#pragma pack( pop )注意btPat[0]。是否有一种方法可以轻松地和正确地消除此警告,而不破坏代码和/或在其中更改太多。注意#pragma's,根据这个警告有任何意义吗?为什么这个结构是这样声明的?(我指的是btPat的东西,而不是#pragma的,我理解的那些人)。
注意:我看到this similar question了,但它真的帮不了我。
更新:正如我所说的,代码工作并给出了正确的结果。因此,复制构造函数或赋值操作符显然并不需要。在我看代码的时候,所有的结构都没有记忆。
发布于 2010-07-28 08:03:35
我将假设您确实希望在纯C++模式下编译这些文件,并且不希望只编译C中的一些文件,以C++和以后的链接编译一些文件。
警告告诉您,编译器生成的复制构造函数和赋值很可能与您的结构错误。在结构的末尾使用零大小数组通常是一种在运行时决定的数组的方式,但在C++中是非法的,但是您可以得到大小为1的类似行为:
struct runtime_array {
int size;
char data[1];
};
runtime_array* create( int size ) {
runtime_array *a = malloc( sizeof(runtime_array) + size ); // [*]
a->size = size;
return a;
}
int main() {
runtime_array *a = create( 10 );
for ( int i = 0; i < a->size; ++i ) {
a->data[i] = 0;
}
free(a);
}这种类型的结构应该是动态分配的--或者使用动态堆栈分配机制--而且通常不会被复制,但是如果您尝试,就会得到奇怪的结果:
int main() {
runtime_array *a = create(10);
runtime_array b = *a; // ouch!!
free(a);
}在本例中,编译器生成的复制构造函数将在堆栈中准确地分配sizeof(runtime_array)字节,然后将数组的第一部分复制到b中。问题是b有一个size字段,表示为10,但是没有任何元素的内存。
如果您仍然希望能够在C中编译这一点,那么您必须通过闭上眼睛来解决这个警告:沉默的特定警告。如果只需要C++兼容性,则可以手动禁用复制构造和分配:
struct runtime_array {
int size;
char data[1];
private:
runtime_array( runtime_array const & ); // undefined
runtime_array& operator=( runtime_array const & ); // undefined
};通过声明复制构造函数和赋值操作符,编译器将不会为您生成副本构造函数和赋值操作符(也不会抱怨它不知道如何生成)。如果错误地尝试在代码中使用这两个私有信息,您将得到编译时错误。因为它们从来没有被调用过,所以它们可以不被定义--这也被用来避免在类的不同方法中调用它们,但是我假设没有其他方法。
由于您正在对C++进行重构,所以我还会将默认构造函数设置为私有,并提供一个静态的公共内联方法,该方法将负责内容的适当分配。如果还将析构函数设置为私有,则可以确保用户代码不会尝试对对象调用delete:
struct runtime_array {
int size;
char data[1];
static runtime_array* create( int size ) {
runtime_array* tmp = (runtime_array*)malloc(sizeof(runtime_array)+size);
tmp->size = size;
return tmp;
}
static void release( runtime_array * a ) {
free(a);
}
private:
runtime_array() {}
~runtime_array() {}
runtime_array( runtime_array const & ); // undefined
runtime_array& operator=( runtime_array const & ); // undefined
};这将确保用户代码不会错误地在堆栈中创建对象,也不会将对malloc/free的调用与对new/delete的调用混为一谈,因为您管理对象的创建和销毁。所有这些更改都不会影响对象的内存布局。
*这里的大小计算有点偏离,并且会过多分配,可能与对象末尾填充的大小一样多( sizeof(int) )。
发布于 2010-07-28 07:55:10
如果这是MSVC编译器(这是警告消息告诉我的),那么您可以使用#杂注警告(即:
#pragma warning( push )
#pragma warning( disable : 4200 )
struct _TREEDATSTR
{
BYTE btLen;
DWORD dwModOff;
BYTE btPat[0];
};
#pragma warning( pop )顺便说一句,关于复制构造函数的消息并不令人毛骨悚然,但是是一件好事,因为它意味着,如果没有btPat中的未知字节,您就不能复制_TREEDATSTR的实例:编译器不知道_TREEDATSTR到底有多大(因为有0大小的数组),因此拒绝生成副本构造函数。这意味着你不能这样做:
_TREEDATSTR x=y;反正也没用的。
发布于 2010-07-28 07:57:05
尝试将其改为btPat[1]。我认为C++和C标准都规定数组不能有0元素。它可能会对依赖于_TREEDATSTR结构本身大小的任何代码造成问题,但通常这类结构是从缓冲区中键入的,在缓冲区中(在本例中),缓冲区的第一个字节决定了btPat中实际的字节数。这种方法依赖于C数组没有边界检查这一事实。
https://stackoverflow.com/questions/3350852
复制相似问题