对于作为指针的迭代器ptr
,std::fill_n(ptr, n, 0)
应该与memset(ptr, 0, n * sizeof(*ptr))
做同样的事情(但请参阅@ KeithThompson对this answer的评论)。
对于C ++ 11 / C ++ 14 / C ++ 17模式下的C ++编译器,我可以期望将这些条件编译为相同的代码吗?如果他们不编译到相同的代码,是否与-O0有显着的性能差异? -O3?
注意:当然,一些/大部分答案可能是特定于编译器的。我只对一两个特定的编译器感兴趣,但请写下你知道答案的编译器。
答案 0 :(得分:6)
答案取决于您对标准库的实施。
例如,MSVC根据您尝试填写的内容的类型,有std::fill_n
的几种实现。
使用std::fill_n
或char*
或signed char*
呼叫unsigned char*
,并直接呼叫memset
以填充数组。
inline char *_Fill_n(char *_Dest, size_t _Count, char _Val)
{ // copy char _Val _Count times through [_Dest, ...)
_CSTD memset(_Dest, _Val, _Count);
return (_Dest + _Count);
}
如果您使用其他类型进行呼叫,则会填写一个循环:
template<class _OutIt,
class _Diff,
class _Ty> inline
_OutIt _Fill_n(_OutIt _Dest, _Diff _Count, const _Ty& _Val)
{ // copy _Val _Count times through [_Dest, ...)
for (; 0 < _Count; --_Count, (void)++_Dest)
*_Dest = _Val;
return (_Dest);
}
确定特定编译器和标准库实现的开销的最佳方法是使用两个调用来分析代码。
答案 1 :(得分:1)
对于适合memset
的所有场景(即所有对象都是POD),当启用任何优化级别时,您很可能会发现这两个语句是等效的。
对于memset
不合适的情况,比较没有实际意义,因为使用memset
会导致程序错误。
您可以使用诸如godbolt(和许多其他人)之类的工具轻松检查自己:
例如,在gcc6.2上,这两个函数生成字面相同的代码,优化级别为-O3:
#include <algorithm>
#include <cstring>
__attribute__((noinline))
void test1(int (&x) [100])
{
std::fill_n(&x[0], 100, 0);
}
__attribute__((noinline))
void test2(int (&x) [100])
{
std::memset(&x[0], 0, 100 * sizeof(int));
}
int main()
{
int x[100];
test1(x);
test2(x);
}