최근 하고 있는 것이 여러 뻘짓이다 보니 본이 아니게 3중 포인터를 쓰게 되더군요.

대략… 3차원 표현 이라던가…

2차원의 영역중, 각 픽셀의 갖고있는 간단한 값을 가져올 때, 안쓸꺼 같던것들이 자주 쓰이더군요.

일단 예제.. 여기서 예제는 int a[10][20][30] 과 같은 역활을 하는 동적 메모리 할당을 해봅니다.

일단 C언어부터…

// 만들때

int ***ptr;
ptr = (int ***)malloc(sizeof(int **) * 10);

for(i=0; i < 10; ++i)
{
    ptr[i] = (int **)malloc(sizeof(int *) * 20);   

    for(j=0; j < 20; ++j)
        ptr[i][j] = (int *)malloc(sizeof(int) * 30);
}

// 해제할때
for(i=0; i < 10; ++i)
{
    for(j=0; j < 20; ++j)
        free(ptr[i][j]);

    free(ptr[i]);
}

free(ptr);

그리고 C++, C++ 답게 new와 delete 명령어를 써서 (물론 위의 C방법으로 해도 상관없습니다.)

 

 //<!----- 3차원 배열 예제 -----//
//배열을 X,Y,Z에 각각 10,20,30 식 할당.
#define ARRAY_X  (10)
#define ARRAY_Y  (20)
#define ARRAY_Z  (30)
void main()
{
     //선언
     int ***ptr;
     //할당
     ptr = new int**[ARRAY_X];    //X차원 선언
     for(int i=0; i<ARRAY_X; ++i) {
         ptr[i] = new int *[ARRAY_Y];  //Y차원 선언
     } 
     for(int i=0; i<ARRAY_X; ++i){
         for(int j=0; j<ARRAY_Y; ++j) {
             ptr[i][j] = new int[ARRAY_Z]; //Z차원 선언
   
             for(int k=0; k<ARRAY_Z; ++k){
            //값을 씀.
                 ptr[i][j][k] = i*j*k;
             }
         }
    }
    // 해제할때, 역으로 Z부터 해제함.
    for(int i=0; i<ARRAY_X; ++i) {
        for(int j=0; j<ARRAY_Y; ++j) {
            for(int k=0; k<ARRAY_Z; ++k){
                printf("ptr[%d][%d][%d] = %d\n", i, j, k, ptr[i][j][k]);
            }
           delete [] ptr[i][j];  //Z차원 해제
        }
        printf("\n");
    }
    for(int i=0; i<ARRAY_X; ++i) {
        delete [] ptr[i];    //Y차원 해제
    }
    delete []ptr;      //X차원 해제
    return 0;
}

 마지막으로 Java ...
Java의 경우.. 포인터가 없죠;;
Java의 메모리는 C언어처럼 직접 건들이는 구조가 아니고,
물리적 메모리 공간과 별도로 여길 관리하는 메모리 관리자가 있습니다..(Virtual Machine(이하 VM)안에)
여기의 메모리 관리자가 알아서 할당합니다. (OS에서 자원 관리 하듯이라 생각하셔도 됩니다.)

즉 int a[10] 이라 하면, 내부적으로는 C언어처럼 malloc(sizeof(int) * 10)이 일어난다는거죠.
C언어적인 문법상으로 보면 그냥 정적 배열처럼 보이지만... 내부적으론 동적 배열 처리한다고 보시면 됩니다.

해제는.. 간단하게 null 해주면 VM의 메모리 관리자가 해당 영역을 없는 셈 칩니다.
(파일을 지울때 앞글자만 지워주고 없는셈 치는것과 같은 이치입니다)

완전히 메모리 상에서 지우고 싶을땐 가비지 컬렉터를 강제로 실행시켜줘야 하지만..
굳이 할필요는 없습니다.
만약 VM이 메모리 할당이 일어날때 더 이상 메모리가 없다면,
현재 사용되지 않는 메모리 할당자들,
즉 위의 null 처럼 되어있는것.. 이라던가, 클래스에서 나와 더이상 사용하지 않는 변수들 같은것들을
찾아 지워준뒤, Compaction등이 일어납니다. 그리고 빈곳에 메모리를 할당하죠.
개념은 이정도고 실제 작동원리나 자세한건 Java책을 참고하시기 바랍니다.

// 만들때
int ptr[][][] = null;
ptr = new int[10][20][30];

// 해제할때
ptr = null;
System.gc(); // 선택사항

전세계에서 제일 많이 사용하는 언어는 자바란 이유가.. 이런 편리함 때문일껍니다... T_T

C언어의 모토(motto)는
-> 어떤 머신이든 제어하는 언어를 만들자.
이지만,

Java의 모토(motto)는
-> 코딩은 1번, 코딩된 소스를 변형없이 모든 머신에서 돌아가는 프로그램을 만들자
라고 합니다.

실제... C언어는 ARM 계열이나. 리눅스의 GCC, MS의 Visual C++ ... 미묘하게 다릅니다.
int 의 경우만해도 보통은 4byte로 알고있지만, 사실은 해당 머신에서 숫자를 표현하기 적당한 공간을 뜻합니다.
무슨말이냐면
간단한 휴대폰의 경우 int의 할당은 2byte or 1byte 만 할당될수도 있고, PC에선 4byte, 서버나 과학용 컴퓨터에선 8byte 이상을 할당할수 있다는 얘기죠.
그게 무슨 대수냐..... 하실지도 모르지만.. 게임 만들다보면.. 이것까지 제어하는 부분까지.. orz

Java는.. int 는 4byte 라고 정해져 있습니다. 이건 어떤 머신이든 VM이 있다면 무족건 4byte로 쳐줍니다.

'C/C++언어' 카테고리의 다른 글

음 고정소수점 만들기..  (0) 2008.05.09
고정 소수점  (0) 2008.05.09
쉬프트 연산과 곱셈  (0) 2008.03.26
[리눅스프로그래밍] makefile  (1) 2008.03.07
ASSERT(), VERIFY(), TRACE()  (0) 2008.03.04

실전 STL 플밍.. 소스 참고..

쉬프트 연산과 곱셈


비트를 이동시키는 쉬프트 연산은 곱셈과 나눗셈의 대용으로 사용할 수 있다. 다음 예제를 실행해 보아라.

 

: shiftmulti

#include <Turboc.h>

 

void main()

{

     int i;

 

     printf("정수를 입력하세요 : ");

     scanf("%d",&i);

     printf("결과=%d\n",i << 1);

}

 

정수 i를 입력받은 후 왼쪽으로 한칸 쉬프트한 값을 출력했다. 실행해 보면 알겠지만 입력한 수의 정확하게 2배되는 값이 출력된다. 5를 입력하면 10, 100을 입력하면 200이 출력될 것이다. 정수를 왼쪽으로 한칸 쉬프트하면 두배가 되며 오른쪽으로 한칸 쉬프트하면 절반이 되는데 어째서 그런지 보자. 입력값이 5였다고 가정하면 이 값은 이진수로 0101인데 이 값을 10진수로 바꾸는 공식은 다음과 같다.

 

1*22+0*21+1*20 = 5

 

이진수의 각 자리수는 2의 거듭승에 해당하는 값을 표현한다. 그런데 비트를 왼쪽으로 한칸 이동시키면 모든 자리수의 지수가 1 올라가기 때문에 2배가 된다. 쉬프트한 결과는 1010이며 이 값은 다음과 같이 10진수로 바꿀 수 있다.

 

1*23+0*22+1*21+0*20 = 10

 

그래서 왼쪽으로 한칸 쉬프트하면 두배가 되는 것이다. 단, 용량 한계를 넘어서는 값은 잘려 나가는데 이는 어쩔 수 없다. 예를 들어 16비트 정수 40000을 왼쪽으로 쉬프트하면 80000이 되는데 16비트로는 이 값을 표현할 수 없으므로 제일 오른쪽의 1이 밀려나고 결과는 14464가 된다. 이 값은 80000-65536인데 밀려나서 버려진 오른쪽 비트의 값이 65536이기 때문이다. 32비트의 int형을 쓰면 웬만큼 큰 수라도 용량의 한계를 넘지 않을 것이다.

왼쪽으로 쉬프트하는 연산이 값을 두배로 만드는데 비해 오른쪽으로 쉬프트하는 연산은 값을 절반으로 만든다. 모든 자리수의 지수가 1 감소하기 때문이다. 이진수 1100을 오른쪽으로 쉬프트하면 0110이 되는데 이 값은 처음값 12의 절반인 6이다. 동일한 원리이므로 따로 그림까지 그릴 필요는 없을 것 같다.

오른쪽 쉬프트는 자리 넘침이 발생하지 않지만 오른쪽으로 밀려나 사라지는 값만큼 오차가 생길 수는 있다. 짝수를 밀면 정확하게 절반이 되지만 홀수를 밀면 제일 오른쪽에 있던 1이 밀려 나므로 실제값보다 0.5더 작은 값이 계산된다. 예를 들어 이진수 0111(7)을 오른쪽으로 밀면 b0가 밀려나고 0011(3)이 된다. 정확하게 계산하자면 3.5가 되어야 하지만 비트는 정수의 세계이기 때문에 밀려난 0.5는 사라지게 된다.

좌우 쉬프트 연산이 곱셈과 나눗셈 대용으로 사용될 수 있다는 것이 도저히 이해가 안간다면 아마도 2진수에 익숙하지 않아서 그럴 것이다. 그렇다면 10진수로 이 현상을 설명해 보자. 10진수 1234에서 각 자리수는 10의 거듭승에 해당하는 자리값을 가지고 있다. 1은 103인 1000자리에 있고 2는 102인 100자리에 있고 3은 101인 10자리에 있는 셈이다.

 

1234=1*103+2*102+3*101+4*100

 

이 값을 왼쪽으로 쉬프트하면 각 자리수의 지수가 1증가한 12340이 된다.

 

12340=1*104+2*103+3*102+4*101+0*100

 

천자리는 만자리로 올라가고 백자리는 천자리로 올라가므로 모든 값이 10배가 되어 전체값도 10배가 되는 것이다. 반대로 오른쪽으로 밀면 모든 자리수가 10배 감소하므로 10분의 1로 값이 줄어든다. 1234를 오른쪽으로 밀면 123이 되고 일자리에 있던 4는 밀려나 사라진다. 물론 실수 차원이라면 123.4가 되겠지만 말이다.

좀 더 쉽게 설명하자면 임의의 십진수가 있을 때 뒤에 0을 하나 더 붙이면 10배가 되고 제일 뒷자리를 제거해 버리면 약간의 오차를 제외하고 1/10로 그 값이 줄어든다. 초등학생들도(심지어 일부 유치원 아동들까지도) 아는 간단한 원리이며 일상 생활에서도 흔히 있는 연산이므로 너무 너무 당연하게 생각될 것이다. 10진수에서 쉬프트 연산이 10배씩 증감하는 것처럼 2진수에서는 쉬프트 연산이 2배씩 증감하는 것이다.

왼쪽으로 한칸 밀면 두 배가 된다. 그럼 이 수를 다시 왼쪽으로 한칸 밀면 2배의 2배, 즉 4배가 될 것이다. 다시 한 번 더 밀면 8배가 된다. 자, 그럼 이제 쉬프트 연산과 곱셈 연산의 관계를 일반화해 보자.

 

a << b == a * 2b

 

a를 b만큼 왼쪽으로 민다는 것은 a를 2의 b승만큼 곱하는 것과 같다. 이 공식은 b가 음수일 때도 똑같이 적용된다. a를 -1만큼 왼쪽으로(즉 오른쪽으로 한칸) 밀면 2-1를 곱하는(즉 2로 나누는)것과 같다. 그래서 곱셈 대신에 쉬프트 연산을 사용할 수 있는데 이 두 연산은 엄청난 속도 차이가 있다. 비트를 이동시키는 것과 일정 횟수 더하기를 반복하는 것은 CPU 입장에서 보면 완전히 다른 작업이기 때문에 속도차가 무려 10배 정도 난다. 쉬프트 연산은 전혀 논리적이지 않으며 기계적이므로 기계가 하기에는 아주 쉬운 연산인 것이다.

a*2를 한 번 할 시간이면 a << 1을 10번 정도 할 수 있다는 얘기다. 이렇게 속도차가 나기 때문에 핵심 게임 엔진이나 시스템 프로그래머들은 곱셈 대신 쉬프트 연산을 즐겨 사용한다. 중요한 루프에서 곱셈을 하느냐 쉬프트 연산을 하느냐에 따라 프로그램의 성능에 확연한 차이가 나기 때문에 쉬프트 연산은 도저히 피할 수 없는 달콤한 유혹인 것이다.

쉬프트 연산이 곱셈에 비해 불리한 점은 2의 거듭승에 대해서만 곱셈이 가능하다는 점이다. 2배, 4배, 8배, 16배 등만 할 수 있으며 3배, 17배 이런 연산은 할 수 없다. 그러나 쉬프트 연산과 덧셈, 뺄셈을 잘 조합하면 이런 연산이 가능해지기도 한다.

 

3배 : a << 1 + a;

9배 : a << 3 + a;

15배 : a << 4 - a;

60배 : a << 6 - a << 2;

 

특히 제일 마지막의 쉬프트 연산으로 60배를 하는 코드는 아주 기발한 응용예이며 감탄을 금하기 어려운 예술 코드라고 할 수 있다. 정밀하게 측정해 보면 이런 연산들이 곱셈보다 수배~수십배 더 빠르다. 보통의 경우라면 일반적인 곱셈을 하는 것이 소스를 유지하기에 편리하지만 속도가 지극히 중요하다면 곱셈보다는 가급적이면 쉬프트 연산을 사용하는 것이 좋다.

'C/C++언어' 카테고리의 다른 글

고정 소수점  (0) 2008.05.09
각 언어별 3중 포인터  (0) 2008.05.06
[리눅스프로그래밍] makefile  (1) 2008.03.07
ASSERT(), VERIFY(), TRACE()  (0) 2008.03.04
템플릿 사용한 max 만들기  (0) 2008.03.03

폴더에있는 모든 소스를 컴파일하여 실행화일을 만든다.

$ g++ -o test *.c


폴더에 있는 모든 오브젝트 파일과 실행화일을 제거한다.

$ rm -f *.o test



만약에 컴파일할때 실행화일의 이름을 정해주지 않으면 실행화일은 a.out 로 생성됩니다.(참고)


makefile 또는 Makefile 또는 GNUmakefile 중에 하나에


test : test.o

[텝] gcc test.o -o test

test.o : test.c

[텝] gcc -c test.c


라고 입력한후에


$ make 명령을 하면

컴파일이 됩니다.

([텝] 이라고 써져있는 부분은 실제로 탭을 누르시고 만약 스페이스로 하시면 Makefile:2 *** missing separator. Stop. 에러가 납니다.)



makefile 와 Makefile 와 GNUmakefile 중에 일반적으로 Makefiel 을 사용을 권장하는데

그이유는 GNUmakefile 은 예전버전의 make 실행과 호환이 안되는경우가 있고

makefile 은 소스와 헷갈릴수 있다고 하네요.




만약 소스가 test1.c test2.c test3.c 이렇게 3개라면


Makefile 을 다음과 같이 작성합니다.(만약 모든소스에 io.h 가필요하다고 했을경우)



test : test1.o test2.o test3.o

[텝] gcc test.o -o test test1.o test2.o test3.o

test1.o : io.h test1.c

[텝] gcc -c test1.c

test2.o : io.h test2.c

[텝] gcc -c test2.c

test3.o : io.h test3.c

[텝] gcc -c test3.c



라고 작성해주심되고요



objects = test1.o test2.o test3.o

test : $(object)

[텝] gcc test.o -o test $(object)

test1.o : io.h test1.c

[텝] gcc -c test1.c

test2.o : io.h test2.c

[텝] gcc -c test2.c

test3.o : io.h test3.c

[텝] gcc -c test3.c


clean :

[텝] rm $(object)



이렇게 작성해 주셔도 됩니다.




그런후에

$make 명령을 하시면 컴파일 및 링크가 되어서 실행화일 test 가 만들어지고요

$make clean 명령을 하시면 모든 오브젝트 화일이 지워집니다.




Makefile 을 다음과 같이 gcc 부분을 $(CC) 로 해도 되고요 g++ 로 해도 됩니다.


objects = test1.o test2.o test3.o

test : $(object)

[텝] $(CC) test.o -o test $(object)

test1.o : io.h test1.c

[텝] $(CC) -c test1.c

test2.o : io.h test2.c

[텝] g++ -c test2.c

test3.o : io.h test3.c

[텝] g++ -c test3.c


clean :

[텝] rm $(object)



gcc 를 $(CC) 로 바꿔 줘도 되는 이유는 Pre-defined macro 되어있어서 입니다.


CC 나 cc 는 gcc 를 의미한느 것으로 미리 정의 되어 있다는 얘기입니다.


gcc 는 또한 어셈 포트란 C++ 등을 모두 포함되어있으며, g++ 는 C++컴파일을 의미합니다.




다음은 Pre-defined macro 내용입니다.


AR = ar (Archive maintaining program)
AS = as (Assembler)
CC = cc (= gcc , C compiler)
CXX = g++ (C++ compiler)
CO = co (extracting file from RCS)
CPP = $(CC) -E (C preprocessor)
FC = f77 (Fortran compiler)
LEX = lex (LEX processor)
PC = pc (Pascal compiler)
YACC = yacc (YACC processor)
TEX = tex (TEX processor)
TEXI2DVI = texi2dvi (Texiinfo file processor)
WEAVE = weave (Web file processor)
RM = rm -f (remove file)


출처 : http://paper.cyworld.com/ggogilee/920537

'C/C++언어' 카테고리의 다른 글

각 언어별 3중 포인터  (0) 2008.05.06
쉬프트 연산과 곱셈  (0) 2008.03.26
ASSERT(), VERIFY(), TRACE()  (0) 2008.03.04
템플릿 사용한 max 만들기  (0) 2008.03.03
<이클립스 디버그 방법>  (0) 2008.01.28
 

C++로 게임 개발을 할 때 몇몇 사람들이 MFC를 사용하기로 결정했다. 늘 저는 ASSERT(), VERIFY(), TRACE() 매크로들의 유익함을 발견한다. 그래서 윈도우 플랫폼을 위한 어떤 프로젝트에서든지 작업할 수 있도록 자신의 버전을 만들기로 했다.


ASSERT()는 값이 0이면 실행을 멈추도록 하기 위해 매개 변수을 평가한다. 릴리즈 모드에서는 assert가 아무 것도 전개되지 않도록 해야한다.


VERIFY()는 릴리즈 모드에서 매개 변수를 전개하도록 되어 있는 것을 제외하고는 ASSERT()와 아주 유사하다.
ASSERT()는 어떠한 함수 호출도 포함되어 있지 않는 표현으로 사용되어야만 한다. 함수 호출을 포함하는 표현을 위해 VERIFY()를 사용해야만 하고 그렇게 해서 함수 호출은 릴리즈 모드에서도 보전된다.


TRACE()는 디버그 윈도우에 출력되는 것을 제외하고는 printf()의 대응물이다. 릴리즈 모드에서는 TRACE() 또한 아무것도 전개하면 안된다.


세 개의 매크로들 중 어떤 것도 릴리즈 모드에서 어떤 런타임 벌점을 의미하지 않는다. 그 매크로들은 미리 정의된 _DEBUG 매크로를 사용해서 디버그 모드와 릴리즈 모드 사이를 구별한다. 이것은 Microsoft Visual C++에 특정적이다. 만약 다른 몇몇 다른 컴파일러를 사용하고 있다면 적절한 매크로를 사용해야만 할 것이다. 


ASSERT(), VERIFY(), TRACE()를 지원하는데 필요한 두 파일이 있다. 그것은 debug.h와 debug.cpp이다. 프로젝트의 몇몇 메인 헤더에 debug.h를 추가해야만 한다. 그것은 자신 안에 어떤 파일도 포함하지 않기 때문에 순환 포함(recurrent inclusion)에 의해 더럽혀지지 않는다. 또한 프로젝트의 소스 파일에 debug.cpp를 더해야만 한다.


Discuss this article in the forums
Date this article was posted to GameDev.net: 7/23/2002
(Note that this date does not necessarily correspond to the date the article was written)

See Also: Sweet Snippets


원문 : http://www.gamedev.net/reference/articles/article1846.asp

'C/C++언어' 카테고리의 다른 글

쉬프트 연산과 곱셈  (0) 2008.03.26
[리눅스프로그래밍] makefile  (1) 2008.03.07
템플릿 사용한 max 만들기  (0) 2008.03.03
<이클립스 디버그 방법>  (0) 2008.01.28
printf 문을 만들어보자  (0) 2008.01.28

+ Recent posts