꽤 재미난 툴 인거 같습니다. 써보세용 ㅋ

키젠을 알고 싶으면 쪽지나 이멜 주세용^^;

전치 연산자를 다음과 같이 함수를 선언함으로써 중첩시킬 수 있습니다.

반환형 operator op (매개변수)

여기서 op는 중첩시킬 연산자 입니다. 따라서 ++ 연산자를 다음과 같은 모양으로
void operator++ ()

// 전치연산자(PreFix Operator)를 중첩(Overloading) 시키기.
#include <iostream>
using namespace std;

typedef unsigned short USHORT;
class Counter
{
public:
 Counter();
 ~Counter() {}
 USHORT GetItsVal() const { return itsVal; }
 void SetItsVal(USHORT x) { itsVal = x; }
 void Increment() { ++itsVal; }
 void operator++ () { ++itsVal; } // 재정의하고 싶은 연산자

private:
 USHORT itsVal;
};

// 디폴트 생성자 초기값 0
Counter::Counter():itsVal(0) {};
 
int main()
{
 Counter i;
 cout << "The value of i is " << i.GetItsVal() << endl;
 i.Increment();
 cout << "The value of i is " << i.GetItsVal() << endl;
 ++i;
 cout << "The value of i is " << i.GetItsVal() << endl;

 return 0;

}

함따라 봤는데 진짜 신기한군요.

포인터 : 포인터란 메모리 주소를 가리킬 수 있는 변수

포인터를 이해하기 위해서는 여러분은 컴퓨터의 메모리에 대해 조금 알아야 합니다. 컴퓨터의 메모리는 그 위치를 가리키는 연속적인 번호가 붙여진

부분으로 나누어질 수 있습니다. 각 변수는 메모리의 각 유일한(주소라고 함)을 가리키고 있습니다.

 

#include <iostream>

using namespace std;

 

int main()

{

    unsigned short shortVar = 5;

    unsigned long longVar = 65535;

    long sVar = -65535;

 

    cout << "shortVar:" << shortVar << "\t";

    cout << "Address of shortVar:";

    cout << &shortVar << "\n";

 

    cout << "longVar:" << longVar << "\t";

    cout << "Address of shortVar:";

    cout << &longVar << "\n";

 

cout << "sVar:";

    cout << sVar << "\t Address of sVar:";

    cout << &sVar << "\n";

 

    return 0;

 

}

 

 

여러분이 각 변수의 실제의 주소를 숫자로 알아야 할 이유는 없습니다. 중요한 것은 각 변수는 자신의 주소를 갖고 또 그 변수의 형만큼의 길이를

갖는다는 것입니다. 여러분은 변수형을 선언함으로써 컴파일러에게 그 변수형에 해당하는 길이 만큼의 메모리를 잡아 놓을 것을 말해줍니다.

컴파일러는 자동적으로 그 변수의 주소를 할당합니다. 예를 들어, long 정수형은 4바이트이고 이는 메모리에서 4바이트를 차지한다는 의미입니다.

 

포인터에 주소를 저장

모든 변수는 주소를 갖고 있습니다. 주어진 변수를 몰라도 여러분은 주소를 포인터에 저장할 수가 있습니다. 예를 들어, howOld가 정수형이라고 합

시다. howOld의 주소를 저장할 포인터를 pAge라 하면 여러분은 다음과 같이 쓸 수 있습니다.

Int *pAge = 0;

이 선언은 pAge가 정수형의 변수 주소를 가리키는(정수형의 포인터라고 한다.) 변수라는 뜻입니다. 다시 말해 pAge는 정수형의 주소를 가지도록

선언되었습니다.

pAge는 변수 입니다. 여러분은 정수형 변수를 선언하면 그 변수에 저장되는 내용은 정수로 해석됩니다. 여러분이 pAge를 포인터로 선언했으면

pAge에 저장되는 내용은 주소로 해석됩니다. pAge는 변수는 변수이되 그 내용이 약간 다를 뿐입니다.

여기의 예에서 pAge는 0으로 초기화되어 있습니다. 그 값이 0인 포인터를 널 포인터라고 합니다. 모든 포인터는 그들이 생겨날 때 어떤 값으로 초기

화가 됩니다. 포인터에 어떤 값을 초기화시켜야 할지 모르겠거든 0으로 초기화합시다. 초기화 되지 않은 포인터를 와일드(wild) 포인터라고 합니다.

♨ 주의 포인터는 항상 초기화 합시다.

여러분이 포인터를 0으로 초기화했다면 언젠가는 howOld의 주소를 pAge에 저장해야 합니다. 다음과 같이 저장합니다.

Unsigned short int howOld = 50; // 변수를 만듦

Unsigned short int *pAge = 0; // 포인터 만듦

pAge = &howOld; // howOld의 주소를 pAge에 저장

줄여서 unsigned short int *pAge = &howOld; // 포인터르 만듦

이제 pAge는 howOld의 주소를 저장하는 포인터입니다. pAge를 사용하여 여러분은 실제적으로 howOld의 값을 알 수 있습니다.

포인터 pAge를 사용하여 howOld에 접근하는 것을 간접 지정(indirection)이라 합니다. 왜냐하면 pAge를 경유하여 howOld로 접근하기 때문입니다.

간접지정 : 포인터에 지정된 주소값을 통해 원래의 변수에 접근하는 방식을 의미합니다. 포인터는 그 안에 지정된 주소값을 통해 그 주소에 저장된

값을 알 수 있는 간접적인 방식을 제공합니다.

 

포인터 이름

포인터는 일반 C++에서 가지는 변수명을 가질 수 있습니다. 이 책에서는 일단 포인터는 p로 시작하는 작명방식을 쓰고자 합니다.

 

간접 지정 연산자

간접 지정(indirection) 연산자(*)는 조회 연산자(dereference)라고도 부릅니다. 포인터가 조회되었을 때 그 포인터에 저장된 주소에 있는 값이 검색됩

니다. 일반적인 변수들은 그 값에 직접 접근 방식을 제공합니다. 가령 여러분이 yourAge라 불리는 unsigned short 정수형을 선언했다면, 여러분은

howOld의 값을 그 새로운 변수에 저장할 수 있습니다.

Unsigned short int yourAge;

yourAge = howOld;

포인터에는 원하는 값의 주소가 저장되어 있으므로 간접 지정 방식을 통해 접근합니다. 포인터 pAge를 통해 yourAge에 howOld 값을 저장하려면 다

음과 같이 합니다.

Unsigned short int yourAge;

yourAge = *pAge;

pAge 앞에 붙어있는 간접 지정 연산자(*)는 '이 주소에 저장된 값'을 의미합니다. 따라서 둘째 줄은 다음과 같이 해석됩니다. 'pAge에 저장된 값(이는

주소를 가리킴)을 가져와, 이 주소가 가리키는 곳으로 찾아가 저장된 값을 찾아 yourAge에 저장합니다.'

*pAge = 5; // 값 5를 pAge 안에 지정된 주소 위치에 저장하라.

 

포인터, 주소, 변수

포인터라는 변수

그 포인터 변수에 저장된 주소값

그 포인터 변수에 저장된 주소값이 가리키는 메모리를 찾아가면 저장되어 있는 실제값

Int theVariable = 5;

Int *pPointer = &theVariable;

theVariable은 5로 초기화된 정수형 값입니다.

pPointer는 정수형 값이 저장되는 주소를 저장하는 포인터입니다.

 

포인터를 사용한 자료 연산

포인터가 변수의 주소값을 할당받았다면, 그 변수의 자료에 접근하기 위해 여러분은 포인터를 쓸 수가 있습니다.

 

 

#include <iostream>

using namespace std;

typedef unsigned short int USHORT;

 

int main()

{

    USHORT myAge; // 변수

    USHORT *pAge = 0; // 포인터

myAge = 5;

        

    cout << "myAge:" << myAge << "\n";

 

    pAge = &myAge;

 

    cout << "*pAge:" << *pAge << "\n\n";

    cout << "*pAge = 7 \n";

 

    *pAge = 7; // sets myAge to 7

 

    cout << "*pAge:" << *pAge << "\n";

    cout << "myAge:" << myAge << "\n\n";

 

    cout << "myAge = 9\n";

 

    myAge = 9;

 

    cout << "myAge:" << myAge << "\n";

    cout << "*pAge:" << *pAge << "\n\n";

    

    return 0;

 

}

 

 

 

 

주소 관찰

포인터는 여러분이 주소를 실제 그 주소를 정확히 알지 못하더라도 연산할 수 있게 해줍니다. 오늘 이후로 포인터에 변수의 주소를 대입하면 그 주소

가 대입이 되고 그 포인터는 그 변수의 주소를 가지고 있다는 것을 자신 있게 알게 될 것입니다.

 

포인터가 가리키는 주소에 저장된 값에 접근하려면 간접 지정 연산자(*)를 씁시다.

모든 포인터를 유효한 주소값이나 널(0)로 초기화합시다.

포인터에 저장된 주소값과 그 주소의 메모리에 저장된 값을 구별할 줄 압시다.

 

포인터를 선언하려면 저장되는 주소가 가리키는 값의 변수형 또는 객체형을 쓰고, 포인터 연산자를 쓰며(*) 그 포인터의 이름을 씁시다.

Unsigned short int *pPointer = 0;

포인터에 값을 저장하거나 초기화할 때 저장하고자하는 주소의 변수명 앞에 주소 연산자(&)를 붙여 대입합니다.

Unsigned short int *pPointer = &theVariable;

포인터를 소환하려면 소환연산자(*)를 포인터 앞에 붙입니다.

Unsigned short int theVariable = *pPointer;

 

도대체 왜 포인터를 쓰는가?

 

여태껏 여러분은 변수의 주소를 가리키는 포인터에 대해 자세히 배웠습니다. 그런데 여러분은 이렇게 복잡한 방식이 맘에 안들 것입니다. 왜 값에

직접 접근할 수 있는 변수가 있는데 포인터를 쓰는 것일까요?

 

포인터 용도

- 자유 기억 장소의 자료를 다룰 때

- 클래스 멤버 자료와 클래스 멤버 함수에 접근할 때

- 함수에 주소 전달을 할 때

 

스택과 자유 기억 공간(Free Stor)

- 전역 공간

- 자유 기억 공간

- 레지스터

- 코드 영역

- 스택

 

지역 변수는 함수의 매개 변수와 함께 스택에 저장됩니다. 코드는 코드 영역에 저장되고 전역 변수는 전역 공간에 저장됩니다. 레지스터는 여러 잡다

한 일들을 합니다.(예를 들어, 스택의 꼭대기와 명령 포인터를 계속 추적하는) 그리고 나머지의 메모리는 자유 기억 공간(free store)로 분류되는데 힙

(heap)이라고 불립니다.

 

지역 변수의 문제점은 영원히 지속되지 않는다는 것입니다. 함수가 반환되면 지역 변수들은 버려집니다. 또한 전역 변수들은 그 문제를 해결했지만

프로그램의 모든 부분에서 제한 없이 접근할 수 있다는 대가를 치러야 합니다.

 

전화기에는 메모리 기능이 있습니다. 김군 집의 전화번호는 1번 단추에, 이군 집의 전화번호는 2번 단추에 기억을 시켜 넣는 것입니다. 이렇게 하면

여러분은 친구 집에 전화를 걸려고 일일이 그 번호를 다 입력을 하지 않아도 됩니다. 그 전화번호는 어디 구석에 넣어둡니다. 여러분이 번호 하나만

누르면 전화가 알아서 찾아가 줍니다. 그 자세한 전화번호는 무엇인지 모릅니다. 하지만 그 집에 통화는 할 수 있습니다.

여러분은 컴퓨터의 메모리에 주소를 통해 접근할 수 있습니다. 이는 실제 전화 번호 입니다. 하지만 여러분은 실제 메모리의 주소를 몰라도 됩니다.

단지 포인터에 잡아넣으면 됩니다. 특정 번호만 누르면 됩니다. 포인터는 여러분이 여러분의 자료에 자세한 주소를 몰라도 접근할 수 있게 해줍니다.

 

포인터는 간접 접근 방식으로 자료에 접근할 때 강력한 도구를 제공합니다. 모든 변수는 주소를 가지고 있는데 이는 주소 연산자 &를 통해 얻을 수

있습니다. 이 주소가 포인터에 저장됩니다.

포인터는 그 포인터가 가리키는 객체의 형을 쓰고 간접 연산자(*)를 붙인 뒤 포인터의 이름을 써서 완성시킵니다.

포인터는 객체, 또는 널을 가리키게 하여 초기화시킵니다.

간접 연산자(*)를 사용하여 포인터에 저장된 주소값을 접근합니다.

     상수형 포인터를 선언하여 그 포인터에 다른 객체의 주소를 대입할 수 없게 하고 또 상수형 객체의 포인터를 선언하여 포인터가 가리키는 값을 바꿀

수 없게 할 수 있습니다. 힙에 new 객체를 만들어 포인터에 그 값을 대입할 수 있습니다. Delete 예약어를 호출하여 메모리를 풀어놓을 수 있습니다.

Delte는 메모리를 풀어놓지만 그 포인터를 파괴하지는 않습니다. 따라서 다른 주소값을 그 포인터에 저장할 수 있습니다.

 

간만에 포인터에 대해서 함 공부하면서 정리한 내용들은데 역시 포인터는 잘쓰면 해가 되고 잘못사용 독이 된다는 사실을 알았습니다…

  1. 배열

    배열은 동일한 형식의 데이터 메모리 상의 연속된 공간에 저장하는 구조를 말합니다.

    장점 : 데이터를 처음부터 끝까지 차례로 처리하는 등의 일을 매우 빠른 속도로 수행할 수 있다는 장점이 있습니다.

    단점 : 배열은 저장 공간이 한정되기 때문에 크기를 늘리거나 중간에 데이터를 삽입하기가 어렵습니다.

    예를들어 100개의 배열을 잡아놓고 사용하다가 101번째 데이터를 저장하려면 101개의 데이터가 들어갈만한 영역에 배열을 새로 잡아야 한다.

     

  2. 리스트

    리스트는 하나하나가 따로 저장됩니다. 데이터들의 저장위치가 포인터로 줄줄이 연결됩니다.

    노드 : 데이터가 저장되는 공간

    링크 : 노드를 연결하는 포인터

    모든 노드가 포인터로 연결되어 있기 때문에 첫번째 노드의 포인터만 기억하고 있으면, 리스트에 저장된 모든 노드를 찾아갈 수 있다.

    데이터의 추가, 삭제가 빈번한 응용에는 배열보다 리스트를 사용하는 것이 좋습니다.

     

  3. 맵은 디셔너리라고도 하고, 해시 테이블 이라고도 합니다. 데이터가 키 값과 함께 저장될 때 맵 구조를 사용하면 좋습니다.

    예를들어 사전처럼 어떤 단어와 그 단어의 뜻을 함께 저장한다고 할 때, 단어는 키가 되고, 그 단어의 뜻은 데이터가 됩니다.

    맵과 일반배열의 차이점

    100대의 차를 주차할 수 있는 주차장에 차를 넣고 넣고 빼는 예를 들어보겠습니다.

    여기서 주차장은 데이터를 저장할 배열이고, 자동차는 데이터, 자동차의 번호가 키 값이라고 생각하시면 됩니다.

    일반 배열 같으면

    1. 들어오는 순서대로 주차장에 넣을 겁니다.
    2. 나중에 차 주인이 차를 빼러 오면 몇 번째 칸에 주차를 했는지 인덱스를 기억하던가
    3. 자신의 차를 발견할때까지 첫번째 칸부터 차례로 찾아보야 합니다.

    맵을 이용하면

    1. 차를 주차할 때부터 키 값을 이용합니다.
    2. 주차장에 100대의 차를 주차할 수 있으니까 자동차 번호의 뒷자리 두 개를 가지고 저장될 위치를 결정하면 되겠네요.

 

  1. MFC 배열 클래스

클래스

데이터 형식

CByteArray

Byte

CDWordArray

DWORD

CObArray

CObject*

CPtrArray

Void*

CStringArray

CString

CUIntArray

UINT

CWordArray

WORD

 

  1. MFC 배열 클래스들의 주요 맴버 함수

함수

기능

SetSize

배열의 크기를 지정합니다.

GetSize

현재 배열에 저장된 항목의 개수를 반환합니다.

GetAt

지정된 인덱스에 저장된 값을 반환합니다.

SetAt

지정된 인덱스에 값을 저장합니다.

Operator[]

GetAt,SetAt 함수의 기능을 할 수 있습니다.

예) array.SetAt(3,10); -> array[3] = 10;

예) int a = array.GetAt(3); -> int a = array[3];

Add

배열의 저장된 항목을 1개 늘리고, 여기에 새로운 값을 추가합니다.

InsertAt

지정된 인덱스에 새로운 항목을 삽입합니다.

RemoveAt

지정된 인덱스의 항목을 삭제합니다.

RemoveAll

배열의 모든 항목을 삭제합니다.

 

InsetAt, RemoveAt, Add 등의 함수는 배열의 속성상 하기 힘든 일을 억지로 시키는 함수들입니다. 이 함수를 호출하는 것은 쉽지만, 그 대가는 생각보다 큽니다. 예를 들어, InsertAt 함수를 호출하여 배열의 6번째 항목에 새로운 값을 추가하도록 하면, 6번째 항목 이후의 모든 값이 뒤로 한 칸씩 밀려야 하기 때문에 배열의 크기가 큰 경우에는 메모리에서 많은 양의 데이터가 이동해야 합니다. 우리는 배열의 속성을 잘 이해하고 있으므로 InsertAt, RemoveAt, Add 함수와 같이 비용이 큰 함수는 불 필요하게 남발하지 말아야 겠습니다.

받아서 실습해 보자.

출처 : cafe.naver.com/cygwin

 

* cygwin을 windows XP에 설치한 후에 hello.c 프로그램을 컴파일 합니다.

 

hello.c 파일의 내용

=============================

1: #include<stdio.h>

2:

3: main()

4: {

5: printf("Hello,World!\n");

6: return 0;

7: }

=============================

 

모두 7줄인 이 프로그램을 컴파일하고 실행해 보겠습니다.

(줄번호 1: 2: 3: ... 7:은 실제 입력하지 않으며, 설명의 편의를 위해 붙인 것임)

 

 

먼저, hello.c 가 있는지 확인합니다. (없다면 편집기로 입력합니다.)

 

$ls

hello.c

 

이제 컴파일해 봅니다.

 

$gcc hello.c -o hello

 

컴파일이 정상적으로 완료되었으면, 실행파일을 찾아봅니다.

[-o 파일이름]은 생성되는 파일에 이름을 붙여주는 것입니다. 

 

$ls

hello.c    hello.exe

 

실행파일을 실행해 봅니다.

 

$./hello.exe

Hello,World!

 

가장 작은 C프로그램을 만들고, 실행합니다.


+ Recent posts