'개발이야기/C, C++'에 해당되는 글 6건

  1. C언어 pthread 사용 방법
  2. 포인터 배열 테스트 코드
  3. C코드 메모리릭 잡기
  4. 부모 클래스의 기본생성자가 없을때 메모리 누수 현상 2
  5. 마방진 원리 및 문제 4
  6. Mangled Name

pthread를 이용하여 스레드를 구동하기 위해서는 pthread_create 함수를 사용합니다.


간혹 스레드를 사용하고 나서 스레드 종료시키는 것을 잊어 버리게 되는데요…
간단한 테스트 프로그램에서는 문제가 되지 않지만, 고객이 사용하는 프로그램이나 전문적인 프로그램에서는 프로그램이 종료 되기 전 반드시 스레드가 먼저 종료 되어야 합니다.


스레드가 동작 중일 때 프로그램이 먼저 종료 될 경우 에러나 예기치 않는 문제가 발생할 수 있기 때문입니다. 따라서, pthread_join 함수를 사용하여 생성된 스레드가 종료 될 때까지 기다리고 프로그램이 종료 되도록 구현합니다.


다음은 pthread 사용 방법 소스코드 입니다.

#include <pthread.h>
 
static pthread_t p_thread;
static int thr_id;
static bool thr_exit = true;
 
/**
 * 스레드 함수
 */
void *t_function(void *data)
{
	while(!thr_exit)
	{
		...
	}
 
	pthread_exit((void *) 0);
}
 
/**
 * 스레드 시작
 */
void start_thread()
{
	thr_exit = false;
	thr_id = pthread_create(&p_thread, NULL, t_function, NULL);
}
 
/**
 * 스레드 종료
 */
void end_thread()
{
	thr_exit = true;
	pthread_join(p_thread, (void**)NULL);	// 해당 스레드가 종료되길 기다린다.
}

/**
 * 메인 함수
 */
void main()
{
	start_thread();

	...

	end_thread();
}



references


'개발이야기 > C, C++' 카테고리의 다른 글

포인터 배열 테스트 코드  (0) 2012.08.07
C코드 메모리릭 잡기  (0) 2012.07.18
부모 클래스의 기본생성자가 없을때 메모리 누수 현상  (2) 2012.06.12
마방진 원리 및 문제  (4) 2012.05.19
Mangled Name  (0) 2012.05.13


포인터 배열 관계에 있어서 햇갈리는 부분을 테스트 해보았다.

int* arr[3];


위와 같은 포인터 타입의 배열에서 의문을 품었다.

배열에서 새로운 객체(or 배열)의 생성이 자유로이 될까?

만약 배열이 생성가능하면 2차원 배열로써 활용가능할까?

에 해당하는 것이다.


  
int _tmain(int argc, _TCHAR* argv[])
{
	int a = 1;
	int b = 2;
	int c = 3;

	int* arr[3];

	arr[0] = new int[3];
	arr[1] = &b;
	arr[2] = &c;

	arr[0][0] = 4;
	arr[0][1] = 5;
	arr[0][2] = 6;

	printf("%d\n", arr[0][2]);
	printf("%d\n", *arr[1]);
	printf("%d\n", *arr[2]);

	return 0;
}

위의 코드는 정상 수행되는 코드로 결과 출력값은 다음과 같다.

※ 출력결과
6
2
3


즉, 배열에서 새로운 객체(or 배열)의 생성이 자유로이 되며,

포인터 배열에서 새로운 배열 할당시 2차원 배열로써 활용이 가능하다.

C와 C++ 알아나가면 알아나갈수록 유연하게 잘 설계되어 있는것 같다. ^^


'개발이야기 > C, C++' 카테고리의 다른 글

C언어 pthread 사용 방법  (0) 2014.03.05
C코드 메모리릭 잡기  (0) 2012.07.18
부모 클래스의 기본생성자가 없을때 메모리 누수 현상  (2) 2012.06.12
마방진 원리 및 문제  (4) 2012.05.19
Mangled Name  (0) 2012.05.13


프로그램을 개발하다보면 사람인지라 버그나 메모리릭이 생기는 것은 당연합니다.


참조: 네이버웹툰, 스마트폰 게임 개발 이야기에서...


따라서 이를 빠르게 발견하고 해결하는 것이 중요합니다.

여기서는 C코드로 짠(콘솔 응용프로그램) 환경에서 메모리릭을 발견하고 잡는 방법을 알아보겠습니다.

다음과 같이 메인 프로젝트를 만들고 메모리릭을 감지하는 코드를 추가합니다. (강조된 부분)

  
// MemoryLeak.cpp : 콘솔 응용 프로그램에 대한 진입점을 정의합니다.
//

#include "stdafx.h"

#ifdef _DEBUG
#include <crtdbg.h>
#define new new(_NORMAL_BLOCK, __FILE__, __LINE__)
#endif

int _tmain(int argc, _TCHAR* argv[])
{
#ifdef _DEBUG
	_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
#endif

	// 메모리릭 발생코드
	char * szName = new char[100];
	szName = "mooyou";
	printf("%s", szName);

	return 0;
}


위의 코드를 실행하면 출력창에 메모리릭이 발생되었다고 나타납니다.

출력 정보를 보면 memoryleak.cpp 파일의 18번째 라인에서 100 bytes 크기의 메모리릭이 발생되었습니다.

이 정보만으로도 충분히 메모리릭을 잡을수 있습니다.



여기서 더 나아가 메모리 블록 위치 정보를 통해서 직접적으로 메모리릭이 발생한 코드로 이동까지 해보겠습니다.

이를위해 다음과 같이 _CrtSetDbgFlag 바로아래 _CrtSetBreakAlloc( 97 ); 코드를 추가합니다.

이는 메모리릭 발생 위치에 BreakPoint를 설정합니다.

  
// MemoryLeak.cpp : 콘솔 응용 프로그램에 대한 진입점을 정의합니다.
//

#include "stdafx.h"

#ifdef _DEBUG
#include <crtdbg.h>
#define new new(_NORMAL_BLOCK, __FILE__, __LINE__)
#endif

int _tmain(int argc, _TCHAR* argv[])
{
#ifdef _DEBUG
	_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
	_CrtSetBreakAlloc( 97 );
#endif

	// 메모리릭 발생코드
	char * szName = new char[100];
	szName = "mooyou";
	printf("%s", szName);

	return 0;
}


즉, 출력된 메모리 블록 위치 정보를 이용하여 BreakPoint를 설정하고, 실행시 설정된 곳으로 이동하겠다라는 것입니다 !

코드를 추가하고 실행하면 다음과 같은 메시지 박스가 나타납니다.

BreakPoint에 의해 중단점을 트리거 했다라는 내용이고 여기서는 중단을 누르면 됩니다.

하지만 누르고 나서 코드를 보면 메모리릭이 발생한 원하는 위치가 아닌 dbgheap.c 파일의 내용이 보이게 됩니다.

모르는 코드화면이 나왔다고 해서 전혀 걱정할 필요가 없습니다. 정상적인 화면이고... 제대로 잘 찾아온 것입니다. 

메모리릭이 발생한 곳으로 이동하려면 호출 스택에서 작업위치를 찾아가면 됩니다.

호출 스택은 보통 출력창과 같은 탭에 있고, 없다면 Ctrl + Alt + C를 눌러 호출 스택을 불러옵니다.

호출 스택은 현재 프로그램에서 실행된 명령을 순차적으로 나열한 것으로 맨 위에 찍힌 스택이 최근에 사용한 명령입니다.

현재 보이는 위치(노란색 화살표)가 msvcr100.dll로 dbgheap.c의 내용을 가르킵니다.

즉, 작업한 코드 영역이 아니죠. 따라서 작업한 영역인 MemoryLeak 스택을 더블클릭하여 이동합니다.

짜잔~ 왼쪽에 초록색 화살표가 보이시나요?

호출 스택을 이용하여 메모리릭이 발생한 위치로 이동하였습니다.



C++로 개발되어진 모듈을 수정하던중 생각지도 않는 문제가 발생했습니다.


문제가 되는 기본 전제는 이러합니다. 부모의 기본생성자가 없다라는 점과 

자식 클래스에서는 이 부모 클래스를 상속받아 생성자를 오버라이딩해서 사용한다라는 점입니다.


여기서 부모 클래스 생성자에서는 메모리 할당을 하고 오버라이딩된 자식 클래스 생성자에서

이를 다시 메모리 할당하고자 할때 메모리 누수가 발생하였습니다. 지금 생각해도 참으로 복잡한 문제이죠...

에러가 난 코드를 최대한 간소화 시켜보았습니다.


//
// 메모리 누수 확인 코드
//
#ifdef _DEBUG
#include <crtdbg.h>
#define new new(_NORMAL_BLOCK, __FILE__, __LINE__)
#endif

//
// Class A
//
class A {
public:
	char* szA;

public :
	A::A(int a)
	{
		szA = new char[a];

		OutputDebugString(_T("A생성자 호출\n"));
	}

	A::~A()
	{
		delete [] szA;

		OutputDebugString(_T("A소멸자 호출\n"));
	}
};

//
// Class B
//
class B : public A {

public:
	B::B(int b)
		// 1.
		// 부모가 기본생성자를 갖고있지 않으므로 자식은 생성자를 만들기 위해서
		// 부모의 사용가능한 생성자를 직접 지정해주어야 한다.
		: A(b)		
	{
		// 2.
		// 부모에서 생성된 객체를 메모리 할당
		szA = new char[b];

		OutputDebugString(_T("B생성자 호출\n"));
	}

	B::~B()
	{
		// 3.
		// 할당한 메모리를 제거하려 했으나 에러 발생
		// 여기서 메모리 누수 발생
		// delete [] szA;

		OutputDebugString(_T("B소멸자 호출\n"));
	}
};

//
// Main Function
//
int _tmain(int argc, _TCHAR* argv[])
{
#ifdef _DEBUG
	_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
#endif

	A a(3);
	B b(3);	// 메모리 누수 발생

	return 0;
}


이를 해결하고자 다음과 같이 Class B를 수정하였습니다.

  
//
// Class B
//
class B : public A {

public:
	B::B(int b)
		// 1.
		// 부모가 기본생성자를 갖고있지 않으므로 자식은 생성자를 만들기 위해서
		// 부모의 사용가능한 생성자를 직접 지정해주어야 한다.
		: A(b)		
	{
		// 4.
		// 지정해준 [: A(b)] 부모 생성자에서 할당했던 객체를 미리 해제하고
		// 해당 생성자에서 할당된 객체는 이후 A소멸자가 호출될때 제거하게 해준다.
		delete [] szA;

		// 2.
		// 부모에서 생성된 객체를 메모리 할당
		szA = new char[b];

		OutputDebugString(_T("B생성자 호출\n"));
	}

	B::~B()
	{
		// 3.
		// 할당한 메모리를 제거하려 했으나 에러 발생
		// 여기서 메모리 누수 발생
		// delete [] szA;

		OutputDebugString(_T("B소멸자 호출\n"));
	}
};


'개발이야기 > C, C++' 카테고리의 다른 글

C언어 pthread 사용 방법  (0) 2014.03.05
포인터 배열 테스트 코드  (0) 2012.08.07
C코드 메모리릭 잡기  (0) 2012.07.18
마방진 원리 및 문제  (4) 2012.05.19
Mangled Name  (0) 2012.05.13

  • 마방진?

마방진이란 아래 그림과 같이 가로, 세로, 및 대각선에 있는 각각의 합이 같도록 배열한 것을 의미



  • 문제

위의 3 * 3 마방진을 구현하시오. 화면에 출력.

(For문 또는 While문, if문, 배열, 함수 사용)


  • 원리



  • 코드보기

마방진.cpp

프로그래밍에 관련된 가이드 문서를 읽던 중...

"LLVM에서 llvm-nm 툴을 이용하여 C++의 Mangled Name을 Demangled 할수 있다" 라는 말을 접하게 되었다.

하지만 아무리 읽어보아도 Mangled Name이 어떤 의미를 갖는지 잘 이해가 가지 않았다.

알아본 결과...

 

Mangled Name은 C와 C++ Linking 과정의 차이에서 알아볼 수 있었다.

  • C는 Overloading을 지원하지 않는 언어이다. 따라서 함수를 단순히 '함수의 이름(Symbol)으로 구분'하여 Linking을 한다.
  • C++은 Overloading을 지원하는 언어이다. 단순히 함수의 이름만으로 어떤 함수를 호출할지 '정확히 구분'할 수 없다. 함수의 이름과 더불어, Parameter의 종류, 개수의 정보도 포함시켜야 Linking시에 Linker가 적당한 함수를 묶어 준다. 이러한 Linkage(링크 규칙)를 'Mangled Name'이라고 부른다.

 

아직 탐험해보지 못한 신비한 세계가 많이 남아있는듯 하다. 

참조: http://blog.naver.com/supsup5642/60156876656