'프로그래밍'에 해당되는 글 105건

  1. 2011.09.21 [MFC] mfc로 툴만들기( 탭 컨트롤 ) - 1
  2. 2011.09.15 [C,C++] 함수 오버로딩(Function Overloading)
  3. 2011.09.14 [C,C++] 디폴트(DeFault) 매개 변수
  4. 2011.09.14 [C,C++] 이름공간(namespace)
  5. 2011.09.14 [C,C++] 인-라인(in-line) 함수
  6. 2011.09.08 [자료구조] 스택(stack) 이란...
  7. 2011.09.06 [자료구조] 연결 자료구조
  8. 2011.09.06 [자료구조] 순차 자료구조 - 선형 리스트의 구현
  9. 2011.09.04 [자료구조] 순차 자료구조 - 선형 리스트.
  10. 2011.08.28 [C,C++] 가변 인자란 ...
  11. 2011.08.28 [C,C++] getchar() 의 문제점.. 2
  12. 2011.08.27 [C,C++] scanf 와 fgets
  13. 2011.08.27 화면 전환, cvFlip()
  14. 2011.08.23 [C,C++] sprintf 사용하기... 2
  15. 2011.08.23 [C,C++] 문자열에 대하여...
  16. 2011.08.22 [C,C++] 배열의 초기화..
  17. 2011.08.21 [C,C++] 포인터 변수에 왜 타입을 지정하는가?!
  18. 2011.08.17 [자료구조] 자료구조는 왜 필요한 것인가...
  19. 2011.08.17 [C,C++] C 포인터란 무엇인가..
  20. 2011.08.17 [openCV] openCV 영상 이진화 하기.. 1
  21. 2011.08.16 [openCV] OpenCV 웹캠 입력 검은 화면이나 회색 화면이 뜰 때...
  22. 2011.08.16 [openCV] OpenCV 웹캠 영상 입력 받기 5
  23. 2011.08.16 [openCV] OpenCV 2.1 버전 설치하기 ( VS 2008 )
  24. 2011.08.12 if문 내부의 조건식의 순서를 바꿔쓰자! 1
  25. 2011.08.12 C++ assert() 함수 사용하기..
  26. 2011.08.09 [API] 메세지 박스 만들기
  27. 2011.08.07 [API] 여러 가지 출력 들 ( 픽셀, 선, 사각형, 원 )
  28. 2011.08.07 [API] DC 를 이용한 문자열 출력
  29. 2011.08.05 [API] 다양한 현재시간과 현재날짜 얻기 1
  30. 2011.08.03 [API] DC ( Device Context ) 란..........

이 포스팅은 지난 포스팅인 기본 뼈대 만들기의 프로젝트에서 이어서 진행된

다.

기본 뼈대 만들기 바로가기

먼저 왜 탭 컨트롤이 필요한지 생각해보자..

탭 컨트롤이란.. 같은 공간에 각 탭별 각각의 주제별로 아이템들을 모아서 사용

하기 좋게 만들 수 있는 것이다...

툴만들기에는 필수적 이라고 볼수있다.. 이제 차근차근 하나씩 만들어보자..

먼저..


도구 상자에서 탭 컴트롤을 추가한다...

추가 위치는 IDD_CONTROLVIEW 다이얼로그에 하면 된다..


추가를 하고 탭컨트롤이 선택된 상태에서 오른쪽 버튼을 클릭. 변수 추가를 선

택한다.


변수명은.. 각자 원하는 것으로 하면된다..


그리고 사용할 탭의 갯수 만큼 다이얼로그 삽입을 한다.. 일단 테스트 용으로 2

개만 만들었다..


다이얼로그의 버튼을 다 제거하고, 대충 예전의 모습과 비슷하게 크기를 조정

한다...


그리고 속성에서 Border를 None으로...

style을  Child로 바꾼다.. ( 다이얼로그 2개 모두. )


그리고 클래스를 저장하기 위해서 다이얼로그를 더블 클릭하면, 이런 창이 생

긴다.. 기본 클래스 CDialogEx에서..


그냥 CDialog로 바꾸고..


클래스 명을 입력한후 마침을 누른다.. ( 클래스 명은 알아서 바꿔도 상관없음)


여기까지 완료했으면.. ControlView.h 에 들어간다.. 그러면 위의 그림과 같이..
마지막 줄에 탭 컨트롤 변수가 추가되어있을 것이다.. 만약 안되어 있다면.. 다

시 위에서 부터 차근차근 보고 확인해보길 바란다..


ControlView.h 에 다이얼로그 클래스의 h 파일을 추가한 후...


변수를 추가한다...
 


생성자에서 다이얼로그 변수를 초기화 해주고, 소멸자에서는 해제를 해준다..

저 해제를 위한 매크로는 기본적으로 포함된 것이 아니기 때문에.. 위에 추가를
해주어야한다.. NULL인지 판단해서 NULL이 아닐때만 해제를 하는 매크로로써
유용하게 사용된다..

 


여기 까지 추가 했으면 클래스 뷰로 가서 CControlview를 클릭한다..


그리고 오른쪽의 재정의 창을 클릭하면, OnInitialUpdate 라는 함수가 있다..

이 함수를 추가하자..


그러면 이런 함수가 하나 생기는데.. 그 곳에다가...


이렇게 추가를 하자...

내용이 길어져서 다음 포스팅에서 계속.........
Posted by 바람처럼..
|

 1] 함수 오버로딩의 이해

확장자가  .c로 끝나는 C 언어로 작성된 프로그램에서는 다음과 같은 함수의

정의가 허용되지 않았다.

int function(void)
{
        return 0;
}

int function(int a, int b)
{
         return a+b;
}

위의 소스는 함수의 이름이 같기 때문에 컴파일 타임에 오류를 발생시킨다. 아

니 c 에서는 오류를 발생 시켰다. 하지만 C++로 넘어오면서 부터 달라졌다. c++

은 호출하고자 하는 함수를 찾을 떄 함수의 이름뿐 아니라 매개 변수의 정보까

지도 동시에 참조한다. 따라서 c++은 이름이 같고 매개 변수의 타입 혹은 개수

가 다른 함수들의 정의를 허용한다. 그리고 이를 가리켜 함수 오버로딩

(function overloading) 이라 한다.

2] 함수 오버로딩의 조건 

c++은 함수의 이름이 같아도 매개 변수의 타입 및 개수가 다르면 문제되지 않으

며, 이를 가리켜 " 함수가 오버로딩 되었다. " 라고 표현한다고 하였다. 다음은

대표적인 함수 오버로딩의 예이다.  

int function1(int n){.....}

int function2(char c) {.......}

위의 두 함수도 오버로딩 되었다. 함수의 이름이 같지만 매개 변수의 자료형이

다르기 때문이다.

int function(int v){.....}

int function(int v1, int v2){.......}

위의 두 함수도 오버로딩 되었다. 함수의 이름과 매개 변수의 자료형은 같지만

자료형의 개수가 다르기 때문이다.

즉, 함수 오버로딩의 기본 조건은 다음과 같다.

"함수의 이름은 같지만 매개 변수의 타입이나 개수가 달라야 한다. "

그 이외에도 함수가 오버로딩 되기 위한 조건이 하나 더 있는데 이에 대해서는

다음 기회에 언급하겠다.

3] 오해하기 쉬운 함수 오버로딩

함수 오버로딩을 처음 접하게 되면 두 개의 함수를 눈으로만 보고 잘못 판단하

는 경우가 종종있다. 예를 하나 들어 보겠다. 다음의 함수들은 오버로딩 조건을
충족하는가, 아니면 충족하지 못하고 컴파일 시 오류를 발생시키겠는가? 눈 여

겨 볼  부분은 함수의 리턴 타입이다.

 #include <iostream>

 void function(void)
{
        std::cout << "function(void  call " << std::endl;
}

int function(void)
{
        std::cout << "function(char c) call " << std::endl;
        return 1;
}

int main(void)
{
        function();
        return 0;
}

물론 위에 정의되어 있는 두 개의 function 함수는 이름은 같지만, 리턴 타입이

다르다. 그러나 리턴 타입만 가지고는 함수가 오버로딩되었다고 말할 수 없다.

결국 컴파일러는 " 난 네가 뭘 원하는지 몰라 (Ambiguous) " 라는 메시지를 출

력하게 된다. 즉, 리턴 타입만 달라서는 함수가 오버로딩되지 않는다.

 

'프로그래밍 > C,C++' 카테고리의 다른 글

[C,C++] memset, memcpy 함수 사용법!  (6) 2011.10.11
[C,C++] 더블버퍼링..  (0) 2011.10.05
[C,C++] 디폴트(DeFault) 매개 변수  (0) 2011.09.14
[C,C++] 이름공간(namespace)  (0) 2011.09.14
[C,C++] 인-라인(in-line) 함수  (0) 2011.09.14
Posted by 바람처럼..
|
디폴트(Default)란 컴퓨터 공학에서 " 기본적인 " 이라는 의미를 지닌다. 매개

변수가 무엇인지는 알고 있을 것이다. 그렇다면 기본적인 매개 변수라는 뜻이

된다.

1] 디폴트 매개 변수의 의미

함수를 다음과 같은 형태로 정의하는 것이 가능하다 .

int function(int a = 0);
{
      return a+1;
}

여기서 매개 변수를 선언하는 부분을 보면 a는 0으로 설정되어 있다. 이것이 바

로 디폴트 매개 변수이다. 그렇다면 이것이 지니는 의미는 무엇일까? 의외로 간

단하다. " 만약에 function이라는 이름의 함수를 호출하면서 인자를 전달하지

않으면 0이 들어 온 것으로 간주를 하겠다 " 는 뜻이다. 말 그대로 디폴트(기본

적으로 설정해 놓은) 매개 변수다.

2] 다양한 형태의 디폴트 매개 변수 설정

이번에는 디폴트 매개 변수의 다양한 형태를 예제를 통해서 살펴 보기로 하자.

다음에 소개하는 것은 직육면체의 넓이를 구하는 프로그램이다.

#include < iostream> 

int BoxVolume(int length, int width = 1, int height = 1); 

int main()
{
       std::cout << " [3,3,3]  :    " << BoxVolume(3,3,3) << std::endl;

       std::cout << " [5,5,def]  :    " << BoxVolume(5,5) << std::endl;

       std::cout << " [7,def,def]  :    " << BoxVolume(7) << std::endl;

       return 0;

int BoxVolume(int length, int width, int height)
{
        return length*width*height;
}

함수 BoxVolume이 아랫쪽에 정의되어 있고, 위쪽에 선언되어 있다. 이러한 형

태의 구성은 이미 C 언어를 통해서 학습한 것이다. 여기서 눈 여겨 볼 부분은

함수의 선언이다. 총 세 개의 매개 변수를 인자로 전달 받는데, 두 번째, 세 번

째 인자에만 디폴트 매개 변수를 설정해 놓았다.

 한가지 주의할 것은 이렇게 함수의 선언이 함수의 정의 이전에 존재하는 경우

디폴트 매개 변수는 선언 부분에 놓여져야 한다는 것이다.  

3] 디폴트 매개 변수와 함수의 오버로딩 

디폴트 매개 변수와 함수 오버로딩을 동시에 잘못 정의하는 경우가 있다. 다음

예제를 보자. 그리고 문제점을 지적해 보자.

#include <iostream>

int function(int a = 0)
{
        return a+1;

int function(void)
{
        return 10;
}

int main(void)
{
        std::cout << function(10) << std::endl;

        return   0;

위의 예제를 컴파일 해 보면 컴파일도 잘 되고 실행도 잘 된다. 왜냐하면 3번쨰

줄에 정의된 함수 function과 7번째 줄에 정의된 함수 function과의 관계는 오버

로딩 관계이고 main 내에서는 int형 데이터 하나를 인자로 전달 받는 함수, 즉

위쪽 함수를 호출하였으므로 문제될 것이 전혀 없다. 그러나 잠재적인 문제를

지니고 있다. 위의 예제에서 main 함수를 다음과 같이 변경한다면 어떻게 될

까?

int main(void)
{
     std::cout << function() << std::endl;

     return   0;
}

드디어 잠재적인 문제가 터지고 말았다. 위의 function 함수를 호출하면서 인자

를 전달하지 않고 있다. 그렇다면 어떤 함수를 호출 하겠는가? 문제를 위에서

정의한 두 함수 모두 호출 가능하다는데 있다. 컴파일러는 두 개의 함수 중에서
무엇을 호출해야 할 것인지를 결정 짓지를 못한다. 따라서 에러를 발생시켜 버

린다. 그러므로 이러한 형식의 함수 정의는 반드시 피해야 한다.


'프로그래밍 > C,C++' 카테고리의 다른 글

[C,C++] 더블버퍼링..  (0) 2011.10.05
[C,C++] 함수 오버로딩(Function Overloading)  (0) 2011.09.15
[C,C++] 이름공간(namespace)  (0) 2011.09.14
[C,C++] 인-라인(in-line) 함수  (0) 2011.09.14
[C,C++] 가변 인자란 ...  (0) 2011.08.28
Posted by 바람처럼..
|
이름 공간이란 다소 생소한 개념이다. C 언어에는 존재하지 않는 개념이기 때

문이다. 그러나 어려운 개념은 아니다. 이름공간이란 이름을 지니는 공간이라

는 뜻이다. 말 그대로 특정영역(공간)에 이름을 붙여주기 위한 문법적 요소이

다.

1] 등장 배경

 프로그램이 대형화되어 가면서 등장하기 시작한 문제는 이름의 충돌이다 .예

를 들어서 은행 관리 시스템을 개발하는데 있어서 세 개의 회사가 참여를 한다.
각 회사의 이름은 A, B, C 이다. 이들은 규모가 큰 관계로 일을 독립적으로 진

행하기로 하였다. 그러기 위해서는 구현해야 할 부분도 적절히 나눴다. 이제 6

개월 뒤 다시 모여서 하나의 프로젝트를 완성하기로 약속을 한다.

 6개월이라는 시간이 흘렀다. 이제 각각의 회사가 구현한 모듈을 하나로 묶고

부족한 부분을 완성 할 때가 되었다. 그런데 문제가 생기고 말았다. A라는 회사

에서 정의한 함수의 이름과 B라는 회사에서 정의한 함수의 이름이 같은 것이

아닌가! 이름 충돌이 나지 않도록 미리 약속이라도 할 것을 그랬다는 후회가 든

다. 이제는 전쟁이다! 이름하나 바꾸는 것이 어디 쉬운 일인가? A  회사의 팀장

과 B 회사의 팀장이 서로 싸우기 시작한다. 물론 싸움의 요는 이거다. " 저희가

무지 바쁘거든요? 당신네 회사에서 양보를 조금 해 주셔야 하게습니다." 그런데
문제는 산 넘어 산이다. C 라는 회사에서는 자신들이 맡은 부분을 구현하는 과

정에서 다른 회사에서 구현해 놓은 함수들을 사용했는데, 이 함수들의 이름이

회사A, B에서 구현해 놓은 함수의 이름과 또 충돌이 나는 것이 아닌가? 결국 이
프로젝트는 실패로 돌아가고 말았다.

이러한 문제를 해결하기 위해서 이름공간(namespace) 이라는 문법이 C++ 표

준에 새롭게 포함되었다.

2] 기본 원리

 한 집에 철수 라는 이름을 지니는 사람이 두 명 살고 있다면 이는 문제가 된다.
" 철수야!" 라고 부르면, 어떤 철수를 말하는지 구분 지을 수 없기 때문이다. 그

러나 서로 살고 있는 집이 다르다면 문제될 것이 없다. 201호에 사는 철수와

202호에 사는 철수는 구분 지을 수 있기 때문이다.  이것이 이름 공간의 기본 원

리이다.  다음 예제는 앞에서 이야기한 문제점을 간략화해서 코드로 옮겨 놓은

것이다. A와B라는 이름의 회사가 구현한 프로그램 모듈을 하나의 완성된 프로

그램으로 옮기는 과정에서 이름 충돌이라는 문제가 발생하였다.

 #include <iostream>

 void function(void)
{
      std::cout << "A.com에서 정의한 함수 " << std::endl;
}

void function(void)
{
      std::cout << "B.com에서 정의한 함수 " << std::endl;
}

 int main(void)
{
       function();
       return  0;
}

 위의 예제에는 A.com 이라는 회사에서 정의한 함수와 , B.com 이라는 회사에

서 정의한 함수의 이름이 같다. 따라서 컴파일 시 문제를 일으킬 것이다. 둘다

필요한 기능의 함수라서 어느것 하나를 뺄 수도 없는 상황이다. (물론 그렇다고
가정만 하자) 

다음 예제에서는 이름 공간을 정의해서 문제를 해결하고 있다. 일단 위의 예제

와 어느 부분이 달라졌는지 비교해 보자.

#include <iostream>

namespace A_COM
{
       void function(void)
       {
               std::cout << "A.com에서 정의한 함수 " << std::endl;
       }
}

namespace B_COM
{
        void function(void)
       {
                std::cout << "B.com에서 정의한 함수 " << std::endl;
        }
}

int main(void)
{

      A_COM:: function();

      B_COM:: function();

       return  0;
}

 위의 예제를 보면 namespace 라는 키워드가 등장한다. 이는 이름 공간을 구

성하겠다는 의미다. 즉 "namespace A_COM " 이라는 선언은 A_COM이라는

이름의 공간을 구성하겠다는 의미가 되며, 이름공간의 범위는 이어서 등장하는
중괄호를 통해서 지정된다. 조금 더 쉽게 이야기를 하면, " 특정 영역(공간)의

범위를 지정하고 이름을 붙여준 것" 이다. 이름 공간이 다르면 같은 이름의 변

수나 함수의 선언이 허용 된다. 하지만 한가지 주의할 것은 이름 공간 내에 선

언되어 있는 변수나 함수에 접근하는 방법이다. 다음과 같은 선언이 필요하다.

A_COM::function(); 

위 코드에서 :: 연산자를 가리켜 " 범위 지정 연산자(scope resolution

operator) " 라 한다. 즉 이름공간의 범위를 지정할 때 사용하는 것이다. 따라서

위 그림과 같은 선언은 다음과 같은 의미를 지니게 된다. 

" A_COM 이라는 이름 공간안에 선언되어 있는 function 함수를 호출하라"

 이렇듯 이름 공간 안에 선언되어 있는 변수나 함수에 접근하기 위해서는 범위

지정 연산자를 사용해서 선언되어 있는 이름공간을 지정해 줘야한다.

참고) 위의 예제들에서는 하나의 이름공간 안에 하나의 선언 및 정의만을 담고

있지만 실제로는 둘 이상의 선언 및 정의를 담을 수 있고 또 그것이 더 일반적

이다.

3] std::cout, std::cin, std::endl

 지금까지 입력 및 출력을 하고자 하는 경우에는 std::cout 과 std::cin을 사용해
왔다. 이것의 정체는 아직 모르지만 사용법만은 알고 있다. 그런데 이제는 조금
이해가 될 것이다. :: 연산자가 어떠한 의미를 지니는지 이해 했기 때문이다.

std::cout은 무슨 의미인가? std라는 이름공간 안에 존재하는 cout을 참조하겠

다는 의미이다. 물론 우리는 아직 cout의 정체를 모르고 있다. 마찬가지로

std::cin은 std라는 이름 공간 안에 존재하는 cin을 참조하겠다는 의미로 사용

된다. 아마도 다음과 같은 가정을 할 수 있을 것이다. 헤더파일 iostream에 선

언되어 있는 cout, cin, 그리고 endl은 std 라는 이름공간 안에 선언되어 있다는
결론을 내릴 수있다. 이름 충돌을 막기 위해서 c++표준에서 제공하는 다양한 함

수나 변수들을 이름공간 std 안에 선언 및 정의 했기 떄문이다.

4]using

이제 cout, cin 그리고 endl을 참조할 때 마다  " std:: " 을 앞에다 붙여줘야 하

는 이유에 대해서 알게 되었다. 그리고 이 일이 여간 귀찮은 것이 아니라는 생

각도 들기 시작한다. 오히려 접근하고자 하는 이름공간에 대한 선언이 불 필요

한 과거의 표준이 더 맘에 끌리기도 한다. 하지만 걱정하지 말자. 추가적인 선

언 하나만으로도 여러분의 이러한 불만사항을 해소할 수 있으니 말이다.

Posted by 바람처럼..
|

in-line 함수의 의미부터 파악해 보자. in은 ' 내부 '를 의미하고, line은 ' 프로그

램 라인 ' 을 의미하는 거이다. 즉 의역 해보면 " in-line 함수" 란 프로그램 라인
안으로 들어가버린 함수라는 의미를 지닌다.  

1] 매크로 함수의 정의와 장점

 우리는 C 언어를 공부하면서 매크로에 대해서 알게 되었다. 여기서는 이에 관

련해서 잠깐 복습을 하기로 하자. 매크로를 이용하면 다양한 일을 할 수 있는

데 그 중에서도 가장 대표적인 일은 함수를 만드는 일이다. 다음은 매크로를 이

용해서 함수를 정의하고 이를 호출하는 형태를 보여준다.  일반적인 순서에 의

해서 전처리, 컴파일, 링크 과정을 거쳐서 실행 파일이 생성될 것이다.  

 #include <iostream>

#define SQUARE(X)   ((X)*(X))

 int main(void)
{

       std::cout << SQUARE(5) << std::endl;

       return 0;
}

 그럼 위의 코드는 전처리 과정을 거치면서 어떻게 변하게 되는가? 다음 코드는
전처리 과정에 의해서 변경된 코드를 보여준다.

 #include <iostream>

 int main(void)
{

       std::cout << ((5)*(5)) << std::endl;

       return 0;
}

  위의 두 코드를 통해서 알 수 있는 사실은 무엇인가? 그것은 함수를 매크로로

정의하면 전처리기에 의해서 함수 호출 문장이 함수의 몸체 부분으로 완전히

대치돼 버린다는 것이다. 그렇다면 이를 통해서 얻게 되는 이점은 무엇인가? 함

수 호출 과정이 사라졌기 때문에 성능상의 이점을 맛볼 수 있다.( 함수 호출은

스택 메모리 공간의 할당도 요구하며 시간도 많이 요구된다.)

  위의 예제와 같이 함수 호출 문장이 함수의 몸체부분으로 완전히 대치되 버리

는 현상을 가리켜 " 함수가 inline화되었다." 라고 표현한다.

 참고) 매크로 함수에 대해서는 장점뿐만 아니라 단점도 알고 있어야 한다. 단

점은 함수의 구현이 까다롭고 디버깅하기 어려우며, 구현하고자 하는 함수의

크기가 크다면 프로그램의 크기가 커지게 된다는 것이다. 보자 자세한 사항은

C 언어 관련 서적을 참조하기 바란다.

 2] C++ 기반 함수의 in-line 화

 앞에서 매크로를 이용해서 함수를 in-line화 하였다. 이처럼 매크로를 이용해

서 C++의 함수도 in-line화 할 수 있다. 그러나 보다 쉬운 방법이 있다. 다음 예

제 코드는 앞에서 본 예제를 C++ 스타일로 in-line화 하고 있다.

#include <iostream>

inline int SQUARE(int x)
{
         return x*x;
}

int main(void)
{
       std::cout << SQUARE(5) << std::endl;

       return 0;
}

 함수를 정의하는 과정에서 키워드 inline을 붙여준 것을 제외한다면 일반적인

함수와 다를 바가 없다. 그렇다면 여기서 붙여준 키워드 inline은 무엇을 의미하

는 것일까? 이는 함수 SQUARE를 inline화 하라는 의미이다.

 정리하자! C++에서는 성능 향상을 목적으로 함수를 inline화 하기 위해서 매크

로를 사용할 필요가 없다. 다만 inline이라는 키워드만 붙여주면 되는 것이다.

얼마나 간편하고 좋은가?

 참고) 매크로를 이용한 함수의 in-line화는 전처리기에 의해서 처리되지만, 키

워드 inline을 이용한 함수의 in-line화는 컴파일러에 의해서 처리된다. 또한 컴

파일러에 따라서는 inline 선언이 오히려 성능 향샹에 해가 된다고 판단될 경우,
그냥 무시해 버리기도 한다.

'프로그래밍 > C,C++' 카테고리의 다른 글

[C,C++] 디폴트(DeFault) 매개 변수  (0) 2011.09.14
[C,C++] 이름공간(namespace)  (0) 2011.09.14
[C,C++] 가변 인자란 ...  (0) 2011.08.28
[C,C++] getchar() 의 문제점..  (2) 2011.08.28
[C,C++] scanf 와 fgets  (0) 2011.08.27
Posted by 바람처럼..
|


스택(stack)이란 쌓아 올린다는 의미다. 따라서 스택 자료구조라는 것은 접시

를 쌓듯이 자료를 차곡차곡 쌓아 올린 형태의 구조를 말한다.

스택 자료구조의 스택은 같은 구조와 크기의 자료를 정해진 방향으로만 쌓을

수 있고, top 이라고 정한 한 곳으로만 접근하도록 제한되어 있다.

스택에서는 top을 통해서 들어온 자료가 일정한 방향으로 차곡차곡 쌓이게 된

다. 스택에서의 top은 현재 스택의 가장 위에 있는 마지막 자료를 가리키고 있

고, 삽입되는 새 자료는 top이 가리키는 자료의 위에 쌓이게 된다. 그러면 삽입

된 자료는 스택의 마지막 자료가 되고, 이 때 top은 삽입된 자료를 마지막 자료

로 가리킨다. 스택에서 자료를 삭제할 때 에도 역시 top을 통해서만 가능하다.

따라서 스택은 시간 순서에 따라 자료가 쌓여서 가장 마지막에 삽입된 자료가

가장 먼저 삭제된다는 구조적 특징을 갖는다. 이러한 스택의 구조를 후입선출

(LIFO, Last-In-First-Out) 이라고 표현한다.

스택에서 top을 통한 삽입 연산을 push, top을 통한 삭제 연산을 pop이라고 한

다.
Posted by 바람처럼..
|

순차 선형 리스트는 논리적인 순서와 물리적인 순서가 같기 때문에 원소의 위

치를 찾아 엑세스 하기 쉽다는 장점이 있지만, 삽입 연산이나 삭제 연산 후에

연속적인 물리 주소를 유지하기 위해서 원소들을 이동시키는 추가적인 작업과

시간이 소요된다. 원소들의 이동 작업으로 인한 오버헤드는 원소의 개수가 많

고, 삽입,삭제 연산이 많이 발생하는 경우에 더 많이 발생한다. 또한 순차 자료

구조는 배열을 이용하여 구현하기 때문에 배열이 갖고 있는 메모리 사용의 비

효율성 문제를 그대로 갖는다.

순차 자료구조에서의 연산 시간에 대한 문제와 저장 공간에 대한 문제를 개선

한 자료 표현 방법으로 연결 자료구조 또는 비순차 자료구조가 있다. 연결 자료

구조에서는 순차 자료구조에서처럼 원소의 논리적인 순서와 물리적인 순서가

일치할 필요가 없다. 연속한 물리주소에 의해 원소의 순서를 표현하는 것이 아

니라 각 원소에 저장되어 있는 다음 원소의 주소에 의해 순서가 연결되는 방식

이기 때문에 물리적인 순서를 맞추기 위한 오버헤드가 발생하지 않는다. 하나

의 순차 자료구조를 위해서는 하나의 고정크기 메모리 공간을 사용하지만, 연

결 자료구조에서는 여러개의 작은 공간을 연결하여 전체를 표현하므로 크기 변

경이 유연하고 좀더 효율적으로 메모리를 사용할 수 있다.

연결 리스트는 리스트를 연결 자료구조로 표현한 구조로서 연결하는 방식에 따

라 단순 연결 리스트와 원형 연결 리스트, 이중 연결 리스트, 이중 원형 연결 리

스트로 나눌수 있다.

연결 자료 구조에서 원소는 연결될 다음 원소에 대한 주소를 저장해야 하기 때

문에 <원소, 주소> 의 단위로 저장해야한다. 이러한 단위 구조를 노드 라고 한

다. 노드는 원소의 값을 저장하는 데이터 필드와 다음 노드의 주소를 저장하는

링크 필드로 구성된다. 데이터 필드는 저장할 원소의 형태에 따라서 하나 이상

의 필드로 구성되기도 한다. 링크 필드는 포인터 변수를 사용하여 주소값을 저

장하며, 포인터 또는 링크, 참조 라고도 한다.

그리고 연결 자료 구조에서는 헤드 라고 불리는 시작 주소가 있다. 그 헤드는

시작 주소이면서, 첫 데이터를 가르키는 역할도 한다. 연결 리스트의 마지막 노

드는 다음에 연결되는 노드가 없으므로 링크 필드에 저장할 주소값이 없는데,

이럴때는 노드의 끝을 표시하기 위해 마지막 노드의 링크 필드에 NULL을 저장

한다. 만약 아무런 노드도 없다면 헤드에 NULL을 저장하여 널 포인터로 만들면
된다.
Posted by 바람처럼..
|


선형 리스트를 순차구조로 구현하기 위해서 배열을 사용한다. 배열은 <인덱스,
원소 > 의 쌍으로 구성되어 메모리에 연속적으로 할당되는데, 이때 인덱스는

배열 원소의 순서를 타나낸다. 배열은 순서를 가진 배열 원소들을 메모리에 연

속하여 구성하므로 프로그램 언어에서 제공하는 배열을 그대로 사용하여 구현

할 수가 있다.

그럼 가장 간단한 1차원 배열의 순차 표현을 알아보자.

1차원 배열은 인덱스를 하나만 사용하는 배열이다. 원소의 순서를 하나의 값으

로 구별할 수 있는 간단한 선형 리스트는 1차원 배열을 사용하여 표현할 수 있

다.

예를 들어 학교의 학생수에 대해서 생각해보자.

1학년에 4반이 있는데, 각 반에는 35명, 36명, 39명, 37명 이렇게 있다고 치자.

이 것을 배열을 통해 나타내면,

int student[4] =  { 35, 36, 39, 37 } ;

이렇게 표현할 수 있다.

1차원 배열 student는 int로 선언 되었으므로, 각 원소의 길이는 4바이트가 된

다. C 프로그래밍에서 배열의 인덱스는 0부터 시작하므로 배열 sale의 각 원소

의 위치는 시작 주소가 a일 때 a + ( 인덱스 * 4바이트 ) 가 된다.

이번엔 2차원 배열의 순차 표현을 알아보자. 

위의 1차원 배열과 마찬가지로 2차원배열은 학교의 학생수 분류에서 학년을 추

가해 보자.

1학년에 4반, 2학년에 4반, 3학년에 4반이 있다.

그렇게 되면, 

int student[3][4] = { { 35, 36, 39, 37 }, 
                             { 37, 36, 38, 39 }, 
                             { 34, 37 ,38 ,37 } } ;
이렇게 표현 할 수 있다.

2차원 배열의 구조를 논리적으로 표현할 때는 행과 열의 구조로 나타내지만, 실

제로 메모리에 저장될 때에는 1차원의 순서로 저장된다. 2차원의 논리적 순서

가 1차원의 물리적 순서로 변환되는 방법은 첫 번째 인덱스를 기준으로 하는 행
우선 순서와 마지막 인덱스를 기준으로 하는 열 우선 순서 방법이 있다.

간단히 말해서 행 우선 방법은 student[0][0] -> student[0][1]->student[0]

[2] 이런식으로 우선 0 의 0~3까지 그다음은 1의 0~3까지. 이런식으로 앞의 행

의 값을 쭉 넣고, 다음 행의 값을 넣는 방식이다.

그리고 열 우선 방법은 student[0][0] ->student[1][0]->student[2][0] 이렇

게 먼저 앞의 0의 0 값, 1의 0값, 2의 0값을 넣고 다음에 0의 1값, 1의 1값, 2의 1

값 이렇게 차례대로 넣는 방식이다. 

행 우선 방법과 열 우선 순서 방법에 따라 물리적 저장 순서가 다르기 때문에

배열의 원소 위치를 계산하는 방법도 달라져야 한다. 행의 갯수가 n1 이고, 열

의 갯수가 n2인 2차원 배열 A[n1][n2]의 시작 주소가 a고, 원소의 길이가 L 일

때, i행 j 열의 원소, 즉 A[i][j]의 위치는 행 우선 구조에서는 a + ( i * n2 + j ) *
L 이 되고, 열 우선 순서 구조에서는 a + (j * n1 + i) * L 이 된다.

논리 순서를 물리 순서로 변환하는 방법은 프로그래밍 언어의 컴파일러에 의해

서 결정되는데, C 컴파일러는 행 우성 순서 방법을 사용한다.

3차 배열도 이와 유사한 방법을 사용한다. 단지 배열의 크기가 지금은 행과 열

이였다면, 거기에 면이 하나 추가되는 개념이며, 논리적 구조를 물리적 구조로

바꾸는 공식은 조금만 생각하면 유추할 수 있으므로, 자세한 설명은 생략한다.
Posted by 바람처럼..
|


문제를 해결하기 위해 추상적으로 정의한 자료와 연산자는 구체적으로 구현되

어 실행되어야 하는데, 연산자의 구현은 처리할 자료를 표현하는 방법에 의해..

결정된다.

따라서 처리할 문제에 대한 자료를 가장 효율적인 방법으로 표현하는 것이 중

요하다.

자료구조에서 기본적인 자료 표현 방법으로는 순차 자료구조와 연결 자료구조

가 있다..

여기서는 순차 자료 구조를 알아보자..

가장 기초적인 자료를 표현하는 방법은 나열 하는 것이다. 동물이면 동물,

식물, 음식, 지리 등등을 나열한 목록을 list 라고 한다..

list 나열한 원소들 간에 순서를 가지고 있는 list를 선형 list 또는 순서 list라고

한다.

리스트를 표현하는 일반적인 방법은 다음과 같다..

list 이름 = ( 항목1, 항목2, 항목 3 )

선형 list는 원소들 같의 논리적인 수서와 메모리에 저장하는 물리적인 순서가

같은 구조로 되어잇는데, 이러한 구조를 순차 자료구조라고 한다..

따라서 순차 자료구조에서는 원소의 논리적인 순서대로 메모리에 연속적으로

저장된다..

즉, 순차 자료구조에서는 원소들이 순서대로 연속 저장되기 때문에 시작 위치

와 원소의 길이를 알면, 특정 원소의 위치를 알 수 있다.

시작 위치가 a고, 원소의 길이가 L 인 list에서 두번째 원소의 길이는

a + L 이고, 세번째 원소의 위치는 a + 2L 이다. 따라서 i 번째 원소의 위치는

a + (i -1 ) * L 이 된다..

 그렇다면, 선형 list 에서의 원소 삽입에 대해 알아보자...

 밥을 먹으로 갔는데, 밥집에 줄이 서있다....

일단 기다린다.. 그런데 누군가 새치기를 했다... (물론 이런경우는 없을 것이지

만 예를 들면이다..)

그렇다면 순번은 한칸씩 뒤로 밀린다...

이 것이 바로 선형 list 에서의 원소 삽입이다...

무언가 list에 순서대로 자료가 저장되어 있지만, 가운데 원소를 하나 집어 넣으

려면, 먼저 삽입할 자리를 만든 후에 그 자리에 원소를 삽입해야 된다..

그렇게 하려면, 메모리에 연속해서 저장되어 있는 자료들을, 한칸씩 뒤로 밀어

야 하겠다..

(n+1) 개의 원소로 이루어진 선형 list 에서 k번 자리에 원소를 삽입하려면,

k번 원소부터 마지막 원소인 n번째  원소까지 n - k + 1 개의 원소를 모두 한자

리씩 뒤로 밀어야 하므로 빈자리를 만들기 위한 이동 횟수는 n - k + 1 이 된다.

그렇다면 반대로 선형 list에서의 원소 삭제를 알아보자.

이번에는 반대로 차례대로 밥집의 줄을 서있는데, 갑자기 앞에 사람이 어디로

가버렸다.. 그렇다면 순서는 한칸씩 앞으로 당겨지게 된다...

이번에는 코드 상으로는 빠진 부분의 값은 뒤의 값이 덮어 버리면 되기 때문에

바로 값이 빠진 곳 부터 한칸씩 앞으로 값을 땡기기만 하면 된다...

이 것을 식으로 쓰면, (n+1)개의 원소로 이루어진 선형 list 에서 k번 자리의

원소를 삭제했다면 k+1번 원소부터 마지막 n번 원소까지 n - ( k + 1 ) + 1개의

원소를 모두 한자리씩 앞으로 옮겨야 하므로 필요한 이동 횟수는 n - ( k + 1 ) +
1 이 되어 정리하면, n - k 가 된다...

Posted by 바람처럼..
|


가변 인자란.. 쉽게 생각하면, 인자(파라미터) 값이 변한다는 의미이다..

정확히는 인자의 개수가 변한다는 뜻이다...

같은 함수명이라도,, 파라미터를 2개, 3개,.. 이렇게 쓸 수 있다는 것이다.

그렇다면 함수 오버로딩이 아닌가 생각할 수도 있다..

하지만 그 것과는 엄연히 다르다..

흔히 우리가 쓰는 printf()를 떠올리면 이해하기가 쉽다...

printf("%d %s ",  int , char ) ;

라고 보면 똑같이 printf() 이지만.. 뒤의 파라미터들이.. 2개일 수도.. 3개일수도

아니면 더 클 수도 있다...

이럴때 쓰는 것이 가변인자이다...

가변 인자를 사용 하려면.. 일단.. 함수의 원형을 정의하면된다..

가변 인자를 이용해서.. 입력된 숫자들의 총합을 구하는 함수를 만들어 보자..

int add ( int, ... ) ;

이렇게 선언을 한다... 앞의 int만 유일하게 고정 인자고,.. 뒤의 ... 으로 뒤에는

몇개의 숫자가 오든 상관없다 ( 앞의 int를 count로 쓸 것이니 함수를 사용할 때는.. 신경을 써야한다..)

그리고 가변 인자를 쓰기 위해서는 헤더파일..

#include <stdarg.h>

를 추가한다...

그리고 가변 인자를 위한 3개의 매크로가 있다..

va_start(va_list list) ;
va_arg(va_list list, type) ;
va_end(va_list list );

이다...

va_start는 list를 초기화 한다.. 그 의미는 포인터를 list의 맨 처음을

가리키도록 하는 것이다...

이 함수를 쓰지 않으면, va_arg를 통해서 모든 인자를 다 쓰면, 처음으로 되돌

릴 수 없게된다...

va_arg는 고정 인자를 제외한 나머지 인자들을 얻기 위해 사용되며, 보통은

for()문을 이용해서 처리한다...

type위치에는 int, char 같이 가변 인자의 자료형을 넣어준다...

va_end는 가변 인자의 사용을 끝낼 때 사용하지만, 보통은 사용 안해도 무방하다..

그럼 동작이 가능한 함수를 만들어 보자...

위의 사진의 int add(int count, ...) 함수의 내부를 보면..

va_* 매크로를 이용하여서 리스트를 초기화, 값을 받고, 종료 시킨다..

그리고 실행시킨 결과 값이다....

어떤가.. 가변 인자를 잘쓰면, 좀 더 깔끔한 코드를 작성할 수 있지 않을까?

하지만, 가장 큰 주의사항들을 정리하면..

꼭, 1개 이상의 고정 인자가 있어야 한다는 점이다.....

위 사진 처럼.. int add(int count, ...)  의 int count가 고정 인자 이다..

이 것을 없얘고 int add(...) 이렇게 쓰면 에러를 일으킨다..

printf()에서 첫 인자가 꼭 "%d", "%d %s" 인 이유가 바로 여기있다..

위처럼 %* 가 있어야.. 뒤에 가변 인자가 몇개인지.. 알 수 있기

때문이다...

'프로그래밍 > C,C++' 카테고리의 다른 글

[C,C++] 이름공간(namespace)  (0) 2011.09.14
[C,C++] 인-라인(in-line) 함수  (0) 2011.09.14
[C,C++] getchar() 의 문제점..  (2) 2011.08.28
[C,C++] scanf 와 fgets  (0) 2011.08.27
[C,C++] sprintf 사용하기...  (2) 2011.08.23
Posted by 바람처럼..
|

char를 입력받을 때 흔히 쓸 수 있는 것이 getchar() 이다..

하지만 getchar()는 쓸 때 한가지 명심할 것이 있다...

getchar() 함수를 써서 값을 입력받은 후, 엔터를 치면 함수가 진행된다...

그 때 개행 문자 하나도 같이 버퍼에 쌓이게 된다...

그 후 getchar()가 실행되어서 버퍼에서 입력된 값을 받은 후..

또 다시 1번 더 getchar()를 실행해 버린다...

예를 들면.. 지금 입력된 값이 y 인지 n 인지 판단하는데..

y 값이면 y, 나머지는 n로 인식된다고 생각해보자...

y 후에 엔터를 쳤더니,

한번은 y로 인식했는데 자동으로 다음에는 n 를 한번 더 인식한다.

( 반복문일 경우임 )

이유는 처음에는 y를 입력받은 걸로 처리하고, 또 버퍼에 값이 있어서 값을 보

니.. \n 인 것이다. 그래서 y가 아니기 때문에.. n로 인식하는 것이다..

이럴때는 getchar()를 2번 연속으로 적으면 된다..

int a = getchar() ; getchar() ;

이렇게 되면 자동으로 a에는 첫 입력값, 그 후에는 그냥 버퍼에 값을 한번 더 부

를 뿐이다....

하지만 문제가 하나있다..

그냥 값 입력없이 엔터를 쳤을 때다... 그렇게 되면 처음 getchar()에서 개행 문

자로 인식 되지만, 뒤의 getchar()에서 문제를 일으킨다..

그래서 이 것을 모두 해결한 코드는..

if( a == '\n') continue;
else getchar() ;

이렇게 쓰면 된다..

'프로그래밍 > C,C++' 카테고리의 다른 글

[C,C++] 인-라인(in-line) 함수  (0) 2011.09.14
[C,C++] 가변 인자란 ...  (0) 2011.08.28
[C,C++] scanf 와 fgets  (0) 2011.08.27
[C,C++] sprintf 사용하기...  (2) 2011.08.23
[C,C++] 문자열에 대하여...  (0) 2011.08.23
Posted by 바람처럼..
|

문자를 입력받을 때 흔히, scanf나 fgets 를 사용한다...

2가지 이외에도 gets() 라는 함수인데,

이 함수는 치명적인 문제가 있다..

버퍼 오버플로우를 검사하지 않기 때문에, 10의 공간을 할당하고 나서,

12의 값을 넣어도 ( 메모리의 크기 ) 일단은 들어간다..

하지만 나중에 큰 문제를 일으킬 소지가 있다...

그리하여, scanf와 fgets를 사용한다고 치면,

scanf는 숫자, fgets는 문자열을 입력받는 것으로 사용하는 것이 좋다.....

scanf의 가장 큰 단점은 띄어쓰기를 인식 못한다는 점이다...

예를들어.. 이름을 입력하고자

char name[20] ;
scanf("%s", name ) ;

이렇게 입력하고 콘솔창에 test test test ; 이렇게 입력한다면...

출력 결과는..

test 만 나온다...

만약 다 입력 받고 싶으면,

scanf("%s%s%s", name1, name2, name3 ) ;

이런식으로 입력하면 되긴하지만.. 원하는 동작은 아니다...

그리하여 scanf가 편리하긴 하지만.. 숫자를 입력받을 때 정도만 쓰는 것이 좋다...

문자열을 입력받을 때는 fgets를 사용하려면,

fgets(name, 10, stdin ) ;

이런식으로 사용하면된다.. 가운데의 10은 버퍼의 크기이기때문에 자유롭게..

변경하면된다...

하지만 fgets도 문제가 있는데.. fgets는 자동적으로 개행문자가 삽입된다..

개행문자는,, printf같은 것을 사용할 때 다음줄로 넘어가는 \n 이다..

그렇기 때문에..

출력시에는 문제없이 출력된 것 처럼 보이나, 문자열의 크기를 비교한다던지

하면 문제가 발생할 수 있다..

그러니 꼭, 입력된 문자의 길이를 검사하여서, 개행문자를 없애고 그 자리에

/0(NULL) 을 넣어주면된다..

이 것을 코드로 표현하면..

name[strlen(name) - 1 ] = '\0' ;

처럼 넣어줘도 된다...

(단, name의 크기가 유동적이면 힘들고, 딱 배열의 크기에 맞게 설정되었을 때에 쓸 수 있다.. )

'프로그래밍 > C,C++' 카테고리의 다른 글

[C,C++] 가변 인자란 ...  (0) 2011.08.28
[C,C++] getchar() 의 문제점..  (2) 2011.08.28
[C,C++] sprintf 사용하기...  (2) 2011.08.23
[C,C++] 문자열에 대하여...  (0) 2011.08.23
[C,C++] 배열의 초기화..  (0) 2011.08.22
Posted by 바람처럼..
|

openCV를 이용해서 간단한 화면 전환 방법을 소개한다...

간단히 cvFlip() 함수를 쓰면 된다...

cvFlip 함수 원형은 다음과 같다..

cvFlip( const CvArr* src, CvArr* dst CV_DEFAULT(NULL),
                     int flip_mode CV_DEFAULT(0));

먼저 첫번째 파라미터에 현재 이미지, 두번째가 변환 후 저장될 이미지를 넣으

면 되고, 마지막, 파라미터를 0을 넣으면 상하 반전, 1을 넣으면 좌우 반전이다.


위의 사진이 0을 넣은 상하반전

,


위의 사진이 1을 넣은 좌우 반전 이다..

두 사진 모두 왼쪽이 원본이고, 오른쪽이 변환된 화면이다..
Posted by 바람처럼..
|

코딩을 하다보면, 문자열에..

숫자를 넣어야 할 때가 있다..

그럴 때 유용한 것이 바로 sprintf 이다..

sprintf를 쓰기 위해서는

#include <stdio.h> 가 필요하다...

사용법은...

char buf[256] ;

int a = 9 ;

sprintf( buf , " test%d", a ) ;

이렇게 넣으면..

buf 에는 test9 라는 문자열이 저장된다...

응용하면,

char *string = "world" ;
sprintf( buf, "Hello %s %d", string, a ) ;

이렇게 쓰면,

buf에 Hello world 9 라고 저장된다...

또,

char buf[256] ;

for( int i = 0 ; i < 3 ; ++i )
{
sprinf( buf, "test %d", i ) ;
printf( "%s\n", buf ) ;
}

이렇게 쓰면,

test0
test1
test2

이런식으로 출력된다..
Posted by 바람처럼..
|

문자열은 C언어에서는 문자 배열이라고 생각하면 된다...

문자열은 여러가지 특징이 있는데, 그 것을 알아보자...

그전에 상수에 값이 들어가는 것에 대해 한번 알아보자...

a , 'a', "a" 이 3 가지의 상수가 있다고 봤을 때.. 이 것들은 모두 다르다..

a 같은 경우는 그냥 일반적인 상수라고 보면된다...

흔히, int a = 4 ; 에서 4 같이 그냥 숫자를 바로 넣는 형태이다...

'a' 는 하나의 문자 상수이다..

'a' 자체가 저장되기 때문에..

char a = 'a' ; 같이 쓸 수 있다...

그럼 내부 적으로는 char a 안에 아스키코드 형태로 97로 저장된다..

( a를 %d로 출력 하면 97이 나오고, %c로 출력하면 a 가 나오는 식이다.)

그럼 마지막으로 "a" 가 바로.. 문자열 형태이다...

단지 따옴표가 붙었을 뿐이지만.. 그 상태로 char* a ; 에 저장을 시키면..

문자열 형태로 저장된다.. ( 즉, char*가 아니라 char면 에러를 일으킨다. )

문자열 이라는 말은 제일 마지막에 /0 (NULL 문자 ) 가 들어가 있는 형태이다.

문자열을 넣을 때 우리는 ..

char* string = "Hello world" ;

이렇게 값을 넣으면 된다...

이러면 자동적으로 마지막에 널이 들어가 있다.

이 것을 메모리 상에서 보면.. string 이라는 포인터 변수가 잡히고,

그 포인터 변수가 가르키는 주소값이 바로.. Hello 의 H의 위치이다..

이 문자열의 주소 위치는 컴파일러가 알아서 저장한다..

그럼 문자열의 출력 방법을 알아보자...

printf("%s\n", string ) ;

printf( string ) ;

puts( string ) ;

이렇게 여러가지가 있는데... 단순 출력만을 한다면..

그냥 puts가 가장 가볍고 좋다..

또 배열처럼 string + 2 라고 쓰면 string 위치부터 + 2 주소값이 더해져서..

Hello의 첫번째 l 을 가리키게 되며, 그 값부터 널을 만나기 전까지 출력이 된다.

그렇다면

char *string ;

char string[] ;

의 차이점은 무엇일까??

쉽게 생각하면.. 같은 행동을 하고, 출력 하기 위한 방식도 똑같고, 각 변수명이

문자열의 첫 주소를 가진다...

하지만 이 2가지는 엄연히 다르다...

위의 *string 같은 경우는.. 포인터 변수로, 따로 포인터 변수의 4바이트 주소를

가지고, 그 주소에서 문자열의 첫 시작점의 주소를 가리킨다..

하지만 2번째 의 경우는 문자열 배열로써...

그냥 문자열의 첫번째 위치를 바로 가리키고 있다...

이 것이 가장큰 차이점이다..

그래서 string++ 을 하면 문자열의 다음 변수를 가리킬 수 있지만,

아래의 배열일 때 ,

string++을 하면 에러가 난다..

왜냐하면 string 자체는 배열명이기 때문에.. 주소값을 가지고는 있지만..

++ 연산자를 풀어보면, string = string + 1 ; 과 같은데,

배열명에는 값을 넣을 수는 없기 때문이다..

그리고 char* string = "Hello" ; 의 Hello는 문자열 상수라고 보면된다..

상수는 즉, 고유한 하나의 값이다... 1이 1이지 0이 아닌 것처럼...

그래서,

*string = 'A' ;

이렇게 값을 넣으려 그러면.. 에러가 난다...

포인터 변수가 가리키는 첫 주소값의 내용을 바꿀 수 있을 것 같지만,

그 것은 불가능하다..

하지만 반대로

char string[] = "Hello" ;

이런 배열 형태로 저장되어 있다면,

string[0]  = 'A' ; 처럼 쓰는 것은 또 가능하다 ...

문자열... 간단히 쓰고있지만, 내부적으로의 흐름을 알아야...

나중에 헷갈리지 않고 잘 쓸 수 있을 것이다...
Posted by 바람처럼..
|

프로그램을 작성할 때 쓰는 가장 보편적이고, 기본적인 자료구조가..

배열이다...

하지만 배열을 초기화를 제대로 하지 않으면 여러가지 문제가 생긴다..

그래서 초기화를 하는 방법을 소개하겠다...

먼저 1차원 배열이다..

가장 기본적인 형태인..

int intp[3];

이렇게만 쓰고 출력을 해보면 쓰레기 값이 들어가 있다..

이제 초기화를 해줘야 하는데..

가장 단순한 초기화 방법은...

int intp[0] = 0 ;
int intp[1] = 0 ;
int intp[2] = 0 ;

이다.. 하지만.. 이번엔 3개 같은 경우니 가능하지만 더 많아지면..

이렇게 하면 소스 가독성이 떨어진다..

그래서 반복문을 사용해서..

for(int i = 0 ; i < 3; i++ )
      intp[i] = 0 ;

이렇게 쓰면 된다..

하지만 가장 좋은 방법은..

int intp[3] = {0} ;

이렇게 쓰는 것이다.. 단, 이 방법은 선언과 함께 사용해 주어야 한다..

방금 방법은.. 0으로 초기화 하는 방법이였고, 혹시 값을 넣어야 한다면,

다음과 같이 초기화 해주면 된다..

( 이 것도 선언과 함께 사용하여야 한다. )

int intp[3] = { 1, 2, 3 } ;

이런 식으로 말이다.. 하지만..
 
int intp[3] = { 1, 2, 3, 4 } ;

이렇게 4개를 넣으면 어떻게 될까?

머.. 동작은 상관없이 된다.. 마지막 4는 짤리겠지만 말이다...

그런데 혹시 뒤에 4개를 넣은게 맞는 것이고,

앞의 3이 잘못 되었다면... 문제가 생길 것이다..

그럴때를 대비해서..

int intp[] = { 1, 2, 3, 4 } ;

처럼 넣어주면 된다...

2차원 배열도 비슷하다..

int intp [2][3] = { 1, 2, 3, 4, 5, 6 } ;

이렇게 선언하거나,

int intp [2][3] = { {1, 2, 3} , { 4, 5, 6} } ;

이렇게 선언하면 된다..
Posted by 바람처럼..
|

포인터 변수는 다음과 같이 정의할 수 있다..

int *a ;
float *b ;
double *c ;
char *d ;

이렇게 말이다..

그런데 저 포인터 변수들의 크기는 얼마일까??

int 는 4바이트,, double은 8바이트, char는 1바이트 일까??

결론을 말하면.. 모두 4바이트 이다....

포인터 변수는.. 주소값을 저장하는 거지.. 변수의 값을 저장하는 것이

아니기 때문이다...

그렇다면.. 모두 4바이트라면.... 그냥 처음부터..

포인터 변수 자료형을 만들어서 선언하면 되는 것이 아닐까??

예를 들면

point * p ; 같이 말이다....

어차피 모두 4바이트고..

int *a 를 선언했다가.. double 변수의 주소를 받고싶으면...
double d = 1.0f ;
(double *) a = &d ;

처럼 형 변환을 거쳐야 하는 수고도 덜고..

point * p ;
double d = 1.0f ;
p = &d ;

이렇게 쓸 수 있지 않을까???

하지만.. 모든 일에는.. 다 의미가 있는 법..

사실.. 포인터 변수에 형이 필요한 것은.. 포인터 변수 때문이 아니라..

나중에 연산을 하려다 보면 필요한 것이다....

포인터 변수자체는 4바이트 이지만..

나중에 int형 포인터 변수에 int형 주소를 저장하고..

그 int형 포인터가 가리키는 주소의 값을 받아오면..

int형 포인터 변수기 때문에 주소값으로 부터 4바이트의 값을 받아온다...

하지만.. 이것을 하나로 통일해 버리면..

받아올 값이.. int라 4바이트, double이라 8바이트, 머 구조체라서 24바이트 처럼..

컴퓨터가 알아서 가져 올 수 없기 때문에...

우리는 미리 형을 지정해 두어서.. 그 포인터 변수가 어떤 값을 가져올 때..

그 포인터 변수형의 바이트 수 만큼 그 주소에서 읽어 오는 것이다...

그러니 포인터 변수를 사용할 때는 꼭, 받아올 변수의 자료형으로 선언하자!

'프로그래밍 > C,C++' 카테고리의 다른 글

[C,C++] scanf 와 fgets  (0) 2011.08.27
[C,C++] sprintf 사용하기...  (2) 2011.08.23
[C,C++] 문자열에 대하여...  (0) 2011.08.23
[C,C++] 배열의 초기화..  (0) 2011.08.22
[C,C++] C 포인터란 무엇인가..  (0) 2011.08.17
Posted by 바람처럼..
|


컴퓨터 알고리즘을 공부하다 보면 자료구조 라는 이야기가.. 항상 같이 나온다...

그렇다면 자료구조는 무엇인가??

쉽게 예를 들어보면, 문구점에 가서 볼펜을 사려 할 때, 빨강, 파랑, 검정, 모두 어지럽게

한 공간에 꽂혀 있다면, 우리는 한 눈에 바로 내가 원하는 물건인지 확인을 하기 위해 하나씩 꺼내 봐야 할 지도 모른다...

하지만, 각 칸을 나눠서 여기는 빨강, 파랑, 검정, 그 중에서도 굵은 펜, 가는 펜 이렇게 분류가 되어있다면,

우리가 의사결정을 하는데 더 쉽고 수월할 것이다...

이것이 바로 자료구조인 셈이다...

이 것을 프로그램의 입장에서 바꿔보면, 데이터들이 중구난방으로 펼쳐져 있다면, 쉽게 접근하기도 어려울 뿐더러..

혹시 사용하게 되더라도 검증을 하거나, 처음부터 하나씩 빼서 확인하는 수 밖에 없다..

하지만 구역 별로 데이터들이 나눠져 있다면, 필요한 조건이 아닌 부분은 제외하고 검색을 하거나 할 수 있다..

이것은 바로 프로그램의 속도의 향상을 가져온다...

이렇게 같은 동작을 하는 프로그램이더라도, 얼마나 설계를 잘 했느냐, 꼭 필요한 자료구조를 사용하였나에 따라서

내부적으로 (혹은 외부적으로도) 더 빠르고, 버그가 없는 프로그램이 될 수 있을 것이다.

자료구조는 이론적인 부분과 효율적인 부분을 모두 다뤄야한다.

이론적으로 그래프 이론, 집합이론 등의 이론을 바탕으로 알고리즘을 분석하여 검색, 정렬 방법을 결정하고

효율적인 부분에서는 최상의 상태를 분석하여 결정해야한다..

이렇게 다양한 자료구조에 대해 이해하고, 논리적이고 수학적인 분석을 통해 최적의 알고리즘을 개발할 수 있는

이론적인 능력을 갖춰야지만 실제 시스템을 개발할 수 있는 고급 개발자가 될 수 있을 것이다..

자료구조는 크게 3가지로 나눌 수 있다..

선형구조, 비선형 구조, 파일 구조 이다...

선형 구조는 자료들간의 앞뒤 관계가 일대일로 고정되어 있는 구조로서, 리스트, 연결리스트, 스택, 큐, 덱 등이 있다.

비 선형 구조는 자료들간의 계층 구조나 망 구조를 갖는 자료구조로 트리와 그래프가 있다..

파일 구조는 서로 관련 있는 필드들로 구성된 레코드의 집합인 파일에 대한 자료구조로서, 보조기억장치에 데이터가

실제로 기록되는 자료구조다. 파일의 구성 방식에 따라 순차 파일과 색인 파일, 직접 파일 등이 있다...
Posted by 바람처럼..
|


포인터,, 언뜻 알고 쓰고 있는 것 같지만, 실제로 사용하다 보면 메모리 릭 문제나, 프로그램을 죽이는 가장 큰 원인이 되는

아주 프로그래머들의 천국과 지옥을 동시에 느낄 수 있게 하는 녀석이다....

편리하지만, 잘못 쓰면 독이 되는 아주 어려운 녀석이다..

오늘은 이 포인터에 대해서 정리를 해볼까 한다...

그렇다면 먼저 포인터란 무엇인가에 대해서 생각해 봐야겠다...

포인터는 한마디로 정의하자면, 메모리의 위치를 표현한 기호라고 생각하면 된다..

int a; 라는 변수를 선언 하였다고 생각해 보자..

a라는 변수가 할당되면서 메모리 주소에는 4바이트 공간이 할당되고, 그 공간의 시작 위치부터 메모리의 주소(번지)를 가지게

된다..

우리가 흔히 a = 4 ; 이런 대입식을 쓰면 a라는 변수에 기록된 메모리 주소에 4를 입력하게된다.. 이런 처리들이 컴파일러와 프

로그램 내에서 알아서 동작을 한다..

이 때 변수 a의 주소값을 가리킬 수 있도록 해주는 것이 바로 포인터 변수이다...

선언할 때 int *a ; 처럼 쓰는 아이들은 포인터 변수이다.. 이것이 중요하다...

*a 와 a 는 다르다..

a가 어떤 내용이 담기는 번지수가 가르키는 공간이라면, *a는 그 번지의 시작점을 가리키는 포인터 변수다....

이렇게 글로 적으면 좀 이해가 안되지만... 계속 머리속에 그려보거나 공책에 그려보면 이해가 될 것이다...

그리고 또 한가지 명심해야 할 것은 포인터 변수에는 주소값만 들어갈 수 있다는 것이다..

int a ;
int *b ;

라고 선언을 했을 때.. 선언 당시에는 int *b를 했지만 그 이후 사용할 때 *b를 쓰면 또 전혀 의도하지 않은 값이 된다..

이때 *는 연산자의 역할을 하기 때문이다...

선언 이외에  *b ; 를 쓰게 되면, *b는 포인터 변수 b가 가리키고 있는 주소의 내용.. 이 된다..

즉,

int *b ;
b = 4 ; << 이 것은 b에 4라는 주소값을 가리키라는 의미이고,

*b = 4 ; << 이것은 현재 어디인지는 모르지만 b가 가리키고있는 공간에 4라는 값을 대입해라.. 라는 뜻이 된다...

일단 이 부분은 넘어가고,

선언된 a, b를 생각했을 때..

b = a ; 라고 적으면 당연히 의도하지 않은 값이 들어간다... 위의 b = 4 와 같은 의미가 되기 때문이다..

이때는

b = &a ; 이렇게 b에 a의 주소값.. 을 넣어줘야 제대로 동작한다...

이렇게 만들게 되면,

a = 4 ; 이렇게 a 에 값을 넣었을 때, *b를 출력해보면 4가 나온다.. 왜냐하면 b가 현재 가리키고 있는 주소는..

a 이기 때문이다..


이렇게 적고 보니, 굳이 왜 쓰는지 이해가 안될 수도 있지만,

참조의 개념에서 보면 정말 꼭 필요한 기능이다..

포인터 헷갈리지 말고 잘 써야할 녀석이다...


Posted by 바람처럼..
|

입력 받은 화면으로 무언가 처리를 하기 앞서서.. 가장 필요한 것이 이진화 인 것 같다...

물론 이진화 없이 바로 처리를 하여도 되지만, 이진화를 하지않고 바로 처리를 하게 된다면,

예상되는 조건들이 너무 많아진다...

이 모든 것들이 이진화를 함으로 해서 흑이냐 백이냐만 판단하면 된다...

(물론, 이진화를 하면서 잃는 부분도 있겠지만, 장점이 더 큰 듯하다. )

일단 기본적인 셋팅은 웹캠 입력과 동일하고 이진화 함수만 추가하였다...

이전 포스팅 보기.

이진화 함수는..

cvThreshold(const CvArr*  src, CvArr*  dst, double  threshold, double  max_value, int threshold_type) 라는 함수이다..

파라미터의 순서대로 이진화를 시킬 영상, 이진화가 된 후 저장될 영상, 경계가 되는 수치값, 그 것을 넘은 수치를 설정하는 값,

그리고 마지막으로 이진화 방식을 설정한다...

이진화 방식은 CV_THRESH_OTSU 으로 많이 하는데.. 그 외에도

#define CV_THRESH_BINARY      0  /* value = value > threshold ? max_value : 0       */
#define CV_THRESH_BINARY_INV  1  /* value = value > threshold ? 0 : max_value       */
#define CV_THRESH_TRUNC       2  /* value = value > threshold ? threshold : value   */
#define CV_THRESH_TOZERO      3  /* value = value > threshold ? value : 0           */
#define CV_THRESH_TOZERO_INV  4  /* value = value > threshold ? 0 : value           */
#define CV_THRESH_MASK        7

이런 정의가 더 있다...

그리고, 이진화를 하려면 단일 채널을 만든 후, gray 변환을 해야한다...

지금처럼 RGB를 모두 추가하여 실행을 하면, 프로그램이 죽는다 ;;;

그래서 간단히 gray라는 이미지 공간을 하나 더 만들고,

gray =  cvCreateImage( cvSize(640,480), 8 , 1) ; 로 이미지를 만든다..

여기서 사이즈는 받아올 이미지와 같아야 하고, 뒤의 1은 채널 수 인데 1로 설정한 건 단일 채널이란 뜻이다. 그다음.

cvCvtColor( const CvArr* src, CvArr* dst, int code ); 이 함수를 통하여, 색 공간을 변형 시켜 주면된다...

마지막 code는 F12로 따라가 보면, 많은 종류가 있는데 오늘은 이진화기 때문에, CV_BGR2GRAY 를 이용한다.

그렇게 해서 컴파일하면...


이런 화면이 나온다... 왼쪽이 원본 화면, 오른쪽이 이진화 화면이다.. 조명의 영향에 따라서..

또 색이 쉽게 바뀌는데 이것은 전혀 처리를 안한 것이기 때문에 또 여러가지 처리를 통해서 노이즈 제거라든지, 이런게 필요하다..

아래의 사진이 오늘 추가된 소스의 전부이다...



자세한 설명은 본문에 있기 때문에, 생략하고, 사진이 안보이면 클릭하면 크게 보인다...


Posted by 바람처럼..
|

흠.. 작업환경이 윈도우 7이고, 2010을 쓰며 OpenCV 2.2를 사용하여 작업을 하려 했는데...

큰 걸림돌이 발생했다...............

기본 적인 웹캠 입력 소스를 작성하였는데, 웹캠이 전혀 아무 동작을 안하는 것이다..

웹캠은 ms hd-3000을 이용했었는데,

드라이버 설정이 잘못 된 것인지, 아예 값도 안들어 오는 것이였다....

한참을 붙잡고 있다가, 불현듯 스치는 생각이 혹시 윈도우 7 때문이였을까 하여, 같은 소스를 릴리즈로 뽑아...

xp를 쓰고 있는 다른 회사 사람 컴퓨터에서 실행했더니... 된다 - _-;;;;;;

흠.. 그래서 openCV 카페부터 구글 까지 쭉 검색해 봤더니..

의외로.. 이런 경우가 많단다 ;;;

이유는 정확히 모르지만, 윈도우의 종류나, 웹캠의 종류나, 쓰는 소스의 방식 등의 문제들로 호환이 잘 안된다고

한다..

어쩃든 웹캠은 입력 받아야 하니... 부랴부랴 directshow를 이용해서 입력받았다...

그 이유도 카페의 한 글에서 경력자이신 듯한 분께서, openCV를 통한 입력은 여러가지 호환 문제가 있으니,

기본적인 입력은 directshow를 이용하면 좋다... 고 하는 글을 보았다...

혹시 이 문제가 생기신 분들은 안정성을 위해서라도 directshow로 입력받고 후처리에서 openCV를 쓰시는 방법을 추천한다..

directshow로 입력 받는 방법은

다른 포스팅 글에서 클래스를 추가해뒀으니, 그것만 받고 기본 예제만 실행해 보시면 금방 할 수 있을 듯 하다....
Posted by 바람처럼..
|


설치 방법 및 셋팅은 이전 포스트 참고.

이제 설치도 끝났고, 셋팅도 끝났으면 openCV를 이용하여 영상을 입력 받으면 된다...

#include <cv.h>
#include <highgui.h>

void main()
{
         IplImage* image = 0; //openCV에서 사용되는 자료형이다.
 
         CvCapture* capture = cvCaptureFromCAM(0); //현재 인식된 웹캠을 찾고,
         cvNamedWindow( "OpenCvCamtest", 0 ); // 화면을 그려줄 윈도우를 생성한다.

         cvResizeWindow( "OpenCvCamtest", 640, 480 ); // 사이즈를 조절한다.(lpIImage를 할당하면서도 조절가능)

         while(1) {
         cvGrabFrame( capture );
         image = cvRetrieveFrame( capture ); // 현재 인식된 장면을 받아오고image에 넣는다.

         cvShowImage( "OpenCvCamtest", image ); // image에 있는 장면을 윈도우에 그린다.

         if( cvWaitKey(10) >= 0 ) // 이게 가장 중요한데 이 WaitKey 함수가 없으면 아무 것도 안그린다.
                break;
         }

         cvReleaseCapture( &capture ); // 할당받았던 웹캠을 해제하고,
         cvDestroyWindow( "OpenCvCamtest" ); // 윈도우를 종료한다.
}

이게 기본적인 소스이다..

위의 소스를 입력하면 바로,


위와 같은 화면이 나온다...

일단 아무 처리도 하지 않은 상태의 캡쳐된 화면이 나온다...

소스는 대충 주석이 달린 의미대로 생각하면 되고,

위에 중요하다고 적은 cvWaitKey()에 대해서 알아보면,

파라미터로 들어가는 숫자는 밀리세컨드 시간으로 10 이면 0.01초를 말한다.

이 시간동안 키 입력 값이 없으면 진행된다는 의미로 보면된다..

그 시간 내에 키가 입력되면 true가 되면서 break 문을 만나 프로그램이 종료된다...

이제 openCV 걸음마 수준인데.. 점점 한가지씩 영상처리 기법들을 알아가며, 최종적으로는..

크로마키 배경 없이 배경 제거를 하는 정도를 목표로 꾸준히 공부해 가야겠다...

Posted by 바람처럼..
|

얼마전부터 영상처리쪽 관련하여 작업을 좀 하다보니,

흠.. 조금이라도 깊이 파고 들어 공부를 해볼 필요성이 있다고 생각이 되었다...

회사에서는 2010에 2.2 버전을 사용했지만,

머 딱히 집에서 공부용이라, 2008에 2.1 버전으로 설치하였다...

머 일단은 웹캠이 필요하니, 인터넷으로 저가형 웹캠을 하나 구입하였다...

먼저 설치 방법은,

다른 분들은 컴파일을 하고 파일을 복사하고, 이런 방법을 쓰던데..

나는 그냥 편하게 설치 파일을 가지고 설치하였다..


설치 파일 링크

위의 경로로 가면 바로 설치 파일 다운이 된다....

먼저 받은 실행파일을 실행하고, 다음 다음 해서 설치를 끝내면 기본 경로는 c:\ 에 openCV 2.1 폴더에 설치가 된다...

이 것을 기준으로 설명하면..

먼저 프로젝트를 생성한다.

콘솔로 생성하고 빈 프로젝트로 만든다...

그리고 사진이 안보이면 클릭하면 크게 보임.



위와 같이 옵션 창에서 포함 파일에 경로를 추가하고,


라이브러리도 똑같이 추가한다.

 


그리고 프로젝트 -> 속성 -> 링커 -> 입력 -> 추가 종속성에 

cv210d.lib highgui210d.lib 2개의 라이브러리를 추가한다. 

뒤에 d 가 붙은 lib 파일은 디버그용 릴리즈용은 d를 제거하면 된다...

그리고 프로젝트의 폴더 안에 cv210d.dll highgui210d.dll cxcore210d.dll 를 추가한다. 혹은 system32 폴더에 추가하면 된다..

이렇게 하면 기본적인 셋팅은 끝난다.

이제 소스만 추가하면 영상처리의 첫 발을 내딛을 수 있다...


Posted by 바람처럼..
|


얼마전에 분명히 문제 없으리라 생각하고 넣은 코드가...

분명히 조건도 다 맞는데 오작동을 하였다.....

흠.. 환장하겠네.. 라고 하는 순간.. 응?? if 문의 조건에.. == 이 아니라.. = 만 있었다....

허허.. 이 것참.. 내 실수이니 뭐라 할 수는 없지만.. 정말 짜증나는 상황이다...

그래서 이럴 경우를 대비해 조건문의 순서를 바꿔쓰면된다...

예전에 어느 블로그에서 본 글이였는데..

예를들어서 a == 0 이라는 조건이 있었는데..

실수로 a = 0 이라고 쓴다면, 머.. 간단한 코드 사이에 껴있다면 금방찾지만, 복잡한 코드속에 묻혀 있다면

정말 찾기 어려울 것이다.. 이럴 때.. 0 == a 라고 쓰는 습관을 들인다면,

0 == a 는 제대로 동작하지만 0 = a 는 에러를 발생 시킨다.. 0이라는 상수에 a를 대입할 수는 없으니 말이다...

이런 식으로 순서를 바꾼다면, 이런 간단한 실수에 시간을 잡아 먹지는 않을 것이다...

물론 처음에 익숙치 않으면 익숙해 지는데 시간이 걸리고, 혹 다른 사람과 같이 소스를 공유하며 작업하는

프로젝트에서는 소스의 가독성이 떨어질 지도 모른다..

하지만 이런 습관들이 디버깅에 드는 시간을 줄여 줄 수 있다면, 작성할 때 한번 쯤 고려해 보는 것도 나쁘지 않을 것이다.
Posted by 바람처럼..
|


 간단한 프로그램을 만든다면, 솔직히 디버깅 모드로 몇번만 코드를 훓으면, 버그를 찾아내기는 어렵지 않다.

하지만 점점 더 프로그램이 커져 나갈 수록, 우리는 간단하다고 생각했던 부분의 치명적인 실수로 프로그램이 연산도중

죽어버리는 상황에 맞닥드리게 된다....

머.. 운이 좋아서 한번에 디버깅 모드로 찾아 갈 수도 있지만, 매번 그렇게 되리라는 보장은 없다...

그럴 때 만약 절때 계산에서 들어가지 말아야 할 조건이 있다면 쓸 수있는 assert() 함수를 이용해 보자...

기본적으로 assert() 함수는 디버그 모드에서 동작하는 디버깅용 함수라고 생각하면 되는데...

assert(a == 0 ) 처럼.. 뒤에 조건을 넣으면 된다...

assert를 사용하기 위해서는 #include <assert.h> 헤더를 추가하고 필요한 부분에서 사용하면 된다..

이 assert가 유용하게 쓰이는 경우가 바로 위의 예 처럼, 0이 절때 들어가서는 안되는 경우나 또 절때 2개의 값이 같을 수 없는

경우(배열의 크기는 10인데 10이 들어간다든지 하는..) 그럴때 유용하다.

assert의 조건문에 걸릴 경우 메세지 박스가 뜨며 어떤 파일, 몇번쨰 줄에서 떳는지 바로 기록해 주기 때문에..

유용하게 사용할 수 있다.
Posted by 바람처럼..
|


메세지 박스 만들기.

윈도우 프로그램에서 간단한 메세지 박스를 띄우는 것은 참 쉽다..

함수 하나만 호출하면 되는데 함수 원형은..

int MessageBox(HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType) ;

첫 파라미터 hWnd는 현재 윈도우의 핸들을 넣으면 되고,

lpText에는 메세지 박스에 출력할 문자열,

lpCaption 에는 메세지 박스 타이틀 바에 제목 문자열,

그리고 마지막 파라미터는 메세지 박스에 어떤 종류의 버튼이 나타날 것인가를 지정하는 플래그가 들어간다.

그 각 플래그 값들은..

MB_ABORTRETRYIGNORE : Abort, Retry, Ignore 세 개의 버튼이 나타난다.

MB_OK : OK 버튼 하나만 나타난다.

MB_OKCANCEL : OK버튼 과 CANCEL 버튼이 나타난다.

MB_RETRYCANCEL : Retry, Cancle 두 개의 버튼이 나타난다.

MB_YESNO : Yes, NO 두 개의 버튼이 나타난다.

MB_YESNOCANCEL : YES, NO, CANCEL 세 개의 버튼이 나타난다.

또 각 메세지 박스의 버튼이 클릭 되면 클릭된 상태에 따라 여러가지 리턴 값이 날라오는데,

그 값을 확인하고 처리를 해 주면 된다..

IDABORT : Abort 버튼을 눌렀을 때.

IDCANCEL : Cancel 버튼을 눌렀을 때.

IDIGNORE : Ignore 버튼을 눌렀을 때.

IDNO : No 버튼을 눌렀다.

IDOK : OK 버튼을 눌렀다.

IDRETRY : Retry 버튼을 눌렀다.

IDYES : Yes 버튼을 눌렀다.

Posted by 바람처럼..
|

앞선 문자와 마찬가지로 여러 가지 출력들에 대하여 알아보자...

먼저 픽셀 출력 법이다..

함수의 원형은

COLORREF SetPixel( hdc, nXPos, nYPos, clrref )

이다.

일단 지금부터 보는 출력용 함수들의 첫번째 인자는 모두 hdc를 필요로 한다.

hdc를 얻는 방법은 바로 앞 포스트를 참조한다.

위의 SetPixel 함수는 x,y 좌표에 마지막 인수로 받은 색정보를 가지고 픽셀을 찍는다..

보통 찍을 때 마다 색 정보가 다르므로 이런 방식을 쓰지만 윈도우 프로그래밍에서는 잘 쓰지 않는다.

선을 긋고 싶으면,

DWORD MoveToEx(hdc, x, y, lpPoint )
BOOL LineTo(hdc, xEnd, yEnd )

를 사용한다.

이 2가지 함수는 먼저 선의 시작점으로 MoveToEx 함수를 이용하여 점을 이동 시킨 후

끝점의 좌표를 LineTo 함수의 x, y 에 넣어주면된다.

즉, MoveToEx(hdc, 100, 100, NULL) ;
LineTo(hdc, 150, 100 ) ;

이런 함수를 만든다면, 이것은 100, 100 에서 150, 100 을 잇는 선을 긋는다.

사각형과 원을 그리는 것은 함수의 형태는 동일하다..
함수의 원형은

BOOL Rectangle(hdc, nLeftRect, nTopRect, nRightRect, nBottomRect )
BOOL Ellipse(hdc, nLeftRect, nTopRect, nRightRect, nBottomRect )

이런 형태이다.

2가지 모두 4점의 좌표를 입력 받는데 사각형은 그 첫번째 x, y (Left, Top) 값과 두번째 x1, y1 ( Right, Bottom) 값을

가지고 사각형을 그리며, 원은 그 사각형에 내접하는 타원을 그린다.
Posted by 바람처럼..
|


DC를 이용해서 문자열 출력을 알아보자...

먼저 문자열을 출력 하기 위해서는

HDC hdc ;
hdc =  GetDC(hWnd) ; //  여기서 hWnd는 처음 윈도우를 만들면서 받아놓은 현재윈도우의 핸들이다.

TextOut(hdc, 100, 100, TEXT("Test"), 4) ;
// TextOut함수는 먼저 hdc를 적고, x, y 좌표, 텍스트 내용, 그리고 텍스트의 길이 순으로 입력한다.

ReleaseCD(hWnd, hdc) ;
//그리고 다 했으면 해제를 해준다...

이것은.. 가장 간단한 문자열 출력 방법이다...

하지만 이 코드로 글자를 찍으면, 윈도우가 다른 윈도우에 가려졌거나, 윈도우의 크기 변경 등이 있다면,

바로 다시 사라져 버린다...

그걸 방지하기 위해서는 항상 새로 그려 줘야 하므로, 루프 내부에 코드를 삽입하거나, WM_PAINT 메세지

안에 코드를 삽입하면 항상 글자가 출력되도록 할 수 있다.
(문자열 출력 방법)

Posted by 바람처럼..
|


스샷을 저장할 일이 생겨서, 스샷을 저장하려다 보니.. 각 찍힌 시간을 파일 명으로 만들고 싶어졌다....

생각보다 api 함수를 이용하면 시간을 얻기는 간단하다.

일단..

<time.h> 를 선언한다. 아래의 함수들은 각 시간을 얻는 함수들 이다.

_strtime : 현재 시간을 문자열로 만들어주는 함수이다. 함수원형은..

char* _strtime( char *time );

이다.

char time[9];

_strtime( time );
// 11:23:24 (시:분:초)형식이며, 24시간으로 표시된다.

_strdate : 오늘 날짜를 문자열로 만들어 주는 함수이다. 함수 원형은..

char* _strdate( char *date );

이다..

char data [9];

_strdate( data );
// 08/05/11 (월/일/년)형식으로 표시된다.

strftime : 이 함수는 사용자가 지정한 형식대로 현재시간을 문자열로 출력하는 함수이다. 함수 원형은..

size_t strftime( char *strDest, size_t maxsize, const char *format, const struct tm *timeptr );

이다..

time_t cur;
struct tm* ptm;
char buf[100] = {0};

cur = time(NULL);
ptm = localtime(&cur);

strftime(buf, sizeof(buf), "%c", ptm);
//08/05/11 11:30:05
strftime(buf, sizeof(buf), "%m/%d/%y %H:%M:%S", ptm);
//08/05/11 11:30:05
strftime(buf, sizeof(buf), "%Y년 %#m월 %#d일 %#I시 %#M분 %#S초", ptm);
//2011년 08월 05일 11시 30분 5초
strftime(buf, sizeof(buf), "%I:%M %p", ptm);
//11:30 AM

// 위 함수를 사용하기 위해서는 localtime(...); 함수로 시간을 우선 얻어와야 한다.

Posted by 바람처럼..
|

윈도우 화면에 무엇인가를 출력하려면, 항상 꼭 필요한 것이 이 DC이다.

보통 처음 프로그래밍을 배울 때는 항상 그냥 DC가 필요하니 GetDC를 해서 쓰고, 릴리즈 하고.. 머 이런식으로 DC 자체에 대

해서는 쉽게 그냥 지나치기 쉽다.

하지만 무언가를 사용하기 위해서는, 그 것의 원리를 이해해야 머리속에 오래 남아있는 법이니.. DC에 대해서 알아보자..

 일단 윈도우는 크게 세 가지 동적 라이브러리로 구성되어 있는데, 메모리를 관리하고 프로그램을 실행시키는 KERNEL, 유저

인터페이스와 윈도우를 관리하는 USER, 화면 처리와 그래픽을 담당하는 GDI가 있다... API 함수들의 대부분은 이 세 가지

DLL에 의해 제공 된다.
 
 이 중에 출력을 하려면 우리는 GDI(Graphic Device Interface ) 모듈을 이용해야 한다.

 DC(Device Context ) 란 출력에 필요한 모든 정보를 가지는 데이터 구조체이며, GDI 모듈에 의해 관리된다.

그렇다면 DC는 왜 필요한 것일까??

상황을 가지고 생각하면, 쉽게 정리가 된다.... 선을 하나 그릴려고 하면.. 점 2개가 있으면 선을 그을 수 있다... 하지만... 잘 생

각해보면, 좌표값 2개도 중요하지만, 선의 색, 굵기,모양, 혹은 점선이라던지 여러가지 방법 등등, 속성 값을 넣자면, 하나둘이

아니다.. 이럴 때 DC를 이용하면, 기본적으로 DC에 default값이 저장되어 있기 떄문에, DC와 좌표 2개면 충분해진다. 혹시 바

꾸고 싶은 속성이 있다면 얻어온 DC에서 바꾸고 싶은 부분만 바꾸면 충분하다.

또 DC에는 현재 윈도우의 값도 가지고 있다... 이것이 제일 중요한 부분인데.. 아까 라인을 예로 들었는데, 좌표 2개를 줬다고

치면, 0,50 에서 100, 150 까지 가는 선을 그리고 싶었는데 그것이 화면 좌표를 기준으로 0,50 부터 100, 150인지 현재 윈도우의

클라이언트 영역의 0,0을 기준으로 하는지를 모르면, 잘못된 값이 나올 수 있다. 하지만 DC는 현재 윈도우의 DC를 보통 Get

해오기 때문에 자동으로 현재 윈도우를 기준으로 좌표를 설정하여 그려준다.

 또 클리핑 영역(윈도우가 2개이상 겹쳤을 때 상위에 가려진 하위 부분은 그릴 필요가 없다. 그 영역을 말함 ) 을 자동으로 계산

해 주어, 뒷 영역의 윈도우의 그림이 앞 윈도우를 침범하지 못하도록 해 준다....

 흔히 에이 윈도우는 당연히 뒤에 그림이 가려지지 라고 생각한다면... 그것은 다 윈도우를 프로그램한 프로그래머가 그렇게 만

들어 두었기 때문이지.. 원래 그렇지는 않다. 화면에 찍히는 랜더링 장면들은 순서에 영향을 받기 때문에 이런 처리가 없다면,

당황스러운 결과가 나올 수 도 있다..

어쨌든 정리해보면,, DC는 위와 같은 역할들을 하고 또 위와 같은 정보들을 가지기 때문에, 선을 그린다던지 도형을 그린다던

지 하는 함수의 제일 첫 인자는 항상 DC를 받아와서 쓴다....

'프로그래밍 > API' 카테고리의 다른 글

[API] DC 를 이용한 문자열 출력  (0) 2011.08.07
[API] 다양한 현재시간과 현재날짜 얻기  (1) 2011.08.05
[API] 커서 바꾸기..  (0) 2011.08.03
[API] 메시지 루프  (0) 2011.08.02
[API] 윈도우 생성..  (0) 2011.08.02
Posted by 바람처럼..
|