코딩을 하거나 문제를 풀 때 배열을 선언하고, 그 안에 초기값들을 채워야 할 경우가 많습니다.
매번 초기화를 하거나 배열이 2,3차원이 되면 코드가 불필요하게 길어지고 가독성이 떨어지는 경우가 있습니다.
C와 C++에서는 이를 방지하기 위해 memset과 fill 두 가지의 배열 초기화 함수를 제공합니다.
변수 초기화를 안한다면?
일반적으로 지역변수에 배열을 선언하고 초기화 해주지 않으면 다음과 같은 쓰레기 값이 채워져 있습니다.
따라서 초기화 작업이 반드시 필요합니다.
#include <bits/stdc++.h>
using namespace std;
int main(){
int arr[10];
bool arr2[10];
for(int i=0 ; i<10 ; i++){
cout << arr[i] << " ";
}
cout << '\n';
return 0;
}
선언과 동시에 초기화
배열 arr 원소 10개가 모두 0으로 초기화 됩니다.
int arr[10] = {};
배열 arr 의 0번째 원소만 1로 초기화 됩니다.
int arr[10] = { 1 };
배열 arr 의 0 번째 원소만 1로 초기화 하고 나머지는 다 0으로 초기화 됩니다.
int arr[10] = { 1, };
반복문을 사용하면 모든 원소를 0이 아닌 특정 값으로 초기화 됩니다.
for(int i=0 ; i<n ; i++){
arr[i] = 10;
}
memset( ) 함수
memset은 char 형과 같은 1byte 자료형에서
원하는 값으로 초기화 할 수 있습니다.
memset 함수 구조
void *ptr : 배열이나 어떤 container의 자료형의 시작 포인터
int value : 배열이나 어떤 container에 채울 자료. 이 값은 unsigned char로 변환되어 삽입
size_t num : 배열이나 어떤 container에 int value 값을 얼마나 넣을지 결정
void memset(void *ptr, int value, size_t num)
int value 에서 unsigned char 로 변환되는 부분은 1byte 단위로 해당 메모리를 초기화 시킨다는 의미입니다.
int형이나 long long형과 같은 4~8byte 자료형이 1byte를 넘어간다면 자동으로 4byte와 8byte로 늘립니다.
이 점이 memset에서 에러가 발생하는 부분입니다.
다시 말해 1byte가 넘어가는 자료형은 -1, 0 이외 수는 원하는 값으로 초기화 되지 않습니다.
이 문제로 보통 memset은 -1, 0으로 초기화할 때만 사용하기를 권장합니다.
다음 코드를 통해 arr2 배열을 false로 초기화 해줍니다.
// memset({초기화 해줄 배열의 주소}, {초기화 할 값}, {배열의 크기});
memset(arr2, False, sizeof(arr));
fill( ) 함수
fill 함수는 느리지만 안전합니다.
fill 함수 구조
ForwardIt first : 배열이나 어떤 container 시작 Iterator, 배열의 경우 첫 포인터
ForwardIt last : 배열이나 어떤 container 끝 Iterator, 배열의 경우 마지막 포인터
const T& value : 배열이나 어떤 container 자료형에 넣을 값
void fill(ForwardIt first, ForwardIt last, const T& value)
내부적으로 for문과 비슷한 방식으로 채워나가기 때문에
memset과 다르게 value 자료형에서의 오버플로우가 아니라면 원하는 값을 채울 수 있습니다.
다음 코드를 통해 arr 배열을 5로 초기화 해줍니다.
// fill({시작할 배열의 주소}, {끝나는 주소}, {초기화 할 값});
fill(arr, arr+10, 5);
1차원 배열
fill(arr, arr+10, 5);
2차원 배열
fill(&map[0][0], &map[ROW-1][COL], 5);
// fill(&arr[0][0], &arr[0][0] + 10*10, 5) 로도 표현이 가능
벡터
fill(v.begin(), v.end(), 5);
memset( ) vs fill( )
그렇다면 memset 함수와 fill 함수 중 어떤 함수가 더 빠를까요?
아래의 코드는 1000x1000 크기의 int형 arr 배열에 memset, fill 함수를
각각 10,000번 수행하고 수행 시간을 비교하는 코드입니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | #include <bits/stdc++.h> #include <time.h> using namespace std; int arr[1000][1000]; int main() { time_t start, end; double result_memset, result_fill; start = clock(); for (int i = 0; i < 10000; i++) { memset(arr, 0, sizeof(arr)); } end = clock(); result_memset = (double)end - start; start = clock(); for (int i = 0; i < 10000; i++) { fill(&arr[0][0], &arr[0][0]+1000*1000, 0); } end = clock(); result_fill = (double)end - start; cout << "memset : " << result_memset << "ms" << endl; cout << "fill : " << result_fill << "ms" << endl; return 0; } | cs |
memset은 약 331초, fill은 약 9175초가 나와 memset이 fill보다 2-30배 월등하게 빠른 것을 확인할 수 있습니다.
이러한 차이는 memset과 fill 구현 방식에서 기인합니다.
memset은 C언어보다 더 low-level 에서 처리하기 때문에 더 빠른 실행이 가능합니다.
반면 fill은 for문을 사용하여 채우는 것과 비슷하게 처리되어 memset보다 느린 실행 속도를 가집니다.
그럼에도 테스트 케이스가 정말 많지 않다면 fill 함수를 사용하는 것을 권장합니다!
참조 : https://0xffffffff.tistory.com/32