如何正确修复“零大小的结构/联合数组”警告(C4200)而不破坏代码? [英] How to correctly fix "zero-sized array in struct/union" warning (C4200) without breaking the code?

查看:3479
本文介绍了如何正确修复“零大小的结构/联合数组”警告(C4200)而不破坏代码?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在将一些代码集成到我的库中。它是一个复杂的数据结构,为速度进行了优化,所以我尽量不要修改太多。集成过程进行顺利,实际上几乎完成(它编译)。有一件事仍然困扰着我。我得到C4200警告多次:

 警告C4200:使用非标准扩展:struct / union中的零大小数组
当UDT包含零大小的数组时,无法生成复制转储或复制赋值运算符

代码有效,但此警告为我提供creep(尤其是copy-ctor的部分)。发出警告,因为结构声明如下:

  #pragma pack(push)
#pragma pack
//字符串
struct MY_TREEDATSTR
{
BYTE btLen;
DWORD dwModOff;
BYTE btPat [0];
};

typedef MY_TREEDATSTR TREEDATSTR;
typedef MY_TREEDATSTR * PTREEDATSTR;

#pragma pack(pop)

注意 btPat [0] 。是否有一种方法如何轻松和正确摆脱此警告,而不破坏代码和/或不得不改变太多。注意 #pragma 的,根据这个警告有什么意义?为什么结构以这种方式声明? (我的意思是 btPat 的东西,而不是 #pragma 的,我明白)。



注意:我看到这个类似的问题,但它确实没有帮助我。



更新:正如我所说,代码工作,并给出正确的结果。所以一个复制构造函数或赋值操作符显然是不需要的。我看看代码,没有任何结构得到memcpy-ed。

解决方案

这是在纯C ++模式下编译的,你不想只是编译一些文件在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 size; ++ i){
a-> data [i] = 0;
}
free(a);
}

这种类型的结构意味着动态分配 - 但是如果你尝试你会得到奇怪的结果:

  int main(){ 
runtime_array * a = create(10);
runtime_array b = * a; //哎哟!
free(a);在这个例子中,编译器生成的复制构造函数将分配完全






$ b > 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
};

这将确保用户代码不会错误地在堆栈中创建对象, new / delete 调用 malloc / free ,因为您管理对象的创建和销毁。这些更改都不会影响对象的内存布局。



[*]这里的大小计算有点偏差,会过度分配, sizeof(int),因为对象的大小在最后有填充。


I'm integrating some code into my library. It is a complex data structure well optimized for speed, so i'm trying not to modify it too much. The integration process goes well and actually is almost finished (it compiles). One thing is still bothering me. I'm getting the C4200 warning multiple times:

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 code works but this warning gives me creeps (especially the part with copy-ctor). THe warning appears because of structures declared like this:

#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 )

Note the btPat[0]. Is there a way how to easily and correctly get rid of this warning without breaking the code and/or having to change too much in it. Notice the #pragma's, have their any significance according to this warning? And why is the structure declared this way anyway? (I mean the btPat thing, not the #pragma's, those i understand).

Note: i saw this similar question, but it really didn't help me.

Update: as I said, the code works and gives correct results. So a copy-constructor or assignment operator is apparently really not needed. And as i look at the code, none of the structures get memcpy-ed.

解决方案

I'll assume that you do want this to be compiled in pure C++ mode, and that you don't want just to compile some files in C and some in C++ and later link.

The warning is telling you that the compiler generated copy constructor and assignment will most probably be wrong with your structure. Using zero-sized arrays at the end of a struct is usually a way, in C, of having an array that is decided at runtime, but is illegal in C++, but you can get similar behavior with a size of 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);
}

This type of structures are meant to be allocated dynamically --or with dynamic stack allocation trickery--, and are not usually copied, but if you tried you would get weird results:

int main() {
   runtime_array *a = create(10);
   runtime_array b = *a;          // ouch!!
   free(a);
}

In this example the compiler generated copy constructor would allocate exactly sizeof(runtime_array) bytes in the stack and then copy the first part of the array into b. The problem is that b has a size field saying 10 but has no memory for any element at all.

If you still want to be able to compile this in C, then you must resolve the warning by closing your eyes: silent that specific warning. If you only need C++ compatibility, you can manually disable copy construction and assignment:

struct runtime_array {
   int size;
   char data[1];
private:
   runtime_array( runtime_array const & );            // undefined
   runtime_array& operator=( runtime_array const & ); // undefined
};

By declaring the copy constructor and assignment operator the compiler will not generate one for you (and won´t complain about it not knowing how). By having the two private you will get compile time errors if by mistake you try to use it in code. Since they are never called, they can be left undefined --this is also used to avoid calling it from within a different method of the class, but I assume that there are no other methods.

Since you are refactoring to C++, I would also make the default constructor private and provide a static public inlined method that will take care of the proper allocation of the contents. If you also make the destructor private you can make sure that user code does not try to call delete on your objects:

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
};

This will ensure that user code does not by mistake create your objects in the stack nor will it mix calls to malloc/free with calls to new/delete, since you manage creation and destruction of your objects. None of this changes affects the memory layout of your objects.

[*] The calculation for the size here is a bit off, and will overallocate, probably by as much as sizeof(int) as the size of the object has padding at the end.

这篇关于如何正确修复“零大小的结构/联合数组”警告(C4200)而不破坏代码?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆