memset
은 <cstring> 헤더에 들어있습니다.
배열 초기화할 때 memset
을 사용하는 분들이 많을 텐데요, fill_n
은 iteration으로 값을 초기화하기 때문에 느릴 것 같아서, fill
은 그냥 memset
보다 느릴 것 같아서(?) memset
을 쓰는 분이 많을 거라 생각합니다.
아니면 그냥 memset
이 쓰기 편해서 쓸 수도 있고요.
어떤 이유건간에 memset
을 배열을 특정값으로 초기화 하려면 각별히 주의하셔야 합니다.
배열의 값을 0, -1 이외의 값으로 초기화 할 때는 사용하면 안됩니다.
void* memset( void* dest, int ch, std::size_t count );
memset
의 함수 원형입니다.
- dest: 값을 채울 오브젝트의 포인터
- ch: 채울 값
- count: 채울 byte 수
여기서 함정이 있는데, ch가 int 변수지만 unsigned char
, 즉 1바이트 값으로 변환되어 count 만큼의 바이트만큼 채워지게 됩니다.
배열의 값을 3으로 초기화 하려고 memset
파라미터로 3을 넣으면 어떻게 될까요?
#include <iostream>
#include <cstring>
using namespace std;
int main() {
int arr[10];
memset(arr, 3, sizeof(arr));
for (auto& c : arr)
cout << c << ' '; // 50529027
return 0;
}
3이 아니라 50529027이라는 값이 나오네요. 왜 그럴까요?
arr[i] = 00000011000000110000001100000011
배열 크기만큼의 모든 바이트에 3(0b00000011)을 대입했기 때문에 이렇게 이상한 값으로 초기화 되는 겁니다.
0과 -1만 memset
으로 잘 초기화 되는 이유는 0은 00000000, -1은 11111111의 값으로 캐스팅 되기 때문에 배열의 초기화랑 잘 맞아떨어져서 되는 겁니다.
그러면 0, -1이 아닌 값으로 배열 초기화를 어떻게 할까요? 그건 fill
, fill_n
을 써야 합니다.
fill
, fill_n
은 무조건 반복문으로 초기화를 해서 느릴까요? 무조건 그런건 아닙니다.
내부적으로 memset
을 쓸 수 있으면 쓰고, 아니면 반복문으로 초기화 하는 식으로 구현되어 있기 때문에 동일합니다.
MSVC의 STL 소스를 보면 fill
이 호출됐을 때 _Fill_memset_is_safe
.... 가 뭔진 모르겠지만 이 값을 확인해서 memset
이 가능한 경우 memset
으로 메모리에 때려넣고, 아니면 밑에 반복문으로 초기화 시키는 것으로 보입니다.
gcc도 확인해봤는데 비슷합니다. fill
함수가 __fill_a
셋중에 하나를 골라 호출하는 방식이네요. 셋 중 하나는 __builtin_memset
을 사용하는 걸 알 수 있습니다.
그런데! 비주얼 스튜디오에서 이것저것 fill
을 사용해봤는데 디버깅해서 찍어보니까 전부 반복문으로 돌아갑니다;;; 왜죠??
대체 언제 fill
을 호출했을 때 memset
이 되는건지;;; 잘 모르겠습니다. 아시는 분은 댓글 부탁합니다
'프로그래밍 > C++' 카테고리의 다른 글
C++ 배열의 값이 전부 같은지 확인하는 방법 (std::equal) (0) | 2020.06.20 |
---|---|
intrinsic popcount, clz, ctz (gcc, msvc) (2) | 2020.06.09 |
C++ regex syntax_option_type (0) | 2020.05.08 |
C++ const, constexpr 키워드 차이점 (0) | 2020.05.06 |
C++ lower_bound, upper_bound 사용법 (0) | 2020.05.01 |