问题描述
在以下代码中:
#include <cstring>
template <unsigned len>
struct Chararray {
Chararray() {
memset(data_,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 -Wextra
进行编译时会发生这种情况。如果我没有通过任何优化标志,就不会有任何警告。另外:如果我把构造函数拿走,警告也会消失。
解决方法
问题似乎与 memset()
有某种关系:由于使用条件 (len != 0
) 避免它不起作用,似乎编译器认识到 CharArray<0>
的起始地址的对象由 CharArray<3>
的初始化生成并对此发出警告。可以通过有条件地不使用 CharArray<3>
初始化 memset()
或专门化该类型来测试该理论,因为这会使警告生效:
CharArray() { if (len != 3) memset(data_,len); }
或
template <>
struct CharArray<3> {
CharArray(): data_() { }
char data_[3];
};
警告可能是虚假的。似乎在使用零大小数组的地址时,编译器已经“忘记”它是通过访问不同数组的成员产生的。避免警告的最简单方法似乎是正确初始化初始化列表中的 data
而根本不使用 memset()
:
template <unsigned len>
struct CharArray {
CharArray(): data_() {}
char data_[len];
};
,
对零长度的类似 C 的数组做任何事情都是非常可疑的。包括在我看来甚至定义一个。
但是,您可以将构造函数专门化为不对零长度数组执行任何操作:
axios.interceptors.response.use((response) => {
if (response.config.responseType === 'blob') {
downloadFileAxios(response);
}
return response;
},async (error) => {
if (error.response.status === 401) {
// 401 redirect
const data = await axios.get('/api/openapi/login');
if (data.data && data.data.url) {
window.location.href = data.data.url;
}
} else {
return Promise.reject(error);
}
});
为了甚至不定义零大小的数组(我认为这不应该成为您可能希望通过该类实现的任何障碍的障碍...),您必须专门化整个类。 (此添加归功于 Dietmar Kühl)