C++ 将数组的所有元素初始化为相同的数字
不久前,我的老老师发布了这段代码,说这是将数组初始化为相同数字(当然不是零)的另一种方法 在这种情况下是三个 他说这种方法比C++ 将数组的所有元素初始化为相同的数字,c++,arrays,for-loop,c++14,C++,Arrays,For Loop,C++14,不久前,我的老老师发布了这段代码,说这是将数组初始化为相同数字(当然不是零)的另一种方法 在这种情况下是三个 他说这种方法比for循环稍微好一点。为什么我需要左换档操作员?为什么我需要另一个长数组? 我不明白这里发生了什么 int main() { short int A[100]; long int v = 3; v = (v << 16) + 3; v = (v << 16) + 3; v = (v << 16)
for
循环稍微好一点。为什么我需要左换档操作员?为什么我需要另一个长数组?
我不明白这里发生了什么
int main() {
short int A[100];
long int v = 3;
v = (v << 16) + 3;
v = (v << 16) + 3;
v = (v << 16) + 3;
long *B = (long*)A;
for(int i=0; i<25; i++)
B[i] = v;
cout << endl;
print(A,100);
}
intmain(){
短整数A[100];
长整数v=3;
v=(v这绝对是一堆废话
对于初学者来说,v
将在编译时计算
在long*B=(long*)A;
之后取消引用B
的行为未定义,因为类型不相关。B[i]
是对B
的取消引用
没有任何理由认为长的
比短的
大四倍
以简单的方式使用for
循环,并相信编译器会进行优化。请相当好,上面有糖。他假设长的比短的长四倍(这不能保证;他应该使用int16和int64)
他用更长的内存空间(64位)填充四个短的(16位)值。他通过将位移位16个空间来设置值
然后他想把一个短数组当作一个长数组,这样他就可以通过25次循环迭代而不是100次循环来设置100个16位的值
这是你老师的想法,但正如其他人所说,这种类型转换是未定义的行为。有很多方法可以用相同的值填充数组,如果你关心性能,那么你需要进行测量
C++有一个专用函数,用于用值填充数组,我会使用这个函数(在之后)#include是一个非常好的工具,如果您准备学习一点汇编程序。正如您从中看到的,编译器可以优化fill
调用到12(和一位)128位存储,任何循环都展开。因为编译器知道目标环境,所以他们可以在源代码中不编码任何特定于目标的假设来完成此操作。我认为他试图通过同时复制多个数组元素来减少循环迭代次数。正如其他用户已经在这里提到的,这逻辑会导致未定义的行为
如果这一切都是为了减少迭代次数,那么通过循环展开,我们可以减少迭代次数。但是对于这样小的数组,它不会明显更快
int main() {
short int A[100];
for(int i=0; i<100; i+=4)
{
A[i] = 3;
A[i + 1] = 3;
A[i + 2] = 3;
A[i + 3] = 3;
}
print(A, 100);
}
intmain(){
短整数A[100];
对于(int i=0;i,正如其他答案所解释的,代码违反了类型别名规则,并做出了标准无法保证的假设
如果您真的想手动执行此优化,这将是一种具有明确行为的正确方法:
long v;
for(int i=0; i < sizeof v / sizeof *A; i++) {
v = (v << sizeof *A * CHAR_BIT) + 3;
}
for(int i=0; i < sizeof A / sizeof v; i++) {
std:memcpy(A + i * sizeof v, &v, sizeof v);
}
长v;
对于(int i=0;i V=(V)问题有C++标记(无C标记),因此应该用C++风格:
// C++ 03
std::vector<int> tab(100, 3);
// C++ 11
auto tab = std::vector<int>(100, 3);
auto tab2 = std::array<int, 100>{};
tab2.fill(3);
<代码> /C++ 03
标准::向量标签(100,3);
//C++ 11
自动选项卡=标准::向量(100,3);
auto tab2=std::array{};
表2.填料(3);
此外,老师还试图比编译器更聪明,它可以做一些令人兴奋的事情。这样做没有意义,因为如果配置正确,编译器可以为您完成:
如您所见,每个版本的-O2
结果代码(几乎)是相同的。对于-O1
,技巧可以提供一些改进
因此,底线是,你必须做出选择:
- 编写难读代码,不要使用编译器优化
- 编写可读代码并使用
-O2
使用Godbolt站点试验其他编译器和配置。
另请参见。您的老师向您展示的代码是一个格式错误的程序,无需诊断,因为它违反了指针实际指向其声称指向的对象的要求(也称为“严格别名”)
作为一个具体的例子,编译器可以分析您的程序,注意a
没有直接写入,也没有short
被写入,并证明a
在创建后从未更改
< > >代码> B/COD>可以证明,在C++标准下,不能在一个格式良好的程序中修改<代码> A<代码>。
(;)
的循环,甚至是范围内的for,很可能会被优化到A
的静态初始化。在优化编译器下,您老师的代码将优化到未定义的行为
如果确实需要一种方法来创建用一个值初始化的数组,可以使用以下方法:
template<std::size_t...Is>
auto index_over(std::index_sequence<Is...>) {
return [](auto&&f)->decltype(auto) {
return f( std::integral_constant<std::size_t, Is>{}... );
};
}
template<std::size_t N>
auto index_upto(std::integral_constant<std::size_t, N> ={})
{
return index_over( std::make_index_sequence<N>{} );
}
template<class T, std::size_t N, T value>
std::array<T, N> make_filled_array() {
return index_upto<N>()( [](auto...Is)->std::array<T,N>{
return {{ (void(Is),value)... }};
});
}
模板
自动索引结束(标准::索引顺序){
return[](自动和&f)->decltype(自动){
返回f(std::积分常数{};
};
}
模板
自动索引(std::integral_常量={})
{
返回索引_over(std::make_index_sequence{});
}
模板
std::数组生成填充数组(){
返回索引_upto()([](自动…是)->std::array{
返回{(void(Is),value)…};
});
}
现在:
int main() {
auto A = make_filled_array<short, 100, 3>();
std::cout << "\n";
print(A.data(),100);
}
intmain(){
自动A=生成填充数组();
std::我不知道为什么它应该稍微好一点,因为它似乎有未定义的行为。这应该是性能增强吗?请注意,v最后是0x30000000300000003L
,假设它没有首先溢出。溢出只是这个代码的第一个问题-这个老师不应该被教GC++。不用担心,编译器也不理解。如果代码在特定的机器上工作,它是一个特定的编译器,它只是偶然的。这仍然是使用for循环,它只是使用一个较小的循环,用25次迭代而不是100次迭代。如果<代码> siZeof(long)int main() {
auto A = make_filled_array<short, 100, 3>();
std::cout << "\n";
print(A.data(),100);
}