반응형
sort compare

Sort Algorithm Compare

  • 시험 방법
    정렬 알고리즘들을 500~550개(랜덤)의 값을 만들어 정렬하는 것을 5000번 반복한 시간을 측정하였다.
qsort test
ALG=selection_sort
OK cnt=5000 FAIL cnt=0
4029 ms
ALG=bubble_sort
OK cnt=5000 FAIL cnt=0
3190 ms
ALG=insertion_sort
OK cnt=5000 FAIL cnt=0
2257 ms
ALG=merge_sort
OK cnt=5000 FAIL cnt=0
361 ms
ALG=quick_sort
OK cnt=5000 FAIL cnt=0
236 ms
  • 랜덤한 값들을 정렬할 경우 알고리즘별로 시간 측정 결과 대략적으로 다음과 같은 순서로 성능이 좋았다. 퀵소트 이름이 괜히 퀵이 아니다.
  • quick > merge > insertion > bubble > selection
  • 위 결과는 정렬할 대상의 상태(값 배열)에 따라 달라질 수 있다.
#include <iostream>
#include <vector>
#include <ctime>
#include <chrono>

void selection_sort(int arr[], int size) {
    for(int i = 0 ; i < size - 1 ; i++) 
        for (int j = i + 1 ; j < size ; j++) 
            if(arr[i] > arr[j]) {
                // swap(arr[i], arr[j]);
                int t = arr[i] ;
                arr[i] = arr[j] ;
                arr[j] = t ;
            }
}


void bubble_sort(int a[], int n){
    int i, j, t;
    for (i = 0; i < n - 1; i++) {
        int bchanged=0 ;
        for (j = 1; j < n - i; j++) // j=(1,10)
        {
            if (a[j - 1] > a[j]){
                t = a[j - 1];
                a[j - 1] = a[j];
                a[j] = t;
                bchanged=1 ;
            }
        }
        if (bchanged==0) break ;
    }
}


void insertion_sort(int arr[], int size) {
    for(int i = 1 ; i <= size - 1 ; i++) 
        for (int j = i-1 ; j >= 0 ; j--) 
            if(arr[j] > arr[j+1]) {
                // swap(arr[j], arr[j+1]);
                int t = arr[j] ;
                arr[j] = arr[j+1] ;
                arr[j+1] = t ;
            } 
            else
                continue;
} 



void merge_sort(int arr[], int size) {
    // std::cout << "merge_sort size=" << size << std::endl ;

    if (size > 2) {
        // 왼쪽 반, 오른쪽 반을 나누어 반복.
        merge_sort(arr, size / 2);
        merge_sort(arr + size / 2, size - size / 2);
        
        // 왼쪽, 오른쪽 반이 각각 정렬된 상태임. merge한다.
        int leftCursor = 0;
        int rightCursor = size / 2;
        //int buff[50];
        int *buff = new int[size] ;

        int buffCursor = 0;
        // 두 그룹을 각각 스캐닝하면서 순서대로 기록. (어느 한쪽이 끝날때까지)
        while (leftCursor < size / 2 && rightCursor < size) {
            if (arr[leftCursor] < arr[rightCursor]) {
                buff[buffCursor++] = arr[leftCursor++];
            } else {
                buff[buffCursor++] = arr[rightCursor++];
            }
        }
        // 남은 한 그룹의 데이터를 append 
        for (int i = leftCursor ; i < size / 2 ; i++)
            buff[buffCursor++] = arr[i];
        for (int j = rightCursor ; j < size ; j++) 
            buff[buffCursor++] = arr[j];
        memcpy(arr, buff, size * sizeof(int));
        delete[] buff ;
    } else { // 원소 개수가 2개 이하인 경우, 비교하여 정렬한다.
        if (arr[0] > arr[1]) {
            // swap(arr[0], arr[1]);
            int tmp = arr[0] ;
            arr[0] = arr[1] ;
            arr[1] = tmp ;
        }
    }
}



// a: 정렬할 데이터 array
// n: 데이터 개수
void quick_sort(int a[], int n){
	int v, t;
	int i, j;
	if (n > 1){
		v = a[n - 1]; // v=축값. 마지막 노드값을 사용
		i = -1; 
		j = n - 1; 
		while (1){ // 분할
			// while (a[++i] < v); // 왼쪽에서부터 축값보다 크거나 같은 값이 있는지? -> 없다면??? 없을수는 없다. 자신끼리 비교가 마지막.
            while (a[++i]<v) {
                // std::cout << "debug : i=" << i << std::endl ; 
            }

			// while (a[--j] > v); // 오른쪽에서부터 축값보다 작거나 같은 값이 있는지? -> 가장작은값이 pivot이면?? index out range???
            while( a[--j]>v) {
                // std::cout << "debug : j=" << j << std::endl ; 
                // minus index로 갔는데 에러가 발생하지 않았지만, 쓸데없는 오버로드다. 다른 언어였으면 indexerror이다. 
                if (j==0)   // 전체 검색 완료이므로 더 이상 할 필요없다.
                    break ;
            }
			if (i >= j) 
                break; // i와 j의 위치가 뒤바뀌었으면 분할 끝. 좌우 분할이 이미 잘되어 있다. 
            // i와 j의 위치가 좌, 우 상태 그대로 라면 
			t = a[i]; // 두 값을 바꿈. 
			a[i] = a[j];
			a[j] = t;
		}
        // 피봇이 들어갈 자리는? 왼쪽을 기준으로 정렬이 다 된 상태에서 마지막 i자리에 큰 값이 와 있다. 
		t = a[i]; // 축 값과 축값의 위치에 있는 값을 바꿈
		a[i] = a[n - 1];
		a[n - 1] = t;
        // i위치 기준으로 좌우 대소 분할 완료.
		quick_sort(a, i); // 왼쪽 구간 퀵정렬. (i개만 한다. 즉, i위치는 개수에 포함 안함.)
		quick_sort(a + i + 1, n - i - 1); // 오른쪽 구간 퀵정렬 (index로 치면 a+i+1부터 a+n-1. 따라서 개수는 a+n-1 - (a+i+1)+1 )
	}
}



void print_array(int *a, int n) {
    for(int i=0; i<n; i++)
    {
        std::cout << a[i] << " " ;
    }
    std::cout << std::endl ;
}

int check_asc(int *a, int n) {
    if (n<=1) return 1 ;
    for (int i=1; i<n; i++) {
        if (a[i-1]>a[i])
            return 0 ;
    }
    return 1 ;
}

int test_alg(void (*pfunc)(int[], int)) {
    int cnt = std::rand()%50+500 ;
    int *a = new int[cnt] ;
    int bcheck=0 ;

    if (true) {
        // 랜덤값 생성
        for (int i=0; i<cnt; i++) {
            a[i]=  std::rand()%1000 ;
        }
    } else {
        // 이미 정렬된 상태값 생성
        for (int i=0; i<cnt; i++) {
            a[i]=  i ;
        }
    }

    // print_array(a,cnt) ; // 정렬 전.
    // (*pfunc)(a, cnt) ;   // 이렇게 해도 가능.
    pfunc(a,cnt) ;
    // print_array(a,cnt) ; // 정렬 후.
    bcheck = check_asc(a,cnt) ;

    delete[] a ;
    return bcheck ;
}


uint64_t getmilli() {
    using namespace std::chrono;
    return duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count() ;
}


int main() {

    std::cout << "sort test" << std::endl ;
    std::srand(std::time(NULL)) ;

    // int a[] = {3,8,5,1,9,4,2,6,7, 10} ;
    // int a[10] ;
    // for (int i=0; i<10; i++) {
    //     a[i] = std::rand()%50 ;
    // }
    // int n = sizeof(a)/sizeof(int) ;
    // print_array(a, n) ;
    // quick_sort(a, n) ;
    // merge_sort(a, n) ;
    // bubble_sort(a, n) ;
    // print_array(a, n) ;
    // return 0 ;

    int bcheck=0 ;
    int okcnt = 0, failcnt=0 ;
    uint64_t ts, te ;


    const char *pfunc_name[] = { "selection_sort",  "bubble_sort", "insertion_sort", "merge_sort",  "quick_sort",   NULL} ;
    void(*pfunc[])(int[], int) = { selection_sort, bubble_sort, insertion_sort, merge_sort, quick_sort,  NULL };
    int pfunc_cnt = sizeof(pfunc) / sizeof(pfunc[0]) ;

    for (int kk=0; kk<pfunc_cnt; kk++) {
        okcnt=0 ;
        failcnt=0 ;
        if (pfunc[kk]==NULL)
            break ;
        std::cout << "ALG=" << pfunc_name[kk] << std::endl ;

        ts = getmilli() ;
        for (int i=0; i<5000; i++) {
            bcheck = test_alg(pfunc[kk]) ;
            if (bcheck) {
                okcnt++ ;
                // std::cout << "OK" << std::endl ;
            } else {
                failcnt++ ;
                // std::cout << "FAIL" << std::endl ;
            }
        }
        te = getmilli() ;
        std::cout << "OK cnt=" << okcnt << "  FAIL cnt=" << failcnt << std::endl ;
        std::cout << te-ts << " ms" << std::endl ;
    }

    return 0 ;
}

만약 이미 정렬된 상태를 다시 정렬을 시키면?

qsort test
ALG=selection_sort
OK cnt=5000 FAIL cnt=0
1507 ms
ALG=bubble_sort
OK cnt=5000 FAIL cnt=0
16 ms
ALG=insertion_sort
OK cnt=5000 FAIL cnt=0
1439 ms
ALG=merge_sort
OK cnt=5000 FAIL cnt=0
164 ms
ALG=quick_sort
OK cnt=5000 FAIL cnt=0
1329 ms
  • 위 경우, bubble, merge가 성능이 좋았다. bubble이 좋았던 이유는 바로 이미 정렬되어 있는지를 처음 루프에서 알아낼 수 있기 때문에 그 조건으로 루프 탈출이 가능하기 때문이다.
  • 위 조건은 quick 소트 입장에서는 worst case이다. 먼저 이미 정렬되었는지 검사하는 루틴을 추가한다면 이런 경우를 방지할 수 있을 것이다.

Author: crazyj7@gmail.com

'Develop > C&CPP' 카테고리의 다른 글

[알고리즘] 정렬5: quick sort  (0) 2019.12.15
[알고리즘] 정렬4: merge sort  (0) 2019.12.15
[알고리즘] 정렬3: Insertion sort  (0) 2019.12.15
[알고리즘] 정렬2: bubble sort  (0) 2019.12.13
[알고리즘] 정렬1: selection sort  (0) 2019.12.13
반응형

Quick Sort

시간복잡도 : O(Nlog_2N) 이지만 최악의 경우는 마찬가지로 O(N^2)

퀵 정렬은 아주 빠른 속도를 나타낼뿐만 아니라 원리도 간단해서 많은 응용 분야에서 사용되고 있다.
퀵 정렬은 연속적인 분할에 의해서 정렬한다.

축(Pivot)값을 중심으로 왼쪽은 이 축값보다 작은 값으로 오른쪽은 모두 이 축값보다 큰 값을 배열시키는 것이다.
이렇게 하여 축값의 왼쪽과 오른쪽 부분을 각각 또다시 분할하고 하는 과정을 분할의 크기가 1이 될 때까지 반복하면 전체적으로는 정렬이 완료된다.

-리스트 마지막 인덱스의 값을 pivot으로 정함.
피봇을 뺀 나머지 배열에서 가장 양쪽 끝 인덱스를 저장. 왼쪽은 i, 오른쪽은 j (인덱스 2개 사용)
i는 오른쪽으로 가면서 해당 값이 pivot보다 작아야 한다. 큰 것이 발견되면 멈춤.
j는 왼쪽으로 가면서 해당 값이 pivot보다 커야 한다. 작은 것이 발견되면 멈춤.
i위치와 j위치의 값을 바꿈.
그런데, i, j가 만나게 되는 경우. (i>=j) 해당 위치가 pivot이 들어갈 자리.
i위치의 값과 pivot 값 swap한다.
pivot을 기준으로 왼쪽, 오른쪽으로 리스트를 나누어, 각각 처음부터 다시 소팅한다.

알고리즘

  1. 만약 n>1 이면
  2. 1 N 크기의 a 배열을 분할 하여 축값의 위치를 mid로 넘긴다.
  3. 2 퀵 정렬 알고리즘(a, mid)
  4. 3 퀵 정렬 알고리즘 (a+mid-1, N-mid-1)
void quick_sort(int a[], int n){
    int v, t;
    int i, j;
    if (n > 1){
        v = a[n - 1]; // v=축값. 마지막 노드값을 사용
        i = -1; 
        j = n - 1; 
        while (1){ // 분할
            while (a[++i] < v); // 왼쪽에서부터 축값보다 크거나 같은 값이 있는지?
            while (a[--j] > v){ // 오른쪽에서부터 축값보다 작거나 같은 값이 있는지? -> 가장작은값이 pivot이면?? index out range 위험 아래 체크 필요.
                if (j==0) break ;
            }
            if (i >= j) break; // i와 j의 위치가 뒤바뀌었으면 분할 끝
            t = a[i]; // 아니면 두 값을 바꿈
            a[i] = a[j];
            a[j] = t;
        }
        t = a[i]; // 축 값과 축값의 위치에 있는 값을 바꿈
        a[i] = a[n - 1];
        a[n - 1] = t;
        quick_sort(a, i); // 왼쪽 구간 퀵정렬
        quick_sort(a + i + 1, n - i - 1); // 오른쪽 구간 퀵정렬
    }
}

퀵 정렬은 난수 배열이나 반쯤 정렬된 배열에 대해서는 빠른 속도를 보이지만 이미 정렬된 배열이나 역순 배열에 대해서는 성능이 엄청나게 떨어진다.
퀵 정렬의 이상적인 경우는 분할이 정확하게 구간을 양분하는 것이다. 이 경우 재귀의 깊이는 logN이 되며 가장 빠른 속도를 나타낸다.
이렇게 퀵 정렬은 분할할 축값이 어느 정도 그 구간의 중앙을 나누느냐에 성능이 좌우된다.
재귀 호출로 인해서 내부 스택이 사용된다. 따라서 퀵 정렬이 속도가 빠른 반면에 메모리의 희생이 있다.
N이 커지면 커질수록 스택의 크기가 커지며 스택 오버플로우가 발생할 수 있다.

성능개선: 이미 정렬된 상태인지를 체크하는 것을 추가한다.

ex)

0,1,2,3,4,5,6,7,8 : 인덱스
5,3,8,4,9,1,6,2,7 : 값

qsort

  1. 피봇을 마지막 번째 노드 값으로 정하자. (보통 첫번째나 마지막번째로 정함.)
    남은 데이터의 양쪽 끝에서 가운데로 스캔해 나가면서 왼쪽에서는 피봇보다 큰 값을 찾으면 멈추고, 오른쪽에서는 피봇보다 작은 값을 찾으면 멈추고, 둘을 교환한다.

pivot=7, scan index=0, 7 start find over or below than pivot.
stop index=2(큰값발견),7(작은값발견) value 8>7>2 ;swap
0,1,2,3,4,5,6,7,8
5,3,2,4,9,1,6,8,7

pivot=7, stop index=4, 6 value 9>7>6 ;swap
0,1,2,3,4,5,6,7,8
5,3,2,4,6,1,9,8,7

pivot=7, stop index=6> 5 ; cross
stop. index=5, 4 인덱스가 교차되면 멈추고, 피봇(오른쪽 파트위치로 인덱스를 잡았으므로 right part에 속해야 한다. left part의 마지막 스캔 위치 인덱스6, 피봇보다 큰 수를 처음 발견한 지점에 들어가야 한다. )과 left last index(6)와 swap
0,1,2,3,4,5,6,7,8
5,3,2,4,6,1,7,8,9
피봇을 기준으로 좌우 분리가 끝남.
피봇 위치가 바뀌면 한 싸이클이 끝난 것임. (pivot 7을 기준으로 좌(low)우(high) 분리 완료)

  1. 좌우 파트("532461", "89")에 대해 각각 qsort 수행. 파트의 원소 개수가 1이면 스킵.
    2-2.
    0 1 (실제 인덱스는 7,8)
    8 9
    pivot=9, stop=N, 0. 왼쪽값을 찾지 못함. 즉, 모두 작다. 이미 피봇은 가장 우측이라 변경할 필요가 없다. 리턴

2-1.
0,1,2,3,4,5
5,3,2,4,6,1
new pivot=1, start 0, 4

pivot=1, stop 0, N; 왼쪽에서는 큰 값을 찾았는데, 오른쪽에서는 작은 값을 찾지 못함. left part last index=0과 swap
0,1,2,3,4,5
1,3,2,4,6,5

  1. 좌우 파트 수행 : "", "32465". 왼쪽 파트는 1개이하라 스킵. 오른쪽 파트 수행
    0 1 2 3 4 (실제 인덱스 : 1,2,3,4,5)
    3 2 4 6 5
    new pivot=5 ; start 0, 3
    pivot=5, stop=3>2.cross! last left index=3. change
    0 1 2 3 4 (실제 인덱스 : 1,2,3,4,5)
    3 2 4 5 6

  2. 좌우 파트 수행 : "3 2 4", "6" ; 오른쪽 파트는 스킵.
    0 1 2 (실제 인덱스 : 1,2,3)
    3 2 4
    pivot=4, stop=N, 1 no swap. break

  3. 좌우 파트 수행 "32", ""
    0 1 (실제 인덱스 : 1,2)
    3 2
    pivot=2, start=0,0
    pivot=2, stop=0, N ; swap with index 0
    0 1 (실제 인덱스 : 1,2)

2 3
6. 좌우 분할 ; "", "3" ; 모두 리턴

0,1,2,3,4,5,6,7,8 : 인덱스
1,2,3,4,5,6,7,8,9 : 값

vector<double>  QuickSort(vector<double>& vec1){
  double i =  0;
  double j = vec1.size()-2;
  double tmp;
  int pivotindex = vec1.size()-1  ;
  double pivot = vec1[pivotindex];
  if  ( vec1.size()<=1  )
    return vec1 ;
  cout <<  "QuickSort: ";
  printvector(vec1)  ;

  while  (i <= j)  {
    while  (vec1[i]  < pivot)
      i++;
    while  (vec1[j]  > pivot)
      j--;
    if  (i <= j)  {
      tmp = vec1[i];
      vec1[i]  = vec1[j];
     vec1[j]  = tmp;
     i++;
     j--;
    }
  }

  // pivot change
  vec1[pivotindex]  = vec1[i]  ;
  vec1[i]=pivot ;
  pivotindex=i ;

  cout <<  "pivotting: ";
  printvector(vec1)  ;

  if  (vec1.size()<=2  )
       return vec1 ;

    // partition
    vector<double> left_vec, right_vec ;
    vector<double>::iterator pivotiter = vec1.begin()+pivotindex ;
    copy(vec1.begin(), pivotiter, back_inserter(left_vec))  ;
    copy(pivotiter+1, vec1.end(), back_inserter(right_vec))  ;

    cout <<  "left: ";
    printvector(left_vec)  ;
    cout <<  "right: ";
    printvector(right_vec)  ;

    if  (left_vec.size()>0  )  {
        QuickSort(left_vec);
        copy(left_vec.begin(), left_vec.end(), vec1.begin())  ;
    }

    if  (right_vec.size()>0  )  {
        QuickSort(right_vec);
        copy(right_vec.begin(), right_vec.end(), pivotiter+1)  ;
    }
    return vec1;
}
반응형

Merge Sort

bottom-up으로 하위 그룹의 정렬부터 상위 그룹 정렬로 올라가며 정렬한다.
머지 정렬은 bottom-up으로 정렬 그룹이 2개 이하일 때를 종료 조건으로 하고, 하위 그룹의 머지 정렬이 끝나면 상위 그룹에서 두 개의 그룹을 합치는 것으로 동작한다.
작은 그룹들이 밑에서부터 미리 정렬되어 있기 때문에 정렬된 그룹간의 정렬이 빠르다는 것을 이용한다. linear하게 비교하면서 merge만 하면 바로 정렬된 상위 그룹을 만들 수 있다.
재귀함수를 사용하여 구현한다.

ex)
5 1 9 3 4 6 2 : 반씩 나눈다.
5 1 9 || 3 4 6 2 : 반
5 | 1 9 || 3 4 | 6 2 : 반
5 | 1 9 || 3 4 | 2 6 : 두 개 이하가 되면 정렬하고 리턴함.
1 5 9 || 2 3 4 6 : 두 그룹을 정렬한다. 리턴한다.
1 2 3 4 5 6 9 : 두 그룹을 정렬한다. 리턴한다.
완료

void mergeSort(int arr[], int size) {
 if (size > 2) {
  // 왼쪽 반, 오른쪽 반을 나누어 반복.
  mergeSort(arr, size / 2);
  mergeSort(arr + size / 2, size - size / 2);

  // 왼쪽, 오른쪽 반이 각각 정렬된 상태임. merge한다.
  int leftCursor = 0;
  int rightCursor = size / 2;
  int buff[50];
  int buffCursor = 0;
  // 두 그룹을 각각 스캐닝하면서 순서대로 기록. (어느 한쪽이 끝날때까지)
  while (leftCursor < size / 2 && rightCursor < size) {
    if (arr[leftCursor] < arr[rightCursor]) {
    buff[buffCursor++] = arr[leftCursor++];
   } else {
     buff[buffCursor++] = arr[rightCursor++];
   }
  }
  // 남은 한 그룹의 데이터를 append 
  for (int i = leftCursor ; i < size / 2 ; i++)
    buff[buffCursor++] = arr[i];
  for (int j = rightCursor ; j < size ; j++) 
    buff[buffCursor++] = arr[j];
  memcpy(arr, buff, size * sizeof(int));
 }else { // 원소 개수가 2개 이하인 경우, 비교하여 정렬한다.
  if (arr[0] > arr[1])
    swap(arr[0], arr[1]);
 }
}
반응형

Insertion Sort

: 삽입 정렬은 왼쪽 부터 항을 1개씩 정렬해나가면서 하나씩 항목을 추가해가면서 뒤에서부터 삽입해서 정렬되는 위치까지 삽입시키는 알고리즘이다.

--> 루프0
index 1을 왼쪽으로 한 칸씩 가면서 들어갈 자리를 찾는다.
index 0과 비교하여 작으면 위치를 바꾼다.
index 1까지 정렬 완료
--> 루프 1
index 2를 왼쪽으로 한 칸씩 가면서 들어갈 자리를 찾는다.
index 1과 비교 : 작으면 위치를 swap하고 계속 진행. 크면 break(루프 탈출)
index 0과 비교 : 작으면 위치를 swap하고 계속 진행. 크면 break(루프 탈출)
index 2까지 정렬 완료
--> 루프2
index 3를 왼쪽으로 한 칸씩 가면서 들어갈 자리를 찾는다.
index 2과 비교 : 작으면 위치를 swap하고 계속 진행. 크면 break(루프 탈출)
index 1과 비교 : 작으면 위치를 swap하고 계속 진행. 크면 break(루프 탈출)
index 0과 비교 : 작으면 위치를 swap하고 계속 진행. 크면 break(루프 탈출)
index 3까지 정렬 완료
...

ex)
loop i=1, j=0 ; index 1의 위치 찾기.
5 1 9 3 4 : index 1을 왼쪽을 비교하여 작으면 swap 크면 break 한다. swap
1 5 9 3 4 : 결과

loop i=2, j=1~0 ; index2의 위치 찾기
1 5 9 3 4 : index 2를 왼쪽을 비교하여 작으면 swap 크면 break 한다. break

loop i=3, j=2~0 ; index 3의 위치 찾기
1 5 9 3 4 : index 3를 왼쪽을 비교하여 작으면 swap 크면 break 한다. swap
1 5 3 9 4 : index 2를 왼쪽을 비교하여 작으면 swap 크면 break 한다. swap
1 3 5 9 4 : index 1를 왼쪽을 비교하여 작으면 swap 크면 break 한다. break
1 3 5 9 4 : 결과

loop i=4, j=3~0 ; index 4의 위치찾기
1 3 5 9 4 : index 4를 왼쪽을 비교하여 작으면 swap 크면 break 한다. swap
1 3 5 4 9 : index 3를 왼쪽을 비교하여 작으면 swap 크면 break 한다. swap
1 3 4 5 9 : index 2를 왼쪽을 비교하여 작으면 swap 크면 break 한다. break
1 3 4 5 9 : 결과

void insertion_sort(int arr[], int size) {
    for(int i = 1 ; i <= size - 1 ; i++) 
        for (int j = i-1 ; j >= 0 ; j--) 
            if(arr[j] > arr[j+1]) {
                // swap(arr[j], arr[j+1]);
                int t = arr[j] ;
                arr[j] = arr[j+1] ;
                arr[j+1] = t ;
            } 
            else
                continue;
} 

'Develop > C&CPP' 카테고리의 다른 글

[알고리즘] 정렬5: quick sort  (0) 2019.12.15
[알고리즘] 정렬4: merge sort  (0) 2019.12.15
[알고리즘] 정렬2: bubble sort  (0) 2019.12.13
[알고리즘] 정렬1: selection sort  (0) 2019.12.13
ASN.1c  (0) 2019.08.01
반응형

Bubble

복잡도: O(n^2)
이중 루프를 돌면서 두 개씩 쌍으로(옆에 있는 것끼리) 비교하여 순서를 맞춘다. (swap) 오른쪽으로 한 칸씩 이동하면서 반복한다.
위 작업을 마치면 마지막에는 가장 큰 값이 들어가게 된다.
다시 전 작업을 반복하는데, 끝 지점을 한 칸 왼쪽으로 이동시킨다. (가장 큰 값은 찾았으므로)

-> 루프0
index 0, 1 비교. (swap)
index 1, 2 비교.
...
index n-2, n-1(마지막인덱스) 비교.
결과 ; n-1인덱스에 가장 큰 값! ; 전체 n-1 번 비교

-> 루프1
index 0, 1 비교.
index 1, 2 비교.
...
index n-3, n-2비교. (마지막 인덱스를 1씩 감소)
결국 n-2에 다음으로 큰 값! ; 전체 n-2번 비교.
...

ex)
loop i=0. j=1-4
5 1 9 3 4 : 인덱스 0, 1을 비교하여 정렬 (before) swap
1 5 9 3 4 : 인덱스 1, 2를 비교하여 정렬
1 5 9 3 4 : 인덱스 2, 3를 비교하여 정렬 swap
1 5 3 9 4 : 인덱스 3, 4를 비교하여 정렬 swap
1 5 3 4 9 : 결과
마지막 인덱스에 가장 큰 값 찾기 완료.
loop i=1. j=1-3
1 5 3 4 9 : 인덱스 0, 1을 비교하여 정렬 (before)
1 5 3 4 9 : 인덱스 1, 2를 비교하여 정렬 swap
1 3 5 4 9 : 인덱스 2, 3를 비교하여 정렬 swap
1 3 4 5 9 : 결과
마지막 전 인덱스에 2번째 큰 값 찾기 완료.
...
(왼쪽 시작부터 버블 모양으로 비교를 한다. 이후 버블을 한 개씩 감소. )

void bubble_sort(int a[], int n){
    int i, j, t;
    for (i=0; i<n-1; i++) {
        for (j=1; j<n-i; j++){
           if (a[j-1] > a[j]){
            t = a[j-1];
            a[j-1] = a[j];
            a[j] = t;
            }
        }
    }
}
  • 성능개선안: swap 교환 횟수가 0이 되면 더 이상 할필요없으므로 리턴하면 된다.

void bubble_sort(int a[], int n){
    int i, j, t;
    for (i = 0; i < n - 1; i++) {
        int bchanged=0 ;
        for (j = 1; j < n - i; j++) // j=(1,10)
        {
            if (a[j - 1] > a[j]){
                t = a[j - 1];
                a[j - 1] = a[j];
                a[j] = t;
                bchanged=1 ;
            }
        }
        if (bchanged==0) break ;
    }
}

'Develop > C&CPP' 카테고리의 다른 글

[알고리즘] 정렬4: merge sort  (0) 2019.12.15
[알고리즘] 정렬3: Insertion sort  (0) 2019.12.15
[알고리즘] 정렬1: selection sort  (0) 2019.12.13
ASN.1c  (0) 2019.08.01
벡터 구조체 필드 기준으로 최대값 최소값 찾기  (0) 2019.05.31
반응형

정렬 알고리즘을 구현해 보고, 성능을 비교해 보자.
대략적으로 성능이 좋지 않은 순서대로 해 보겠다.

Selection Sort

복잡도: O(n^2)

아무 생각없이 바로 만들 수 있는 sorting 코드.
이중 루프를 돌면서 왼쪽부터 가장 작은 값을 찾아 넣음. (크면 바꾼다. swap 이것을 계속 반복하면 가장 작은 값이 index 0에 들어가게 된다.)
(일반적으로 성능 상관없이 정렬 작업할 때)

루프가 하나 끝나면 index 0가 최소값이 되고, 다음 루프로 index 1에 그 다음 작은 값이 들어가는 것을 반복한다.

-> 루프 0
index 0자리를 선택하여 나머지 전체에서 가장 작은 값을 넣고, (n-1번 비교)
index 0를 선택한 것을 index 1 부터 n-1까지 비교하면서 작은값이 있으면 swap. ; n-1인덱스에 가장 큰값이 이동됨.
-> 루프 1
index 1을 선택하여 그 다음 작은 값을 찾아 넣고. (n-2번 비교)
index 1을 선택한 것을 index 2부터 n-1까지 비교. ; n-2인덱스에 그 다음으로 큰 값이 이동됨.
...

ex)
loop i=0. j=1-4
5 1 9 3 4 : 인덱스 0, 1을 비교하여 대소위치 변경. swap
1 5 9 3 4 : 인덱스 0, 2를 비교하여 대소위치 변경
1 5 9 3 4 : 인덱스 0, 3를 비교하여 대소위치 변경
1 5 9 3 4 : 인덱스 0, 4를 비교하여 대소위치 변경
1 5 9 3 4 : 결과
인덱스0에 가장 작은 값 찾기 완료.
loop i=1. j=2-4
1 5 9 3 4 : 인덱스 1, 2을 비교하여 대소위치 변경
1 5 9 3 4 : 인덱스 1, 3를 비교하여 대소위치 변경 swap
1 3 9 5 4 : 인덱스 1, 4를 비교하여 대소위치 변경
1 3 9 5 4 : 결과
인덱스1에 2번째 작은 값 찾기 완료.
...
(왼쪽부터 위치를 하나 찍고 이후 나머지 것들과 모두 비교. 오른쪽으로 한 칸씩 이동)


void selection_sort(int arr[], int size) {
    for(int i = 0 ; i < size - 1 ; i++) 
        for (int j = i + 1 ; j < size ; j++) 
            if(arr[i] > arr[j]) {
                // swap(arr[i], arr[j]);
                int t = arr[i] ;
                arr[i] = arr[j] ;
                arr[j] = t ;
            }
}

'Develop > C&CPP' 카테고리의 다른 글

[알고리즘] 정렬3: Insertion sort  (0) 2019.12.15
[알고리즘] 정렬2: bubble sort  (0) 2019.12.13
ASN.1c  (0) 2019.08.01
벡터 구조체 필드 기준으로 최대값 최소값 찾기  (0) 2019.05.31
smart pointer 스마트포인터?  (0) 2018.07.19
반응형
ASN1C

ASN.1 c/cpp code

What is ASN.1

Abstract Syntax Notation One 이란 뜻.
ITU에서 네트웍 데이터 교환을 정의한 프로토콜로 사용하는 표준화 포맷임. (X.680, X.681, X.682, X.683, X.690, X.691, X.692, X.693 등에 설명)
이 기종 장치들 간의 데이터 교환시 표준화가 필요하다. (byte ordering 등 여러가지 문제)
여러가지 ISO 표준 문서에서 사용하는 자료 구조 형태를 보면 ASN.1 문법으로 사용한다.
ASN.1 에서는 자료 구조들을 정의하는데, 길이라던지 범위라던지 등의 제약 조건 등도 포함된다. 자료 구조를 정의하는 문법의 문서 형태라서 소프트웨어 구현시에는 자체로 사용할 수 없고, ASN.1을 인코딩하여 사용한다. 이런 인코딩 들을 하기 위한 작업을 개발 언어별로 소스를 자동으로 만들어 주는 역할을 하는 것이 ASN.1 컴파일러 이다.

BER는 ASN.1의 기본 인코딩 규칙을 나타낸다. ASN.1 데이터를 인코딩하는 다른 방법으로는 DER, CER, PER 등이 있다.

ASN.1 compiler는 ASN.1 문법으로 작성된 문서를 읽어 원하는 언어의 소스 파일로 만들어 주는 역할을 한다. (asn1c는 c 소스 코드를 생성한다.)

compiler download

Download asn1c(Compiler) source code and build.
버전에 따라 생성되는 코드가 다를 수 있다. 가장 최신 버전을 사용하는 것이 좋다. 상용으로 컴파일러를 파는 곳도 많다.

# wget https://lionet.info/soft/asn1c-0.9.28.tar.gz
# tar xvfz asn1c-0.9.28.tar.gz
# cd asn1c-0.9.28
./configure
./make
./make install
# asn1c -h
ASN.1 Compiler, v0.9.28

# Installation path may be /usr/local/bin.

Syntax

문법은 ASN.1 Syntax 검색하여 자세한 것은 찾아보고, 여기서는 간단한 예제 맛 보기.

Order ::= SEQUENCE {
  header Order-header,
  items SEQUENCE OF Order-line
  }
Order-header ::= SEQUENCE {
  number Order-number,
  date	Date,
  client Client,
  payment Payment-method
}

Certificate ::= SEQUENCE {
  tbsCertificate TBSCertificate,
  signatureAlgorithm AlgorithmIdentifier,
  signatureValue BIT STRING
}

TBSCertificate ::= SEQUENCE {
  version 	[0] EXPLICIT Version DEFAULT v1,
  serialNumber CertificateSerialNumber,
  signature	AlgorithmIdentifier,
  issuer	Name,
  validity	Validity,
  ...
}

Test ASN1c

asn 파일을 만들었으면 asn1c로 c코드를 생성한다.
Makefile을 만들어 사용하기.

--build.sh
#!/bin/sh
make -f Makefile.asn

--Makefile.asn
CC=cc
CPP=g++

.SUFFIXES=.c .o
.SUFFIXES=.cpp .o

all:
	asn1c format_iso.asn
	-rm converter-sample.c converter-sample.o
	$(CC) -c -fPIC -g *.c -I.
	$(CPP) -c -fPIC -g *.cpp -I.
#	$(CPP) -o a.out *.o

--Makefile.api
...
all:
	$(CC) -c -fPIC *.c -I.
	$(CPP) -c -fPIC asnapi.cpp -I . -D_LITTLE_ENDIAN_

test:
	$(CPP) -c -fPIC asnapitest.cpp -I.
	$(CPP) -o a.out *.o 
		

위에서 converter-sample.c를 참고하여 인코딩을 하는 것을 쉽게 따라해 볼 수 있다.
실제 사용하지는 않을 테니까 rm으로 빌드시 삭제하였다.

ASN api wrapper

ASN1 문서가 asn1c에 의해 컴파일되면 소스 코드들이 잔뜩 생긴다. 정의한 자료구조마다 헤더파일과 소스 파일이 각각 생성되어 생성된 소스 파일들이 너무 많다.
자료구조들을 사용하기 위해서는 wrapper하여 변환하는 함수를 만드는 것이 좋다.
ASN1 문서에 자료 구조 정의한 이름으로 소스 파일들이 생성되니, 최상위에 정의한 자료구조 명으로된 헤더 파일을 include하여 작성한다. 타입은 자료구조명에 _t가 붙어서 자동으로 생성된다.

wrapper example)

-- asnapi.h
#include "myData.h"
...
myData_t* mynotation2iso(string mynote) ;
myData_t* xer2iso(string mynote) ;
string iso2xer(myData_t *mydata) ;
myData_t* bin2iso(char *bin) ;
char* iso2bin(myData_t *mydata, OUT int *retbufsize) ;
...

--asnapi.cpp
위 wrapper 함수들을 구현

ASN 자료구조 생성

mynotation2iso():
mydata_t *mydata ;
// 메모리 초기화
mydata = (mydata_t*) calloc(1, sizeof *mydata) ;
// 메모리 해제 함수 연결. 메모리 해제시 내부의 리스트 엘리먼트들에 할당한 
// 메모리들이 있을 때는 수동으로 메모리 해제 함수를 만든다.
mydata->bodies.list.free = free_body ;

// 스트링 할당. 4바이트. 마지막 null 포함하여 octet string자료 구조내에 저장. 
// 내부에서 contents 메모리 할당이 발생함. free 필요.  
OCTET_STRING_fromBuf(&mydata->header.formatId, "abc", 4) ;
mydata->header.number = 1 ;

Body_t* body=NULL;
body = (Body_t*) calloc(1, sizeof *body) ;
body->reprenentation.qualityRecord.qualityBlock.list.free=free_qualityBlock ;

// 버퍼에서 스트링을 읽어 자료구조 octet 스트링에 복사. 일반 바이너리값 
// 바이트 어레이도 이렇게 저장 가능.  
OCTET_STRING_fromBuf( &body->representation.captureDateTime, 
	(const char*)&utc, 9) ;
QualityBlock_t *qb=NULL ;
qb = (QualityBlock_t*) calloc(1, sizeof *qb) ;
..
// 리스트 구조(sequence of)는 이렇게 추가한다.
asn_sequence_add(&body->representation.qualityRecord.qualityBlocks, qb) ;
...
int zero=0 ;

// new_fromBuf로 하면 octet 스트링 자료구조 메모리가 할당되어 반환된다. 
body->representationBody.extendedData = 
	OCTET_STRING_new_fromBuf(&asn_DEF_OCTET_STRING, (const char*)&zero, 2) ;
asn_sequence_add(&mydata->bodies, body) ;

// XER 포맷으로 변환하여 스트링 출력
xer_fprintf(stdout, &asn_DEF_mydata, mydata) ;
return mydata ;

free_body(Body_t *b):
free_scd(b->representation.channelDescription.descriptions.x) ;

// 리스트 해제. 내부에 free 지정 콜백이 돌아가면서 내부의 메모리 해제.
asn_sequence_empty(&b->representation.qualityRecord.qualityBlocks)

// 내부 버퍼 메모리 해제 및 OCTET_STRING 구조체 메모리도 해제.
// extendedData 자체가 OCTET_STRING* 타입. 별도 할당(new_fromBuf)한 
// 메모리 해제가 필요하여 마지막 플래그값으로 0이로 설정. 
OCTET_STRING_free(&asn_DEF_OCTET_STRINTG, b->representationBody.extendedData,0) ;

// 마지막플래그는 contents_only로 1이면 contents만 해제한다. 
// octet_string 구조체(컨테이너) 메모리는 해제하지 않음.
// captureDateTime 자체가 OCTET_STRING 타입으로 별도 할당한 메모리가 아니라
// 상위 구조체 할당시 잡힌 영역임. 여기서 해제 불필요.
OCTET_STRING_free(&asn_DEF_OCTET_STRINTG, &b->representation.captureDateTime,1) ;
free(b);

set_nodefree(mydata_t *mydata):
mydata->bodies.list.free=free_body ;
asn_anonymous_sequence_ *ss = _A_SEQUENCE_FROM_VOID( &mydata->bodies) ;
for (int j=0; j<ss->count; j++) {
	Body_t *body = (Body_t*)ss->array[j] ;
	body->representation.qualityRecord.qualityBlocks.list.free = 
		free_qualityBlock ;
}

위의 예제를 보면 복잡해 보인다. 실제 생성된 소스코드들을 분석해야지만 사용할 수 가 있다. 특히 리스트 사용이나 스트링 처리, 메모리 관리가 처음에 어려울 수 있다. 위의 예제를 참고하면 스트링이나 리스트 사용시 도움이 될 것이다. (주의: asn1c는 버전에 따라 Integer_t에 int 값 할당하는 것도 에러가 날 수 있다.)


Example

original page http://lionet.info/asn1c/examples.html

  • MyModule.asn1
MyModule DEFINITIONS ::=
BEGIN

MyTypes ::= SEQUENCE {
    myObjectId   OBJECT IDENTIFIER,
    mySeqOf      SEQUENCE OF MyInt,
    myBitString  BIT STRING {
                        muxToken(0), 
                        modemToken(1)
                 }
}

MyInt ::= INTEGER (0..65535)

END
  • asn1c MyModule.asn1

  • create structure files.

  • my-program.c

#include <stdio.h>	/* for stdout */
#include <stdlib.h>	/* for malloc() */
#include <assert.h>	/* for run-time control */
#include "MyTypes.h"	/* Include MyTypes definition */

int main() {
	/* Define an OBJECT IDENTIFIER value */
	int oid[] = { 1, 3, 6, 1, 4, 1, 9363, 1, 5, 0 }; /* or whatever */

	/* Declare a pointer to a new instance of MyTypes type */
	MyTypes_t *myType;
	/* Declare a pointer to a MyInt type */
	MyInt_t *myInt;

	/* Temporary return value */
	int ret;

	/* Allocate an instance of MyTypes */
	myType = calloc(1, sizeof *myType);
	assert(myType);	/* Assume infinite memory */

	/*
	 * Fill in myObjectId
	 */
	ret = OBJECT_IDENTIFIER_set_arcs(&myType->myObjectId,
			oid, sizeof(oid[0]), sizeof(oid) / sizeof(oid[0]));
	assert(ret == 0);

	/*
	 * Fill in mySeqOf with a couple of integers.
	 */

	/* Prepare a certain INTEGER */
	myInt = calloc(1, sizeof *myInt);
	assert(myInt);
	*myInt = 123;	/* Set integer value */

	/* Fill in mySeqOf with the prepared INTEGER */
	ret = ASN_SEQUENCE_ADD(&myType->mySeqOf, myInt);
	assert(ret == 0);

	/* Prepare another integer */
	myInt = calloc(1, sizeof *myInt);
	assert(myInt);
	*myInt = 111222333;	/* Set integer value */

	/* Append another INTEGER into mySeqOf */
	ret = ASN_SEQUENCE_ADD(&myType->mySeqOf, myInt);
	assert(ret == 0);

	/*
	 * Fill in myBitString
	 */

	/* Allocate some space for bitmask */
	myType->myBitString.buf = calloc(1, 1);
	assert(myType->myBitString.buf);
	myType->myBitString.size = 1;	/* 1 byte */

	/* Set the value of muxToken */
	myType->myBitString.buf[0] |= 1 << (7 - myBitString_muxToken);

	/* Also set the value of modemToken */
	myType->myBitString.buf[0] |= 1 << (7 - myBitString_modemToken);

	/* Trim unused bits (optional) */
	myType->myBitString.bits_unused = 6;

	/*
	 * Print the resulting structure as XER (XML)
	 */
	xer_fprint(stdout, &asn_DEF_MyTypes, myType);

	return 0;
}
  • $ cc -o a.out -I. *.c
  • ./a.out
<MyTypes>
    <myObjectId>1.3.6.1.4.1.9363.1.5.0</myObjectId>
    <mySeqOf>
        <MyInt>123</MyInt>
        <MyInt>111222333</MyInt>
    </mySeqOf>
    <myBitString>11</myBitString>
</MyTypes>
  • my-program2.c
#include <stdio.h>	/* for stdout */
#include <stdlib.h>	/* for malloc() */
#include <assert.h>	/* for run-time control */
#include "MyTypes.h"	/* Include MyTypes definition */

int main(int argc, char *argv[]) {
	char buf[1024];	/* Hope, sufficiently large buffer */
	MyTypes_t *myType = 0;
	asn_dec_rval_t rval;
	char *filename;
	size_t size;
	FILE *f;

	/*
	 * Target variables.
	 */
	int *oid_array;	/* holds myObjectId */
	int oid_size;
	int *int_array;	/* holds mySeqOf */
	int int_size;
	int muxToken_set;	/* holds single bit */
	int modemToken_set;	/* holds single bit */

	/*
	 * Read in the input file.
	 */
	assert(argc == 2);
	filename = argv[1];
	f = fopen(filename, "r");
	assert(f);
	size = fread(buf, 1, sizeof buf, f);
	if(size == 0 || size == sizeof buf) {
		fprintf(stderr, "%s: Too large input\n", filename);
		exit(1);
	}

	/*
	 * Decode the XER buffer.
	 */
	rval = xer_decode(0, &asn_DEF_MyTypes, &myType, buf, size);
	assert(rval.code == RC_OK);

	/*
	 * Convert the OBJECT IDENTIFIER into oid_array/oid_size pair.
	 */
	/* Figure out the number of arcs inside OBJECT IDENTIFIER */
	oid_size = OBJECT_IDENTIFIER_get_arcs(&myType->myObjectId,
			0, sizeof(oid_array[0]), 0);
	assert(oid_size >= 0);
	/* Create the array of arcs and fill it in */
	oid_array = malloc(oid_size * sizeof(oid_array[0]));
	assert(oid_array);
	(void)OBJECT_IDENTIFIER_get_arcs(&myType->myObjectId,
			oid_array, sizeof(oid_array[0]), oid_size);

	/*
	 * Convert the sequence of integers into array of integers.
	 */
	int_size = myType->mySeqOf.list.count;
	int_array = malloc(int_size * sizeof(int_array[0]));
	assert(int_array);
	for(int_size = 0; int_size < myType->mySeqOf.list.count; int_size++)
		int_array[int_size] = *myType->mySeqOf.list.array[int_size];

	if(myType->myBitString.buf) {
		muxToken_set   = myType->myBitString.buf[0]
					& (1 << (7 - myBitString_muxToken));
		modemToken_set = myType->myBitString.buf[0]
					& (1 << (7 - myBitString_modemToken));
	} else {
		muxToken_set = modemToken_set = 0;	/* Nothing is set */
	}

	return 0;
}

Authors
crazyj7@gmail.com
Written with StackEdit.

반응형

벡터 구조체 필드 기준으로 최대값 최소값 찾기


#include <algorithm>

#include <vector>


typedef struct _A {

double x ;

double y ;

} A ;


bool compareA(const A& a1, const A&a2) {

// 앞에 있는 노드의 x값이 더 작게 소팅한다.

return a1.x < a2.x ;

}



std::vector<A> alist ;

A tmp ;

tmp.x = 10 ;

tmp.y = 70 ;

alist.push_back(tmp) ;

tmp.x = 5 ;

tmp.y = 60 ;

alist.push_back(tmp) ;

tmp.x = 3 ;

tmp.y = 50 ;

alist.push_back(tmp) ;

tmp.x = 8 ;

tmp.y = 80 ;

alist.push_back(tmp) ;


A mina = *std::min_element(alist.begin(), alist.end(), compareA) ;

A maxa = *std::max_element(alist.begin(), alist.end(), compareA) ;

// 이터레이터를 리턴하므로 *를 붙여 객체값을 얻어온다. 


printf("mina %lf %lf \n", mina.x, mina.y) ;

printf("maxa %lf %lf \n", maxa.x, maxa.y) ;


mina 3.000000 50.000000 
maxa 10.000000 70.000000 




반응형



+ 스마트포인터 (Smart Pointer) shared_ptr


동적으로 할당된 메모리를 관리하기 용이하다.
동작개념; 모든 것을 스택에 두어 로컬 변수로 관리하면 대부분의 메모리 문제가 해결된다는 것을 이용한다.
스택에 저장된 메모리는 스콥을 벗어나면 자동 해제되어 안전하다.

스콥을 벗어날때 참조된 메모리를 해제한다. unique_ptr 이 이러한 방식.
그러나 포인터를 복사하여 참조할 수 도 있어서 가장 마지막에 사용하는 곳에서 메모리 해제를 해야되는데, 트래킹을
하든지 해야된다. 참조가 0이되면 그 때 실제 해제되는 것이다. shared_ptr 이 이러한 방식.

C++표준 이전에는 auto_ptr을 사용했으나 폐기예정. (STL에서 사용시 문제 발생) 사용하면 안 됨.
C++11의 새로운 스마트 포인터는 unique_ptr, shared_ptr 이다.

unique_ptr ; 스코프 방식
shared_ptr ; 레퍼런스 카운팅 방식

-동적으로 메모리 할당한 객체라면 스택변수 shared_ptr에 항상 저장하라!
-동적메모리는 new든 malloc이든 할당과 동시에 shared_ptr에 저장하라!     

결국은 c++11에서는 항상 shared_ptr을 써라!!!


void leak(){
Simple *myptr = new Simple();
myptr->go();
} // 메모리 누수 발생. delete가 빠졌다.


#include <memory>
void noleak() {
shared_ptr<Simple> smartptr(new Simple() );
smartptr->go();
}     // 메모리 자동 해제.!!!!!!!!!!!


-스마트 포인터 사용 방법
shared_ptr<Simple> smartptr(new Simple() );
or
auto smartptr = make_shared<Simple>();


-주의!  shared_ptr을 한 객체에 하나만 생성해야 한다. 그렇지 않으면 중복해제가 됨. !! Warning!!

void a() {
Nothing *my = new Nothing();
shared_ptr<Nothing> sp1( my ) ;
shared_ptr<Nothing> sp2( my ) ;
}
// 에러위험. 위 함수가 실행되면, my객체의 constructor 1번 호출, destructor 2번 호출된다.

void a() {
Nothing *my = new Nothing();
shared_ptr<Nothing> sp1( my ) ;
shared_ptr<Nothing> sp2 = sp1 ;     // sp를 대입해서 참조해야 한다.
}
// ok. 위 함수가 실행되면, my객체의 constructor 1번 호출, destructor 1번 호출된다.





반응형


+ const와 pointer 헛갈림 주의!
pointer(*) vs const pointer( *const )
포인터냐 콘스트 포인터냐?

주소를 보호하는 걸까 데이터를 보호하는 걸까? 
const뒤에 오는 것이 중요! *p면 값 보호, p면 주소 보호.
또는 영어로 코드를 거꾸로 읽으면 간단하다.


1)  값의 보호 ; const 뒤에 *가 있다.   (많이 사용. 값의 보호가 주로 목적)

  • 그냥 포인터임. p is a pointer. p앞에 *가 있다.
const char *p =&xxx;          ====> const 뒤에 *가 있으면 값의 보호다.
char const *p = &xxx ;  (상동!) ==> const 뒤에 *가 있으면 값의 보호다.

변수 p 바로 앞에 *가 있으면 그냥 일반 포인터가 된다. (const pointer가 아니라 일반 포인터일뿐) 
포인터가 가리키는 타입이 const char ; 값 보호.
p is a pointer to  const char/char const(same) ; 코드를 거꾸로 읽으면 이렇게 됨.
*p=1;            // error!!  const로 지정된 값 보호.  값 변경 불가.
p=&yy ;          // ok!  주소가 변경됨.


2) 주소 보호 ; const 뒤에 *가 없음. 앞에 있음. (많이 쓰이지 않음. 선언과 동시에 초기화 필요.)

  • 콘스트 포인터임. const pointer​  (*const). 포인터 보호.
int *const p = &xxx ;     
int *const p ;    ==> 에러! 초기화를 해줘야 한다. 왜? 변경불가이기 때문에...

변수 p 바로 앞에 *가 없으면 앞을 묶어서 타입으로 봐야 한다. *const 이므로 const pointer 가 된다. (일반 포인터가 아니다.)
p is a const pointer(*) to int. ; 코드를 거꾸로 읽으면 이렇게 됨.

*p=1;            // ok 값 변경.
p=&yy ;          // error! 주소변경 불가.


3)  값과 주소 둘 다 보호
const int *const p=&xxx;
p is a const pointer(*) to const int. 
*p = 1 ;     // error
p = &yy;     // error


즉, *가 어디에(변수에? or const에?)  붙었나 살펴보거나, 코드를 뒤에서 부터 읽으면 된다.



+ Recent posts