|
황경록 님이 쓰신 글 :
: 먼저 지금까지 테스트 코드와 같이 코딩을 하셨다면 무조건(?) 코드를 싹 수정하셔야
: 합니다. 그렇지 않으면 향후 포인터 지옥과 더불어 시스템에 치명적이 될 수 있습니다.
위의 말씀에 전적으로 동의합니다.
하지만, 아래 말씀의 의도는 알겠으나 예를 잘못드신 것 같군요.
: a="aaaaa"; 의 코드는 a 가 가리키는 알수없는 번지에 aaaaa 를 기록하게 됩니다.
a="aaaaa";를 하게 되면 a의 값인 메모리의 특정 번지로부터 시작해서 "aaaaa"라는 문자열 자체를 저장하는 것이 아닙니다. 정적 문자열인 "aaaaa"는 컴파일 시에 이미 실행 파일에 자리잡게 됩니다. a="aaaaa";를 하게 되면 그 문자열의 알 수 있는 시작번지가 a 변수에 저장되는 것입니다. 그리고 이 경우는 메모리 릭이 아닙니다.
a의 값인 메모리의 번지로부터 시작하는 메모리에는 보통 해당 문자열의길이+1 만큼의 영역만을 컴파일러가 잡아 놓았다고 보는 것이 안전할 것입니다.
한편, 지금과 같은 정적 문자열의 경우에는 Project 옵션에서 "Merge duplicate strings"를 선택해두면 아래와 같은 효과가 발생할 수 있습니다.
char *a="aaaaaa";
char *b="aaaaaa";
char *c="aaaaaa";
이렇게 하면 a,b,c 모두 동일한 번지를 가리키게 됩니다.
strcpy(a,"ddddd"); 하면
a,b,c가 가리키는 동일한 번지를 시작번지로 하여 "ddddd"를 저장하게 되고 "aaaaa"는 사라집니다.
물론, strcpy를 할 때 두번째 아규먼트인 "ddddd"는 정적 문자열이므로 컴파일 시 또다른 번지에 저장되어 있는 것입니다.
이 것은 예를 위한 것일뿐이고 실제로 사용하는 것은 추천하지 않습니다.
아울러, 포인터 변수를 사용할 때는 char *a="aaaaa"; 에서 &a, a, *a를 확실하게 구분할 수 있어야 합니다.
일단 a는 변수이므로 메모리에 자리를 잡습니다. 이 게 포인터 변수이므로 32bit 운영체제에서는 4바이트를 차지하게 됩니다. 이 시작 번지를 1번지라고 가정합시다.
그러면 &a는 1이 됩니다.
"aaaaa"는 컴파일 시 100번지를 시작으로 저장되어 있다고 가정합시다.
a="aaaaa"를 하면 1번지로부터 시작하여 4바이트 영역에 100을 저장합니다.
그러면 a는 100이 됩니다.
*a는 a[0]과 같은데 1번지로부터 시작하여 4바이트 영역에 저장되어 있는 값 100을 번지로 보고 이 번지에 저장되어 있는 'a' 문자를 참조하는(읽는) 것입니다.
(char *a="aaaaa";는 char *a; a="aaaaa";를 하나로 해놓은 것임)
:
: 클래스로 테스트를 해볼 필요도 없습니다.
:
: 새 프로젝트를 만드시고 버튼을 하나 올려놓고 코드 가드를 on 하신 후 다음의 코드를
: 실행시켜 보시기 바랍니다.
:
: char *a = NULL;
: a = "aaaaa";
:
: ShowMessage( a );
:
: 런타임시에는 아무 오류가 잡히지 않겠지만 코드 가드에서는 메모리 릭에 대한 에러를 보여줍니다.
:
: 분명 a 는 캐릭터 형의 널 포인터 변수이지만 이 변수 자체가 특정 번지를 가리키고 있다고 가정한다면
: a="aaaaa"; 의 코드는 a 가 가리키는 알수없는 번지에 aaaaa 를 기록하게 됩니다.
: ShowMessage( a ); 를 하면 당연히 a 가 가리키는 번지의 값이 aaaaa 이기 때문에 aaaaa 가 출력될
: 것입니다.
:
: 따라서 런타임시에 오류가 나지 않습니다.
:
: a 가 가리키는 번지는 메모리 관리자에 의해 allocation 되지 않았으므로 다른 프로그램이 a 가 가리키는
: 번지에 값을 쓸 수 있게 되는 오류가 일어나게 됩니다.
:
: 그리고 마지막으로 재미있는 부분은 "aaaaa" 입니다. a 가 포인터 변수이므로 실제 컴파일러가 컴파일시
: "aaaaa" 를 특정 번지에 기록한 후 이 포인터를 a 에 assign 한다는 점입니다.
:
: 만약 다음과 같이 int 형 포인터를 사용하면 컴파일시 컴파일러가 에러를 잡아내는 걸 확인하실 수 있습니다.
:
: int *a = NULL;
: a = 1111;
:
: ShowMessage( a );
:
:
:
: 친구들 님이 쓰신 글 :
: : 먼저 답변 감사합니다.
: :
: : 보통 대부분의 다른 언어로 코딩할때...즉, VC++이나 java인 경우
: : 객체생성없이 코딩하는 경우 에러가 발생(ex. null pointer Exception)합니다.....
: : (
: : MyClass->Method1(); <-- NULL값에 access불가!!!! 라는 에러 발생
: : )
: :
: : 근데 왜 볼랜드는 되는 걸까요??( 볼랜드 컴파일러가 좋아서 그런건가요??.....ㅡㅡ;;;.......)
: :
: : 결국 개념적으로 확실하게 하려면 이 같은 코딩은 하지 말아야 하는군요.......
: :
: :
: : 지금까지 답변달아주신 모든 분들께 감사한 마음 잊지않겠습니다.....:)
: :
: :
: :
: :
: :
: :
: : 만복대.산수유 님이 쓰신 글 :
: : : : 하지만 제가 생성한 코드는 new없이 global하게 pointer만을 선언하고 사용하고 있습니다..
: : : : 즉,
: : : :
: : : : TMyClass *MyClass; 이렇게 선언하고
: : : :
: : : : MyClass->Method1(); 이렇게 쓰고 있습니다..
: : : :
: : : : TObject를 상속받고 vcl.hpp를 사용도 하고 있습니다..
: : : : 그런데 객체생성없이 수행이 된다라는것이 이상한 겁니다...
: : : : 왜 런타임시에 에러가 안날까요?
: : :
: : : 그건, 해당 인스턴트의 자료에 액세스하지 않았기 때문입니다.
: : : 첨부 파일에 있는 TTest1 클래스에는 일단 virtual 메서드가 없습니다.
: : : TTest1 *Test1; //파일 수준에서 선언한 전역변수; NULL로 초기화됨
: : : TTest1* 형의 NULL 포인터에 대해 ShowMsg()를 호출하면 TTest 클래스(not Instance)의 메서드 테이블에 있는 ShowMsg를 호출합니다.
: : : 이 함수를 호출할 때는 먼저 CPU의 특정 레지스터에 인스턴스의 포인터를 저장해두고 함수를 호출할 것입니다(추측).
: : : (위의 경우 Test1의 값, NULL을 레지스터에 저장)
: : : 그러한 후 호출된 함수로 실행 지점이 옮겨가면 호출된 함수에서는 해당 레지스터에서 인스턴스 포인터(즉, this)를 가져옵니다. (이러한 작업을 하는 코드 생성은 컴파일러가 자동으로 할 것입니다)
: : : 이 인스턴스 포인터로 하는 일은 가상함수 테이블과 자신만의 데이터 멤버에 액세스하는 것입니다.
: : :
: : : 만약, 가상함수(순수가상 말고)를 선언해두고 해당 함수를 호출하면 오류가 발생할 것입니다.
: : : 왜냐하면, 인스턴스 포인터가 vtable를 액세스하여 해당 함수를 찾아와야 하는데 가리키는 메모리가 엉뚱한 곳이기 때문입니다. 이 것은 멤버변수들(함수들말고)에 대해서도 같습니다.
: : :
: : :
: : :
: : : 친구들 님이 쓰신 글 :
: : : : 먼저 답변해주셔서 감사합니다...
: : : :
: : : : 님께서 말한대로 struct와 동일하게 사용된다면
: : : : TMyClass MyClass;
: : : : 이렇게 선언하고 MyClass.Method1() 이렇게 쓴다는것이 이해가 됩니다..
: : : :
: : : : 컴파일 타임에 메모리에 올라간다고 했으니깐요...
: : : :
: : : : 하지만 제가 생성한 코드는 new없이 global하게 pointer만을 선언하고 사용하고 있습니다..
: : : : 즉,
: : : :
: : : : TMyClass *MyClass; 이렇게 선언하고
: : : :
: : : : MyClass->Method1(); 이렇게 쓰고 있습니다..
: : : :
: : : : TObject를 상속받고 vcl.hpp를 사용도 하고 있습니다..
: : : : 그런데 객체생성없이 수행이 된다라는것이 이상한 겁니다...
: : : : 왜 런타임시에 에러가 안날까요?
: : : :
: : : : test코드를 zip으로 첨부합니다...
: : : :
: : : : Unit2.cpp가 수행되는 이유가 무엇얼까요?
: : : :
: : : : 이유를 알아야...이 코드를 계속 놔두어도 될지 아니면
: : : : 싹 뜯어고칠지 정해야 되기때문에....많은 reply부탁 드립니다...
: : : :
: : : :
: : : :
: : : :
: : : : 황경록 님이 쓰신 글 :
: : : : : 클래스 자체가 꼭 new 로 생성을 해야만 메모리가 할당되는 것은 아닙니다.
: : : : :
: : : : : class TMyClass
: : : : : {
: : : : : Method1()
: : : : : ...
: : : : : }
: : : : :
: : : : : 으로 만드신 베이스 클래스라면 일반 스트럭처와 동일하게 사용할 수 있습니다.
: : : : : 스트럭처와 클래스는 분명 큰 차이점이 있지만 그 기반은 동일하니까요.
: : : : :
: : : : : TMyClass MyClass;
: : : : :
: : : : : MyClass.Method1() 의 형식으로 호출이 가능하며 메모리 누수와는 관계가 없습니다.
: : : : : 그리고 MyClass 는 컴파일 시에 TMyClass 만큼의 메모리가 할당되며 프로그램 종료시에
: : : : : 해제됩니다.
: : : : :
: : : : : 보통 빌더에서 new 를 사용해서 객체를 생성하는 것은 델파이에서 온 VCL Class 때문입니다.
: : : : : VCL 클래스는 반드시 new 를 통한 메모리 할당을 통해서 이용해야 합니다.
: : : : : VCL 의 구조적 특성( ^^ 예전에 읽었는데 영문이라 대충 읽어서 ... ) 때문입니다.
: : : : :
: : : : : 만약 클래스를 VCL 클래스 기반으로 만든다면 VCL 베이스 클래스인 TObject 를
: : : : : 상속받으시면 됩니다.
: : : : :
: : : : : class TMyClass : public TObject
: : : : : {
: : : : : Method1()
: : : : : ...
: : : : : }
: : : : :
: : : : : TMyClass* pMyClass = new TMyClass();
: : : : : pMyClass->Method1();
: : : : :
: : : : : 이 되는 것이죠....
: : : : :
: : : : : 친구들 님이 쓰신 글 :
: : : : : : 답변 감사합니다...위 답변을 보고 코드를 수정을 했습니다....
: : : : : :
: : : : : : 근데...제가 의문시되는것은 new로 생성을 하지 않은 코드가 돈다는 겁니다...
: : : : : : 아래 코드는 project option의 autocreate form도 아닙니다...
: : : : : : 즉, new 해주는 부분이 없습니다..그냥 function call하는것처럼 쓰고 있습니다..
: : : : : : 물론 객체를 생성하면 Method called on invalid object라는 코드가드에서 뿌리던 메시지는 사라집니다..
: : : : : :
: : : : : : 지금까지 객체생성없이 잘 사용했기때문에 만약 문제가 있다면...
: : : : : : 모든 소스를 바꾸어야 하는 문제가 생기기 때문에 저한테는 매우 중요한 문제입니다..
: : : : : :
: : : : : : 아무쪼록 이런 경험이 있으시면 reply감사하겠습니다...
: : : : : :
: : : : : : 그럼...
: : : : : :
: : : : : :
: : : : : :
: : : : : :
: : : : : :
: : : : : :
: : : : : : 이상해 님이 쓰신 글 :
: : : : : : : 코드 가드를 이용했더니 다음과 같은 에러를 뱉어냅니다...
: : : : : : :
: : : : : : : Error 00164. 0x100C00 (Thread 0x0CA0):
: : : : : : : Method called on invalid object: Attempt to access 4 byte(s) at 0x00000000.
: : : : : : : Call Tree:
: : : : : : : 0x00414B8A(=AAAA.exe:0x01:013B8A) C:\test\EScnro.cpp#77
: : : : : : : ===> 에러가 EScnro에서 발생해서 AScnro까지 던져주고 있습니다...
: : : : : : : 0x0043E67D(=AAAA.exe:0x01:03D67D) C:\test\DScnro.cpp#289
: : : : : : : 0x004497E0(=AAAA.exe:0x01:0487E0) C:\test\CScnro.cpp#132
: : : : : : : 0x0044A9E7(=AAAA.exe:0x01:0499E7) C:\test\BScnro.cpp#293
: : : : : : : 0x0044A30E(=AAAA.exe:0x01:04930E) C:\test\AScnro.cpp#229
: : : : : : : 0x400EE4E7(=vcl60.bpl:0x01:03D4E7)
: : : : : : : ------------------------------------------
: : : : : : : 위와 같은 에러를 수백개 뱉어내네요....메모리도 콸콸 세고.......-_-;;
: : : : : : : 문제는 invalid object라는 에러인데.... EScnro.cpp는 객체를 생성을 하지않고 쓰고 있습니다
: : : : : : : 여기서 제가 의문이 드는것은 객체 생성을 하지 않고 다음과 같이 사용했을때
: : : : : : : 런타임시에 객체가 없다고 에러를 뱉어야 하는데 어떻게 빌더가 에러없이 도는지 이해가 가지 않습니다...
: : : : : : :
: : : : : : : ============header 파일====================
: : : : : : :
: : : : : : : #ifndef EScnroH
: : : : : : : #define EScnroH
: : : : : : :
: : : : : : : //---------------------------------------------------------------------------
: : : : : : : class TEScnro : public TObject
: : : : : : : {
: : : : : : : private: // User declarations
: : : : : : : public: // User declarations
: : : : : : : __fastcall TEScnro();
: : : : : : : AnsiString __fastcall ShowMessage(AnsiString msg);
: : : : : : : };
: : : : : : : //---------------------------------------------------------------------------
: : : : : : : extern PACKAGE TEScnro *EScnro;
: : : : : : : //---------------------------------------------------------------------------
: : : : : : :
: : : : : : : #endif
: : : : : : :
: : : : : : :
: : : : : : : ============cpp 파일====================
: : : : : : : #include "EScnro.h"
: : : : : : :
: : : : : : : //---------------------------------------------------------------------------
: : : : : : : #pragma package(smart_init)
: : : : : : :
: : : : : : : TEScnro *EScnro;
: : : : : : :
: : : : : : : __fastcall TEScnro::TEScnro()
: : : : : : : {
: : : : : : : }
: : : : : : :
: : : : : : : AnsiString __fastcall TEScnro::ShowMsg(AnsiString msg)
: : : : : : : {
: : : : : : : return "객체생성없이 왜 도냐?";
: : : : : : : }
: : : : : : :
: : : : : : : ============================================================================
: : : : : : :
: : : : : : : 만약 일반 c처럼 생각해서 객체 없이 함수만 호출한다고 한다면...
: : : : : : :
: : : : : : : TEScnro *EScnro; ==> TEScnro EScnro; 이렇게 포인터 없이 사용해야하는거 아닌가요??(struct 사용할때처럼...)
: : : : : : : 그리고 EScnro->ShowMessage(msg); 이렇게 사용하는게 아니라
: : : : : : : EScnro.ShowMessage(msg);로 사용하는거 아닌가요??
: : : : : : :
: : : : : : : 위와 같이 코딩하면 왜 수행이 되는지 알려주시면 감사하겠습니다...
: : : : : : : 또한 메모리 누수 현상과도 관계가 있는지요??
: : : : : : :
: : : : : : : 조그만 도움이라도 부탁합니다...ㅜㅜ
|