728x90

이번에 비트맵을 불러와서 Resizing 하고 다시 저장해서 확인하기 위해 


비트맵 파일을 불러오고 저장하는 예제를 만들었다.


#define LOG_OUT(_x_) OutputDebugString(_x_) 

#define LOG_OUT_W(_x_)  OutputDebugStringW(_x_) 

#define LOG_OUT_A(_x_)  OutputDebugStringA(_x_) 


bool LoadBmp(char* filename, byte** pImage);

bool SaveBmp(char* filename, byte* pImage, int width, int height);


int _tmain(int argc, _TCHAR* argv[])

{

    byte* pLoadImage = nullptr;


    LoadBmp("../Original_image.bmp", &pLoadImage);


    SaveBmp("../Save_image.bmp", pLoadImage, 1920, 1080);


    free(pLoadImage);


    return 0;

}


bool LoadBmp(char* filename, byte** pImage)

{

    FILE* fp;


    // 비트맵 정보 구조체

    BITMAPFILEHEADER BMFH; ///< BitMap File Header.

    BITMAPINFOHEADER BMIH; ///< BitMap Info Header.


    fopen_s(&fp, filename, "rb");

    if (nullptr == fp)

    {

        LOG_OUT_A("fopen() error");

        return false;

    }


    // 지정한 크기만큼 파일에서 읽어옴, 그리고 비트맵파일헤더에 넣어줌

    fread(&BMFH, sizeof(BITMAPFILEHEADER), 1, fp);

    if (BMFH.bfType != 0x4d42) // 비트맵 파일이 아니면 종료한다.

    {

        fclose(fp);

        LOG_OUT_A("not '.bmp' file !!!");

        return false;

    }


    fread(&BMIH, sizeof(BITMAPINFOHEADER), 1, fp); //인포헤더에 있는 크기의 정보만큼 읽어서

    if (BMIH.biBitCount != 24 || BMIH.biCompression != BI_RGB) //24비트인지 체크하고, 압축이 안되어 있는지 체크를 함

    {

        fclose(fp);

        return false;

    }


    INT Width = BMIH.biWidth;

    INT Height = BMIH.biHeight - 1;

    INT BytePerScanline = (Width * 3 + 3) & ~3;  // 패딩

    INT size = BMFH.bfSize;


    *pImage = (BYTE*)malloc(size);


    fread(*pImage, size, 1, fp);  // 파일의 정보를 전부 읽어온다.

    //*pImage += BytePerScanline * Height;


    // FILE*을 닫음.

    fclose(fp);


    return true;

}


bool SaveBmp(char* filename, byte* pImage, int width, int height)

{

    // DIB의 형식을 정의한다.

    BITMAPINFO dib_define;

    dib_define.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);

    dib_define.bmiHeader.biWidth = width;

    dib_define.bmiHeader.biHeight = height;

    dib_define.bmiHeader.biPlanes = 1;

    dib_define.bmiHeader.biBitCount = 24;

    dib_define.bmiHeader.biCompression = BI_RGB;

    dib_define.bmiHeader.biSizeImage = (((width * 24 + 31) & ~31) >> 3) * height;

    dib_define.bmiHeader.biXPelsPerMeter = 0;

    dib_define.bmiHeader.biYPelsPerMeter = 0;

    dib_define.bmiHeader.biClrImportant = 0;

    dib_define.bmiHeader.biClrUsed = 0;


    // DIB 파일의 헤더 내용을 구성한다.

    BITMAPFILEHEADER dib_format_layout;

    ZeroMemory(&dib_format_layout, sizeof(BITMAPFILEHEADER));

    dib_format_layout.bfType = *(WORD*)"BM";

    dib_format_layout.bfSize = sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER);// +dib_define.bmiHeader.biSizeImage;

    dib_format_layout.bfOffBits = sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER);


    // 파일 생성.

    FILE* fp = nullptr;

    fopen_s(&fp, filename, "wb");

    if (nullptr == fp)

    {

        LOG_OUT_A("fopen() error");

        return false;

    }


    // 생성 후 헤더 및 데이터 쓰기.

    fwrite(&dib_format_layout, 1, sizeof(BITMAPFILEHEADER), fp);

    fwrite(&dib_define, 1, sizeof(BITMAPINFOHEADER), fp);

    fwrite(pImage, 1, dib_define.bmiHeader.biSizeImage, fp);

    fclose(fp);


    return true;

}


소스 코드 : 

test.cpp


728x90
728x90

책들을 봐도 잘 이해가 안되는 것 같아서 따로 찾아보다가 좋은 예시를 찾았다...


큰 그룹으로 나누어 주는 API 는 ID3D11DeviceContext::Dispatch() 이다.

ipImmediateContextPtr->Dispatch( 3, 2, 1 );

이렇게 큰 블럭 단위로 나누고 난 후에 ComputeShader HLSL 에서는 이들을 세부적인 스레들로 분할하는 문법을 지정한다.


[numthreads(4, 4, 1)]
void MainCS( ... )
{
        ....
}


결과적으로 위의 그림처럼 스레드들이 생성되어서 병렬적으로 실행이 된다.
위에 나열된 숫자들은 스레드 ID 로써의 역활을 한다.
즉, 어떤 스레드의 ID 가 MainCS 함수에 파라메터로 넘어오면,
그 ID 를 통해서 해당 버퍼에 값을 작성하게 된다.

예를 들어, 

[numthreads( 256,1,1) ]

void VectorAdd( uint3 id: SV_DispatchThreadID )
{

  gBufOut[id] = gBuf1[id] + gBuf2[id];

}


아무리 스레드들이 복잡하게 동작하더라도, 위와 같이 ID 를 통해서 제어한다면
그 어떤 작업도 문제없이 할 수 있다.


728x90
728x90

컴퓨터를 바꾸게 되어 코드를 옮기고 


빌드를 하려고 하는데 갑자기 


FXC : error X3501: 'main': entrypoint not found


라는 에러 메시지가 출력되었다.

이것은 HLSL 컴파일러의 형식, 타입이 잘못 설정되어 있어서 생기는 문제였다.

다음과 같이 속성을 변경해 주면 된다. (셰이더 모델과 타입은 현재 작성한 HLSL에 맞추어서 변경하면 된다.)


728x90
728x90

윈도우 키 입력을 받아 테스트 할 때 쓰기 위해 Define을 해놓은 키보드 코드 이다.


간단하게 많이 쓰는 키보드는 적어놓고 나머지는 파일을 첨부한다.


#define VK_0 0x30

#define VK_1 0x31

#define VK_2 0x32

#define VK_3 0x33

#define VK_4 0x34

#define VK_5 0x35

#define VK_6 0x36

#define VK_7 0x37

#define VK_8 0x38

#define VK_9 0x39

#define VK_A 0x41

#define VK_B 0x42

#define VK_C 0x43

#define VK_D 0x44

#define VK_E 0x45

#define VK_F 0x46

#define VK_G 0x47

#define VK_H 0x48

#define VK_I 0x49

#define VK_J 0x4A

#define VK_K 0x4B

#define VK_L 0x4C

#define VK_M 0x4D

#define VK_N 0x4E

#define VK_O 0x4F

#define VK_P 0x50

#define VK_Q 0x51

#define VK_R 0x52

#define VK_S 0x53

#define VK_T 0x54

#define VK_U 0x55

#define VK_V 0x56

#define VK_W 0x57

#define VK_X 0x58

#define VK_Y 0x59

#define VK_Z 0x5A


WindowsKeyboardCodes.h


출처 : http://www.opensource.apple.com/source/WebCore/WebCore-855.7/platform/WindowsKeyboardCodes.h

728x90
728x90

list는 '시퀀스 컨테이너'이며 '노드 기반 컨테이너'입니다. 그래서 데이터의 삽입, 삭제가 시퀀스 중간에 자주 발생할 때 사용하면 좋은 컨테이너입니다.

 

list의 주요 개념을 그림으로 표현하면

 

 

 

1, list의 반복자

위 그림처럼 list는 앞쪽과 뒤쪽 모두에 데이터를 추가(push_front(), push_back())할 수 있는 함수들을 제공합니다. 또 list는 이중 연결 리스트로 구현되어 있어 앞뒤로 이동 가능한 '양방향 반복자'를 제공합니다. 연결 리스트의 특징인 중간에 삽입, 삭제가 빈번하게 발생할 때 효율적인 컨테이너입니다.

 

list 간단한 예제(데이터 앞쪽, 뒤쪽 추가)

#include <iostream>
#include <list>
using namespace std;

void main( )
{
    list<int> lt;


    lt.push_back(10)// 리스트 뒤쪽에 추가
    lt.push_back(20);
    lt.push_back(30);
    lt.push_back(40);
    lt.push_back(50);

    list<int>::iterator iter;
    for( iter = lt.begin() ; iter != lt.end() ; iter++)
        cout << *iter << ' ';
    cout << endl;

    lt.push_front(60)// 리스트 앞쪽에 추가
    lt.push_front(70);
    lt.push_front(80);
    for( iter = lt.begin() ; iter != lt.end() ; iter++)
        cout << *iter << ' ';
    cout << endl;

}

  1. 10 20 30 40 50
    80 70 60 10 20 30 40 50

 list는 데이터를 앞뒤에 추가할 수 있는 '시퀀스 컨테이너'로 push_front()와 push_back()함수를 제공합니다.(push_front()를 제공하는 컨테이너는 list와 deque뿐입니다.)

10~50까지 리스트 뒤쪽에 추가되며 60, 70, 80은 계속 앞쪽에 추가됩니다.

 

 

 

 

  list는 '연속 메모리 기반 컨테이너'가 아니므로 operator[]연산자는 가지고 있지 않고 반복자를 통해서만 컨테이너의 요소들에 접근할 수 있습니다. list는 양방향 반복자를 제공합니다. 그래서 반복자에 산술 연산은 불가능하다.(산술 연산이 가능한 반복자는 '임의 접근 반복자'뿐입니다. '연속 메모리 기반 컨테이너'가 임의 접근 반복자를 제공합니다.) 8개의 STL 컨테이너 모두 양방향 반복자 이상을 제공하며 이 중 '연속 메모리 기반 컨테이너'인 string, vector, deque는 산술 연산이 가능한 임의 접근 연산자를 제공합니다.

 

다음은 양방향 반복자를 사용하는 예제입니다.

#include <iostream>
#include <list>
using namespace std;
void main()
{
    list<int> lt;


    lt.push_back(10);

    lt.push_back(20);
    lt.push_back(30);
    lt.push_back(40);
    lt.push_back(50);

    list<int>::iterator iter;
    for( iter = lt.begin() ; iter != lt.end(); iter++)
        cout << *iter << ' ';
    cout << endl;

    iter--;

// iter += 2; 산술 연산은 불가능

    cout << *iter << endl;
    cout << *--iter << endl;
    cout << *--iter << endl;
    cout << *--iter << endl;
    cout << *--iter << endl;

}

  1. 10 20 30 40 50
    50
    40
    30
    20
    10

 양방향 반복자로 반복자에 ++, -- 연산자 모두 가능하며 산술연산은 불가능합니다.

 

 

 

  모든 STL 컨테이너는 시작과 끝을 가리키는 반복자 쌍을 반환하는 begin()과 end()를 가진다고 했습니다. 또 모든 컨테이너는 이와 반대인 rbegin(), rend() 함수로 끝과 시작을 가리키는 반복자 쌍을 제공합니다.

 

rbegin()과 rend()함수의 사용

#include <iostream>
#include <list>
using namespace std;

void main()
{
    list<int> lt;

    lt.push_back(10);

    lt.push_back(20);
    lt.push_back(30);
    lt.push_back(40);
    lt.push_back(50);

    list<int>::iterator iter;
    for( iter = lt.begin() ; iter != lt.end(); iter++)
        cout << *iter << ' ';
    cout << endl;

    list<int>::reverse_iterator riter;
    for( riter = lt.rbegin() ; riter != lt.rend(); riter++)
        cout << *riter <<' ';
    cout << endl;
}

  1. 10 20 30 40 50
    50 40 30 20 10

 list의 뒤에서 앞쪽으로 이동하도록 reverse_iterator를 제공합니다. 이 시작과 끝을 가리키는 반복자 쌍을 반환하는 함수가 rbegin()과 rend()입니다. 반복자의 자세한 내용은 뒤쪽에서 다시 공부합니다.

 

 

 

2, list의 주요 특징과 함수

  list의 장점 중 하나가 데이터 요소의 삽입, 삭제시에 다른 '시퀀스 컨테이너'보다 '효율적이다'는 것입니다. 이유는 '연속 메모리 기반 컨테이너'처럼 컨테이너 요소들이 밀려나지 않기 때문입니다.

모든 컨테이너는 insert()함수와 erase()함수를 가지고 있어 반복자가 가리키는 위치의 요소를 삽입하거나 삭제할 수 있습니다. 또 STL이 제공하는 여러 삽입, 삭제 알고리즘을 사용할 수 있습니다.

이때 '연속 메모리 기반 컨테이너'는 주의할 점이 있습니다. 연속 메모리 기반 컨테이너는 요소의 삽입과 삭제가 '선형 시간'이라는 것입니다. 또 메모리가 재할당될 수 있고 컨테이너의 모든 다른 요소들이 복사될 수 있다는 것입니다.

 

vector와 list의 삽입 예제

#include <iostream>
#include <list>
#include <vector>
using namespace std;

void main()
{
    vector<int> v;
    list<int> lt;

    v.push_back(10);
    v.push_back(20);
    v.push_back(30);
    v.push_back(40);
    v.push_back(50);


    lt.push_back(10);

    lt.push_back(20);
    lt.push_back(30);
    lt.push_back(40);
    lt.push_back(50);

    vector<int>::iterator viter = v.begin();
    viter++;
    viter = v.insert( viter, 80); // vector의 두 번째 요소로 삽입
     cout << *viter << endl;

    list<int>::iterator liter = lt.begin();
    liter++;

    liter = lt.insert( liter, 80); // list의 두 번째 요소로 삽입
      cout << *liter << endl;

    cout << " vector : ";
    for( viter = v.begin() ; viter != v.end(); viter++)
        cout << *viter << ' ';
    cout << endl;

    cout << " list : ";
    for( liter = lt.begin() ; liter != lt.end(); liter++)
        cout << *liter << ' ';
    cout << endl;
}

  1. 80
    80
     vector : 10 80 20 30 40 50
     list : 10 80 20 30 40 50

 결과는 같지만 내부적인 동작은 전혀 다릅니다. vector의 요소는 밀려나며 메모리 재할당 및 복사가 발생할 수 있습니다.

아래는 위 예제의 그림입니다.

 

 

 

 

아래는 erase()함수 예제입니다.

#include <iostream>
#include <list>
#include <vector>
using namespace std;

void main()
{
    vector<int> v;
    list<int> lt;

    v.push_back(10);
    v.push_back(20);
    v.push_back(30);
    v.push_back(40);
    v.push_back(50);


    lt.push_back(10);    lt.push_back(20);
    lt.push_back(30);
    lt.push_back(40);
    lt.push_back(50);

    vector<int>::iterator viter = v.begin();
    viter++;
    viter = v.erase( viter); // vector의 두 번째 요소로 삭제
     cout << *viter << endl;

    list<int>::iterator liter = lt.begin();    liter++;
    liter = lt.erase( liter); // list의 두 번째 요소로 삭제
     cout << *liter << endl;

    cout << " vector : ";
    for( viter = v.begin() ; viter != v.end(); viter++)
        cout << *viter << ' ';
    cout << endl;

    cout << " list : ";
    for( liter = lt.begin() ; liter != lt.end(); liter++)
        cout << *liter << ' ';
    cout << endl;
}

  1. 30
    30
     vector : 10 30 40 50
     list : 10 30 40 50

 erase()함수를 사용한 삭제도 '삽입' 동작과 같은 특징을 보입니다.

 

컨테이너 요소의 삽입, 삭제는 다음에 다시 공부하겠습니다.


출처 : http://blog.daum.net/coolprogramming/76

728x90

'Basic Programming > STL' 카테고리의 다른 글

STL - Set, MultiSet 컨테이너  (0) 2016.04.04
STL - Deque 컨테이너  (0) 2016.04.04
STL - String 컨테이너  (0) 2016.04.04
STL - Vector 컨테이너  (0) 2016.03.23
STL - Standard Template Library (STL)  (0) 2016.03.22
728x90

vector는 STL 컨테이너에서 가장 많이 사용하는 컨테이너 중 하나입니다.

 

STL 컨테이너는 각각 자신만의 특징을 가지고 있습니다. 각 컨테이너의 특징은 성능('시간 복잡도'나 '공간 복잡도')과 STL 여러 요소에 영향을 주기 때문에 각 컨테이너의 특징을 이해하는 것은 상당히 중요합니다. 그래야 자신의 프로그램에 맞는 적절한 컨테이너를 선택하여 사용할 수 있습니다.

 

vector의 주요 특징은 앞장에서 배운 것처럼 '시퀀스 컨테이너'이면서 '연속 메모리 기반 컨테이너'입니다. 또 컨테이너에 데이터가 삽입될수록 메모리가 자라나게 됩니다. 연속 메모리 기반이므로 메모리가 자라나면 기존 메모리를 삭제하고 새로운 메모리를 재할당하여 사용합니다.

vector의 주요 개념을 그림으로 표현하면

 

 

 

컨테이너에 앞서 계산 성능을 평가하는 '계산 시간 복잡도'를 간단히 정리하고 가겠습니다.

시간 복잡도는 "어떤 일을 얼마나 오랜 시간 동안 수행해야 하는가?"에 대한 함수로 나타내는 것을 의미합니다.

'시간 복잡도'는 크게 네 가지로 볼 수 있습니다.

  • 상수 시간 복잡도(constant time complexity) : 작업양이 증가할 때 변하지 않는 일정한 수행 성능을 보인다는 것.
  • 로그 시간 복잡도(logarithmic time complexity) : 작업양이 증가할 때 수행 시간도 일정하게 늘어나지만, 로그 함수의 비율로 늘어나는 것.
  • 선형 시간 복잡도(linear time complexity) : 작업양이 증가할 때 수행 시간도 비례해서 늘어나는 것.
  • 지수 시간 복잡도(exponential time complexity) : 작업양이 증가할 때 수행 시간도 지수함수의 비율로 늘어나는 것.

그림으로 보면

 

 

 

1, vector의 특징과 주요 함수

예제로 vector의 함수들을 공부합니다.

 

가장 간단한 예제(정수 추가 후 출력)

#include <iostream>
#include <vector>
using namespace std;
void main ( )
{
    vector<int> v;

    v.push_back(10);
    v.push_back(20);
    v.push_back(30);
    v.push_back(40);
    v.push_back(50);

    forint i = 0 ; i < v.size() ; i++)
        cout << v[i] << ' ';
    cout << endl;
}
  1. 10 20 30 40 50
  •  push_back()컨테이너 v에 요소를 추가하는 함수입니다. (모든 시퀀스 컨테이너는 요소를 끝에 추가할 수 있고 push_back()함수를 가지고 있습니다.)
  •  size()는 v 요소의 개수를 반환합니다. (모든 컨테이너는 컨테이너의 요소 개수를 반환하는 size()함수를 가지고 있습니다.)
  •  operator[]() 함수는 i 번째 인덱스의 요소를 반환합니다.(모든 연속 메모리 기반의 컨테이너와 map은 operator[]()함수를 가지고 있습니다.)

 

vector의 중요한 특징 중 하나가 연속 메모리 기반 컨테이너이므로 요소가 추가될 때 메모리가 자라난다는 것입니다. 추가될 때마다 메모리가 자라난다는 것은 메모리를 재할당해야(메모리의 크기를 늘려야 하므로) 한다는 것을 말합니다. 그래서 너무 비효율적으로 메모리 재할당되는 것을 막기 위해 요소가 추가될 때마다 메모리를 늘리지 않고 미리 여유 메모리 공간을 확보합니다.(여유 메모리의 크기를 늘리는 정책은 컴파일러마다 조금씩 다릅니다.)

 

중요 특징 - capacity(할당된 메모리 공간의 크기)와 size(저장된 데이터 요소의 개수)

#include <iostream>
#include <vector>
using namespace std;

void main ( )
{
    vector<int> v;


    cout << "size"<< ":" << "capacity " << endl;
    v.push_back(10);
    cout << v.size() << " : " << v.capacity() << endl;
    v.push_back(20);
    cout << v.size() << " : " << v.capacity() << endl;
    v.push_back(30);
    cout << v.size() << " : " << v.capacity() << endl;
    v.push_back(40);
    cout << v.size() << " : " << v.capacity() << endl;
    v.push_back(50);
    cout << v.size() << " : " << v.capacity() << endl;
    v.push_back(60);
    cout << v.size() << " : " << v.capacity() << endl;
    v.push_back(70);
    cout << v.size() << " : " << v.capacity() << endl;
    v.push_back(80);
    cout << v.size() << " : " << v.capacity() << endl;
    v.push_back(90);
    cout << v.size() << " : " << v.capacity() << endl;
    v.push_back(100);
    cout << v.size() << " : " << v.capacity() << endl;

    forint i = 0 ; i < v.size() ; i++)
        cout << v[i] << ' ';
    cout << endl;
}

  1. size:capacity
    1 : 1
    2 : 2
    3 : 3
    4 : 4
    5 : 6
    6 : 6
    7 : 9
    8 : 9
    9 : 9
    10 : 13
    10 20 30 40 50 60 70 80 90 100

10개의 정수를 push_back()하고 있습니다. size는 v에 저장된 요소의 개수이며 capacity는 할당된 메모리 공간의 크기입니다. VS2008 컴파일러는 기존 메모리의 반만큼의 메모리를 더 늘려서 메모리를 할당합니다. 또 기존 메모리의 모든 컨테이너 요소를 재할당된 메모리로 복사합니다.

 

 

  

 

 

그래서 컨테이너 요소의 개수가 유동적인 곳에는 vector가 비효율적입니다.

위 내용을 해결하는 방법은 두 가지 정도가 있습니다.

첫째는 생성자를 이용하여 메모리 공간(capacity)과 크기(size)을 미리 확보하고 초기화하는 것입니다.

#include <iostream>
#include <vector>
using namespace std;

void main( )
{
    vector<intv(10);

    cout << v.size() << " : " << v.capacity() << endl;
    for(int i = 0 ; i < v.size() ; i++)
        cout << v[i] << ' ';
    cout << endl;

    for(int i = 0 ; i < v.size() ; i++)
        v[i] =  (i+1)*10;

    for(int i = 0 ; i < v.size() ; i++)
        cout << v[i] << ' ';
    cout << endl;
}
  1. 10 : 10
    0 0 0 0 0 0 0 0 0 0
    10 20 30 40 50 60 70 80 90 100

 v(10) 처럼 size를 10으로 설정하면 0으로 초기화된 size 10개와 메모리 공간이 확보됩니다. 값을 변경할 때 operator[]()함수를 사용했습니다.


아래는 위 예제의 그림입니다.( vector<int> v(10); )

 

 

 

둘째는 reserve()함수를 이용하여 메모리 공간(capacity)만 미리 확보하는 것입니다.

#include <iostream>
#include <vector>
using namespace std;

void main( )
{
    vector<int> v;

    v.reserve(10);

    cout << "size"<< ":" << "capacity " << endl;
    cout << v.size() << " : " << v.capacity() << endl;
    for(int i = 0 ; i < 10 ; i++)
    {
        v.push_back((i+1)*10);
        cout << v.size() << " : " << v.capacity() << endl;
    }

    for(int i = 0 ; i < v.size() ; i++)
        cout << v[i] << ' ';
    cout << endl;
}
  1. size:capacity
    0 : 10
    1 : 10
    2 : 10
    3 : 10
    4 : 10
    5 : 10
    6 : 10
    7 : 10
    8 : 10
    9 : 10
    10 : 10
    10 20 30 40 50 60 70 80 90 100

메모리 공간(capacity)은 미리 확보했으므로 push_back()함수를 호출해도 메모리 재할당이 발생하지 않습니다.

 

아래는 위 예제의 그림입니다. ( v.reserve( 10 ); )

 

 

 

2, vector의 반복자

모든 컨테이너는 자신만의 반복자를 갖습니다.

반복자를 사용하는 간단한 예제

#include <iostream>
#include <vector>
using namespace std;

void main( )
{
    vector<int> v;

    for(int i = 0 ; i < 10 ; i++)
        v.push_back(i+1);

    vector<int>::iterator iter;
    for( iter = v.begin() ; iter != v.end() ; iter++)
        cout << *iter << endl;
}
  1. 1
    2
    3
    4
    5
    6
    7
    8
    9
    10

 모든 컨테이너의 반복자는 각 컨테이너 클래스의 내포 클래스(vector<int>::iterator)로 정의되어 있습니다. 또 반복자는 반복자끼리 비교할 수 있는 ==과 != 가 연산자 중복되어 있으며 다음 요소로 이동하도록 ++연산자와 반복자가 가리키는 컨테이너 요소에 접근하도록 *연산자와 ->연산자가 중복되어 있습니다.

 

 

 

vector의 반복자는 임의 접근 반복자로 반복자에 산술 연산이 가능합니다.

#include <iostream>
#include <vector>
using namespace std;

void main( )
{
    vector<int> v;

    for(int i = 0 ; i < 10 ; i++)
        v.push_back(i+1);

    vector<int>::iterator iter;
    for( iter = v.begin() ; iter != v.end() ; iter++)
        cout << *iter << endl;

    iter = v.begin();
    iter += 3;
    cout <<"[3] : " << *iter << endl;
    *iter = 0;
    cout <<"[3] : " << *iter << endl;
}
  1. 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    [3] : 4
    [3] : 0

vector의 반복자는 iter에 산술연산이 가능합니다. (모든 연속메모리 기반 컨테이너는 임의 접근 반복자를 제공합니다.)

또 *연산자를 사용하여 반복자가 가리키는 메모리에 접근하고 변경할 수 있습니다.

그림으로 보면

 

 

 

만약 읽기만 가능한 반복자를 사용해야 한다면 const_iterator를 사용해야합니다.

#include <iostream>
#include <vector>
using namespace std;

void main( )
{
    vector<int> v;

    for(int i = 0 ; i < 10 ; i++)
        v.push_back(i+1);

    vector<int>::const_iterator iter;
    for( iter = v.begin() ; iter != v.end() ; iter++)
        cout << *iter << endl;

    iter = v.begin();
    iter += 3;
    cout <<"[3] : " << *iter << endl;
    //*iter = 0; 에러! const_iterator는 가리키는 요소를 변경할 수 없습니다.
    cout <<"[3] : " << *iter << endl;
}
  1. 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    [3] : 4
    [3] : 4

 iter는 상수 반복자(const iterator)로 가리키는 요소의 변경이 불가능합니다. 반복자는 STL의 마지막 부분에서 자세히 공부합니다.


출처 : http://blog.daum.net/coolprogramming/76

728x90

'Basic Programming > STL' 카테고리의 다른 글

STL - Set, MultiSet 컨테이너  (0) 2016.04.04
STL - Deque 컨테이너  (0) 2016.04.04
STL - String 컨테이너  (0) 2016.04.04
STL - List 컨테이너  (0) 2016.03.23
STL - Standard Template Library (STL)  (0) 2016.03.22
728x90

STL은 표준 C++ 라이브러리의 일부분으로 Standard Template Library의 약자입니다.

STL은 사람마다 조금씩 다른 정의를 내립니다. C++의 권위자인 Scott Meyers는 STL을 "반복자를 가지고 동작하는 C++ 표준 라이브러리의 일부분"이라고 정의했습니다.

STL은 우리가 C++프로그래밍에서 만들어야 하는 여러 가지 자료구조 클래스와 알고리즘 등을 미리 만들어 놓은 라이브러리로 반복자라는 놈을 통해서 동작하는 라이브러리입니다.

 

STL의 주요 구성 요소로 컨테이너, 반복자, 알고리즘, 함수자가 있습니다.

  • 컨테이너(Container) : 객체들을 저장하는 객체 혹은 클래스(vector, list, string, map 등)
  • 반복자(iterator) : 컨테이너에 저장된 요소를 순회하고 접근하는 객체 혹은 클래스(추상화)
  • 알고리즘(Algorithm) : 데이터를 다루기위한 일련의 함수(find, short, search 등)
  • 함수자(Functor) : 함수처럼 동작하는 객체로 operator() 연산자를 오버로딩한 클래스의 객체

 

그림으로 간단하게..

 

 

 

1, 컨테이너

컨테이너는 동일한 타입의 객체를 저장, 관리할 목적으로 만들어 놓은(추상화한) 클래스입니다.(혹은 그 컨테이너 클래스에 의해 만들어진 객체를 컨테이너라고도 합니다.)


컨테이너는 크게 두 가지로 나뉩니다.

  • 표준 시퀀스 컨테이너(standard sequence container) : vector, string, deque, list인 선형방식의 컨테이너
  • 표준 연관 컨테이너(standard associative container) : set, multiset, map, multimap인 비선형방식의 컨테이너

 

 

또 컨테이너는 데이터를 하나의 메모리 단위로 저장하느냐에 따라 두 가지로 나뉩니다.

  • 연속 메모리 기반 컨테이너(contiquous-memory) : 보통 데이터 여러개가 하나의 메모리 단위에 저장합니다. 배열 기반 컨테이너(array-based container)라고도 합니다.
  • 노드 기반 컨테이너(node-based) : 데이터 하나를 하나의 메모리 단위에 저장합니다.

 

위 두 그림을 꼭 기억하세요. 컨테이너의 종류는 상당히 중요합니다. 종류에 따라 여러 가지 특징(성능, 무효화, 알고리즘 사용)이 달라지기 때문입니다. 자세한 내용은 다음에 하나하나 공부하도록 하겠습니다.

 

대표적인 컨테이너가 vector입니다.

간단한 vector 예제입니다.

#include <iostream>
#include <vector>
using namespace std;
void main( )
{
    vector<int> v;

    v.push_back(10);
    v.push_back(20);
    v.push_back(30);

    for(int i = 0 ; i < v.size() ; i++)

        cout << v[i] << endl;

}

  1. 10
  2. 20
  3. 30

 vector<int> 클래스와 이 클래스로 생성한 객체 v가 컨테이너입니다. push_back()과 size()는 멤버 함수입니다. 

v[i]는 operator[]() 멤버 함수인 연산자 중복 함수입니다. 컨테이너 v는 int형 객체(정수) 10, 20, 30을 저장합니다.

 

2, 반복자

반복자는 포인터와 비슷한 놈이라고 알아두시면 되겠습니다. 단 반복자는 컨테이너에 저장된 객체들에 접근하고 순회하는 일반화된 방법을 제공합니다.

그래서 반복자는 몇 가지 특징을 가져야 합니다.

  • 컨테이너 내부의 객체를 가리키고 접근할 수 있어야 한다.
  • 컨테이너 내부의 모든 객체를 순회할 수 있어야 한다.

STL의 모든 컨테이너는 자신만의 반복자를 제공합니다.

 

반복자는 조작 방법에 따라 다섯 가지로 나뉩니다.

  • 입력 반복자(input iterator) : 현 위치의 데이터를 읽기만 가능한 반복자
  • 출력 반복자(output iterator) : 현 위치에 데이터를 쓰기만 가능한 반복자
  • 순방향 반복자(forward iterator) : 입력, 출력 반복자 기능 +  순방향으로 이동 가능한 반복자
  • 양방향 반복자(bidirectional iterator) :  순방향 반복자 기능 + 역방향으로 이동 가능한 반복자
  • 임의 접근 반복자(random access iterator) : 양방향 반복자 기능 + 반복자의 산술 연산이 가능한 반복자

 

임의 접근 반복자를 예로 보겠습니다. vector는 임의 접근 반복자를 제공합니다.

#include <iostream>
#include <vector>
using namespace std;

void main( )
{
    vector<int> v;

    v.push_back(10);
    v.push_back(20);
    v.push_back(30);

    vector<int>::iterator iter;
    for(iter = v.begin() ; iter != v.end() ; iter++)
        cout << *iter << endl;

    iter = v.begin()+2;
    cout << *iter << endl;
}

  1. 10
    20
    30
  2. 30

 vector<int>::iterator처럼 반복자 클래스는 vector 클래스의 내포 클래스로 정의되어 있습니다. begin() 멤버 함수는 컨테이너 v에 저장된 첫 번째 객체를 가리키는 반복자를 반환합니다. end() 멤버 함수는 저장 객체의 끝을 의미하는 반복자를 리턴합니다. 또 임의 접근 반복자는 v.begin()+2처럼 반복자에 산술 연산이 가능합니다.

반복자는 뒤에서 다시 공부합니다.

 

3, 알고리즘

일반적인 용어의 알고리즘은 어떤 문제를 해결하기 수행 절차나 방법을 의미합니다. 만약 '정렬'이라는 문제가 주어진다면 정렬이라는 문제를 해결하는 방법(알고리즘)은 무수히 많습니다. 또 검색, 삭제, 복사 등의 문제도 모두 수많은 해결 방법이 존재합니다. 이런 해결 방법의 절차를 알고리즘이라 합니다.

 

 STL은 프로그램에서 반복되는 여러 가지 문제들의 해결 방법(알고리즘)을 일반화된 함수(템플릿 함수)로 제공합니다.

 

대표적인 정렬 알고리즘입니다.

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
void main( )
{
    vector<int> v;

    v.push_back(10);
    v.push_back(80);
    v.push_back(30);
    v.push_back(20);
    v.push_back(50);

    vector<int>::iterator iter;
    for(iter = v.begin() ; iter != v.end() ; iter++)
        cout << *iter <<' ';
    cout << endl;

    sort(v.begin(), v.end());

    for(iter = v.begin() ; iter != v.end() ; iter++)
        cout << *iter <<' ';
    cout << endl;
}
  1. 10 80 30 20 50
    10 20 30 50 80

 모든 알고리즘은 반복자로 동작합니다. sort()는 v의 첫 번째 객체를 가리키는 반복자와 마지막을 의미하는 반복자를 인자로 받아 정렬합니다.

 

for 루프도 for_each()알고리즘을 사용하여 작성할 수 있습니다.

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
void Print(const int& n)
{
    cout << n <<' ';
}
void main( )
{
    vector<int> v;

    v.push_back(10);
    v.push_back(80);
    v.push_back(30);
    v.push_back(20);
    v.push_back(50);

    for_each(v.begin(), v.end(), Print);
    cout << endl;

    sort(v.begin(), v.end());

    for_each(v.begin(), v.end(), Print);
    cout << endl;
}
  1. 10 80 30 20 50
    10 20 30 50 80

 for_each()는 v.begin() 반복자부터 v.end()반복자까지 v 컨테이너에 저장된 모든 객체를 인자로 Print함수를 호출합니다. for_each() 알고리즘은 QuicStart2의 '함수 포인터와 함수자3'를 참고하세요.

알고리즘은 뒤에서 다시 공부합니다.

 

for_each()는 아래와 비슷하게 정의되어 있습니다.

template<class _InIt,class _Fn1>
inline  _Fn1 for_each(_InIt _First, _InIt _Last, _Fn1 _Func)
{  
    for (; _First != _Last; ++_First)
        _Func(*_First);
    return (_Func);
}

 

4, 함수자

함수자는 함수처럼 사용할 수 있는 객체입니다. 함수자는 멤버 함수 operator()를 가지고 있기 때문에 객체를 함수처럼 사용할 수 있습니다.

 함수자는 QuickStart2의 '함수 포인터와 함수자2'를 참고하세요.

 

대표적인 함수자 클래스가 less<>와 greater<> 입니다.

#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>
using namespace std;

void Print(const int& n)
{
    cout << n <<' ';
}
void main( )
{
    vector<int> v;

    v.push_back(10);
    v.push_back(80);
    v.push_back(30);
    v.push_back(20);
    v.push_back(50);

    for_each(v.begin(), v.end(), Print);
    cout << endl;

    sort(v.begin(), v.end(), less<int>() );

    for_each(v.begin(), v.end(), Print);
    cout << endl;

    sort(v.begin(), v.end(), greater<int>() );

    for_each(v.begin(), v.end(), Print);
    cout << endl;
}
  1. 10 80 30 20 50
    10 20 30 50 80
    80 50 30 20 10

  sort()함수의 세 번째 인자로 비교 함수자(predicate)를 전달하면 이 비교 함수자를 이용하여 정렬 객체를 비교합니다.

 

함수자 greater<>와 less는 아래와 비슷하게 정의되어 있습니다.

template<class _Ty>
struct greater{  
    bool operator()(const _Ty& _Left, const _Ty& _Right) const
    {  
        return (_Left > _Right);
    }
};
template<class _Ty>
struct less
{
    bool operator()(const _Ty& _Left, const _Ty& _Right) const
    {  
        return (_Left < _Right);
    }
};



728x90

'Basic Programming > STL' 카테고리의 다른 글

STL - Set, MultiSet 컨테이너  (0) 2016.04.04
STL - Deque 컨테이너  (0) 2016.04.04
STL - String 컨테이너  (0) 2016.04.04
STL - List 컨테이너  (0) 2016.03.23
STL - Vector 컨테이너  (0) 2016.03.23
728x90

#include "stdafx.h"
#include "stdlib.h"
#include "assert.h"
#include <windows.h>
#include <stdio.h>
#include <list>
#include <algorithm>

using namespace std;
void Delete(char* p)
{
     delete p;
}

int _tmain(int argc, _TCHAR* argv[])
{
    //포인터 보관
     list<char*> g_listL; // 16MByte
     list<char*> g_listM; // 1MByte
     list<char*> g_listS; // 1~1024byte
    
    //약 2GB미만의 메모리 할당

     for (int i=0;i<115;i++)
     {
          g_listS.push_back(new char[4096]); // 1~8192Byte
          g_listL.push_back(new char[1024*1024*16]); // 16MByte
          g_listM.push_back(new char[1024*1024]); // 1MByte
     } 
    // 16Mbyte 리스트는 해제
     for_each(g_listL.begin(),g_listL.end(),Delete);
     
     int nSizeL = (int)g_listL.size();
     g_listL.clear();

       
    // 절반 횟수로 좀던 큰 덩어리 32MByte로 할당 
    nSizeL = nSizeL /2; 
     for (int i=0;i<nSizeL;i++)
     {
          g_listL.push_back(new char[1024*1024*32]);
     }

     for ( ; ; )
     {
     }
 
     return 0;
}



1. 하나의 사용자 프로세스가 사용하는 약 2GB 미만의 주소공간을 할당한 후의 모습




2. 할당된것중 16MBye크기들만 해제한후  단편화된 모습 (연속적인 메모리 공간이 없어보인다)



3. 다시 더 큰 32MByte 로  할당시 실패 




*!! 단편화가되면!!
- 할당가능한 위치를 찾느라 시간이 상대적으로 더걸린다. 
- 여유 메모리가 있음에도  연속적인 공간이없어 할당에 실패한다.



출처 : http://egloos.zum.com/ldw9981/v/3504884

728x90

+ Recent posts