7

在以下代码中:

#include <cstring>

template <unsigned len>
struct CharArray {
  CharArray() {
    memset(data_, 0, len);
  }
  char data_[len];
};

struct Foobar {
  CharArray<5> a;
  CharArray<3> b;
  CharArray<0> c;
};

int main() {
  Foobar f;
}

该类型CharArray<0>最终将一个大小为零的数组作为其唯一成员。我知道这通常是 GCC 扩展和不安全的做法。问题不在于那个。

当我使用 gcc 10.2.0 编译代码时,我收到以下警告:

<source>: In function 'int main()':
<source>:5:3: warning: array subscript 8 is outside array bounds of 'Foobar [1]' [-Warray-bounds]
    5 |   CharArray() {
      |   ^~~~~~~~~
<source>:18:10: note: while referencing 'f'
   18 |   Foobar f;
      |          ^

使用 gcc9 及更早版本没有警告。

问题:下标 8 是从哪里来的?Foobar [1]那里提到了什么?看起来有一个 Foobars 的数组,我们正在尝试访问该数组中的元素 8。不知道这怎么可能发生。如果有人知道细节,如果您能解释一下,我将不胜感激。

gcc++-10在 Ubuntu 20.04 中使用-O3 -Wall -Wextraas 选项编译时会发生这种情况。如果我不通过任何优化标志,则不会有任何警告。另外:如果我把构造函数拿走,警告也会消失。

4

2 回答 2

3

似乎这个问题与memset(): 有某种关系,因为使用条件 ( len != 0) 避免它不起作用,编译器似乎认识到CharArray<0>' 对象的起始地址是由初始化产生的,CharArray<3>并对此发出警告。CharArray<3>可以通过有条件地不使用该类型进行初始化或专门化该类型来测试该理论memset(),因为这会使警告消失:

CharArray() { if (len != 3) memset(data_, 0, len); }

或者

template <>
struct CharArray<3> {
  CharArray(): data_() { } 
  char data_[3];
};  

该警告可能是虚假的。似乎在使用零大小数组的地址时,编译器已经“忘记”它是通过访问不同数组的成员而产生的。避免警告的最简单方法似乎是data在初始化列表中正确初始化并且根本不使用memset()

template <unsigned len>
struct CharArray {
  CharArray(): data_() {}
  char data_[len];
};  
于 2020-12-22T09:16:07.343 回答
2

对一个零长度的类 C 数组做任何事情都是非常可疑的。在我看来,甚至包括定义一个。

但是,您可以专门构造函数以不对零长度数组执行任何操作:

template<> CharArray<0>::CharArray() {}

为了甚至不定义一个零大小的数组(我认为这不应该成为您可能希望在一般类中实现的任何障碍......),您必须专门化整个类。(此添加归功于 Dietmar Kühl)

于 2020-12-22T09:17:06.997 回答