'C/C++'에 해당되는 글 16건
- 2010.04.29 c/c++에서의 일반변수, 포인터변수, 메모리에 대해 2
- 2010.04.26 증가연산자 혹은 증감연산자 사용시 주의사항
- 2010.04.23 소스 파일이 모듈을 빌드했을 때와 다릅니다. 현재 위치에 사용할 수 있는 소스 코드가 없습니다. 1
- 2010.04.21 메모리릭 검출 중에 황당한 오류를 접한 경우
- 2010.04.20 알수없는 위치에 메모리릭이 발생하는 경우 검출법(vs2008기준)
- 2010.04.20 ZeroMemory또는 memset시 유의사항
특정 언어에 국한되는 문제는 아니지만, c/c++ 이하 c에서의 일반 변수와 포인터 변수와 메모리에 대한 얘기를 해볼까 합니다. 일단 메모리는 선형구조를 갖습니다. 그리고 메모리 각각의 주소를 가지고 있어요. 현실세계의 주소의 의미와 같습니다. 메모리라는 나라가 있다면 각 바이트는 그 나라의 국민이라고 하면 될까요?
앞서 살짝 언급했지만, 메모리의 주소는 바이트단위로 나누어집니다. 시작주소는 0이구요. 0부터 쭈욱 뻗어나갑니다. c에서는 이를 16진수로 표현하고 있습니다. 16진수 2자리가 1바이트라는건 아시죠? 그렇다면 0x00000000부터 ...까지 되어있겠네요. 한계는 거의없다고 봐도 과언이 아닙니다. 이게 물리적인 메모리(램)의 크기를 따라가는 것이 아니기 때문입니다. (실례로 어떤 프로그램 2개를 띄웠을때 같은 주소를 참조하고 있는데 값이 다른경우가 있습니다. 이 문제는 관련 책자를 찾아보세요.) 하지만 최대값은 0xFFFFFFFF이 되겠습니다. 여기서 잠깐, 왜 주소를 4바이트로 사용할까요? 그 이유는 4바이트만으로도 메모리내부의 주소를 모두 참조할 수 있기 때문입니다. 그래서 적정량을 4바이트로 두는 것이죠. 이것은 포인터 변수의 크기이기도 합니다. 1
일반변수의 포인터변수 모두는 메모리에 저장됩니다. 단, 저장되는 구조가 조금 다릅니다. 하지만 공통적으로 선언됨과 동시에 메모리의 어딘가에 해당변수의 크기만큼 배정됩니다.(해당변수를 sizeof한 만큼이 배정됩니다.) 하지만 저장됨과 동시에 일반변수는 값이 저장되는데 반해, 포인터변수는 메모리의 주소가 저장됩니다. 2
예를 들어 아래처럼 두 변수가 각각 0x0015F7FC, 0x0015F7F0위치에 배정되었다고 칩시다.
int* p; // 0x0015F7F0
변수가 초기화되어있지 않았기 때문에 쓰레기값이라고 불리는 값 0xCCCCCCCC가 들어있을겁니다. 두 변수 모두 예외는 아닙니다. 일단 n의 주소로 가보면(메모리의 주소 입력부에서 &n입력),
역시 cc cc cc cc로 채워진 걸 볼 수 있습니다. 시작주소 + 변수가 차지하는 바이트수를 읽으시면 되는데, int형이기 때문에 4바이트를 차지하니까 0x0015F7FC부터 +4바이트를 읽으시면 됩니다.
마찬가지로 p의 주소에 가봅시다. (메모리의 주소부터 &p를 입력)
역시 cc cc cc cc로 채워진 걸 볼 수 있습니다. 위와 마찬가지로 시작주소 + 변수가차지하는 바이트수까지 읽어오시면 되는데, 포인터 변수이기 때문에 0x0015F7F0부터 +4바이트를 읽으시면 됩니다.
이번엔 다음과 같이 초기화를 사용해봤습니다.
재컴파일을 했더니 위치가 약간 달라졌네요. 각각 0x002AFE60, 0x002AFE54위치에 배정되었다고 칩시다.
int* p = &n; // 0x002AFE54
먼저 n의 주소로 가봅시다.(&n)
0x002AFE60부터 4바이트를 읽어옵시다.
41 01 00 00 이 나오게 되는데 이걸 10진수로 바꿔보면 321이 아닌 1090584576이 나오게 됩니다..
이 문제는 리틀인디안 빅인디안(big-endian) 문제로, 인텔에선 리틀인디안(little-endian) 방식을 채택했기 때문에 다소 이상하게 나오는 것처럼 보이는 겁니다. 뒤에서부터 읽어오시면 됩니다. 00 00 01 41 그러면 321이 나오게 됩니다.
이번엔 p의 주소로 가봅시다. (&p)
0x002AFE54부터 4바이트를 읽어옵시다.
위와 마찬가지로 리틀인디안 방식으로 읽어오면,
00 2a fe 60이 됩니다. 바로 이것이 n의 주소가 됩니다. &n = 0x002AFE60
즉, 일반변수의 경우 일반적인 값이 저장되고, 포인터변수의 경우는 메모리의 주소가 저장되는 셈입니다. 변수의 주소라고 쓰려다가 고쳤는데, 그 이유는 다음과 같이 주소를 적당히 대입해도 가능하기 때문입니다. 하지만 이 방법은 다소 위험한 방식으로 거의 사용하진 않습니다만.. 일부 임베디드 프로그래밍에서는 자주 사용되는 듯 합니다.
정리해보면 일반변수와 포인터 변수와의 차이는 내부적으로 저장되는 값의 형태 차이입니다.
int* p;
n = 100;
p = &n;
printf("%d", n ); // n의 값
printf("%d", p ); // p의 값(n의 주소값)
여기서 잠깐, 주소를 표현하는데 있어서 아스트릭 연산자를 써서 해당주소의 값을 가져올 수 있습니다.
단, 가져올 때는 해당 변수의 자료형의 크기만큼 가져옵니다.
int n = 321;
printf("%d", *&n );
/* 포인터 변수 자체가 주소값이기 때문에 아스트릭 연산자를 이용해 값을 가져올 수 있습니다.
p가 저장하고 있던 값(n의 주소)부터 p의 원형(int)의 크기만큼 가져옵니다.
321이 나옵니다.*/
int* p = &n;
printf("%d", *p );
/* 아래처럼 하면 포인터 변수의 값(n의 주소값을 가져옵니다.)
먼저 p의 주소를 가져오게되는데, 따라서 이중포인터형으로 잠깐 변형됩니다. 그 후 아스트릭 연산자가 있으므로 해당 포인터 변수의 원형(int*)의 크기만큼 값을 읽어옵니다. */
printf("%d", *&p);
// 아래처럼하면 321이 나옵니다.(n의 주소에 있는 값)
printf("%d", **&p);
// 아래처럼 하면 어떤값이 나올까요?
// p의 주소의 값(n의 주소)를 char*형으로 바꾼 후, 그 주소의 값을 가져옵니다.
// 방금 원데이터형의 크기만큼 가져온다고 했는데, 원래 int형이었지만, char형으로 바뀌었기 때문에,
// 1바이트만 가져오게 됩니다.
// 00 00 01 41 을 읽어와야 정상이지만, char형으로 캐스팅했기 때문에 41만 읽습니다.
// 즉 65가 출력됩니다.
printf("%d", *(char*)*&p );
'C/C++ > 프로그래밍 일반' 카테고리의 다른 글
컴파일러에 암시 또는 힌트 (0) | 2010.07.08 |
---|---|
리다이렉션(< or >)을 활용한 파일입출력 (0) | 2010.05.31 |
ZeroMemory (1) | 2010.04.29 |
memset, memcpy, memmove, memcmp 멤형제들 (0) | 2010.04.29 |
알수없는 위치에 메모리릭이 발생하는 경우 검출법(vs2008기준) (0) | 2010.04.20 |
i++;
i--;
같이 자주쓰이는 연산자중 하나인데요.
간편하고 빨라서 아주 요긴하게 쓰이긴 하지만, 이 때문에 실수가 가끔 일어나기도 합니다.
이를테면....
i = 2;
int result = max( i--, 0 );
얼핏보면 제대로 나오는 것같지만 result가 0이 됩니다. 1을 예상했는데도 말이죠...
여기에서 max가 매크로로 되어있다면....?
이 경우는 max가 함수가 아닌 매크로로 되어있다면 가능한 일입니다.
WinDef.h에 정의되어있는 max매크로입니다.
#define max(a,b) (((a) > (b)) ? (a) : (b))
즉, 이렇게 변경이 되겠죠.
int result = (((i--) > (0)) ? (i--) : (0))
따라서 0이 나오는 겁니다.
'C/C++ > 에러도우미' 카테고리의 다른 글
기본 클래스를 정의하지 않았습니다. (0) | 2010.06.15 |
---|---|
교차 포함 시 대량의 오류가 발생하는 경우 (3) | 2010.06.13 |
소스 파일이 모듈을 빌드했을 때와 다릅니다. 현재 위치에 사용할 수 있는 소스 코드가 없습니다. (1) | 2010.04.23 |
메모리릭 검출 중에 황당한 오류를 접한 경우 (0) | 2010.04.21 |
ZeroMemory또는 memset시 유의사항 (0) | 2010.04.20 |
소스 파일이 모듈을 빌드했을 때와 다릅니다. 현재 위치에 사용할 수 있는 소스 코드가 없습니다.

소스 파일이 모듈을 빌드했을 때와 다릅니다.
상당히 짜증나는 부분입니다.
많은 분들의 얘기를 들어보면 그중 원인이 디버깅 심볼 데이터베이스를 구축하지 않아서 그럴수도 있다.. 라고 써있는데요 프로젝트 속성에 거의 들어가지 않는 시점에서 그 부분은 거의 신빙성이 없어 보입니다.
일단 첫번째 해결책은 솔루션 재빌드가 되겠습니다.
하지만 솔루션 재빌드를 해도 오류가 개선되지 않는다면 다음과 같은 시도를 해볼 수 있습니다.
http://cafe.naver.com/powermania/14옵션>디버깅>일반>소스파일이 원래 버전과 정확하게 일치해야함 체크해제
하지만 저렇게 바꾸는 것은 왠지 찜찜한 기분이 들어서 저렇게 바꾸진 못하겠더군요.
따라서 곰곰이 생각해보던 중, 빌드된 시점과 소스파일의 인코딩 상태가 다르다는 점을 알 수 있었습니다. 또는 편집하고 있는 도중에 아주 간혹.. 오브젝트파일을 자신도 모르게 이전 버전으로 교체해버리는 경우도 있어요. 이건 저도 예전에 경험해봤던 일이었죠. 그래서 해당파일을 파일 > 고급 저장 옵션에서 유니코드로 바꿔준 후 다시 빌드하니까 그런 에러가 사라지더군요.
하지만 특정파일만 유니코드로 하고 나머지는 안시로 하라? 역시 찜찜한 기분이 들어서 못바꾸겠더군요.
그래서 해결방안은?
일단 해당파일의 소스를 복사한 후, 해당파일을 완전히 삭제한 후, 동명으로 새롭게 파일만들어서 소스코드를 붙여넣기 한 후 빌드했습니다.
결과는? 해결입니다. 제대로 되더군요.
현재 위치에 사용할 수 있는 소스 코드가 없습니다.
소스 파일이 모듈을 빌드했을 때와 다릅니다.의 경우는 에러는 나긴 하지만 나름 디버깅도 되고 쓸만했습니다.
하지만 어느순간부터 저 에러가 난다면..?
프로그래머는 소스 파일이.. 이 대화상자가 뜰 때 [아니오] 버튼을 눌러서 그렇습니다.
해당소스파일을 디버깅할 때 제외시키는 것이죠. 무의식적으로 아니오 누를 수 있는데.. 낭패보기 쉽습니다.
이 부분은 그냥 솔루션 > 속성 > 소스파일 디버그 부분의 다음 소스파일을 찾지않음 부분에서 해당파일을 삭제해주세요. 그럼 소스파일이.. 라는 대화상자를 다시 보실 수 있으실 겁니다.
그 후에는 위와 같은 해결책으로 해결하시면 되요!
'C/C++ > 에러도우미' 카테고리의 다른 글
기본 클래스를 정의하지 않았습니다. (0) | 2010.06.15 |
---|---|
교차 포함 시 대량의 오류가 발생하는 경우 (3) | 2010.06.13 |
증가연산자 혹은 증감연산자 사용시 주의사항 (0) | 2010.04.26 |
메모리릭 검출 중에 황당한 오류를 접한 경우 (0) | 2010.04.21 |
ZeroMemory또는 memset시 유의사항 (0) | 2010.04.20 |
대략 다음과 같은 에러가 발생하는 경우입니다..
error C2365: 'operator new' : 재정의: 이전 정의는 '함수'입니다.
error C2491: 'new' : dllimport 데이터을(를) 정의할 수 없습니다.
error C2078: 이니셜라이저가 너무 많습니다.
error C2440: '초기화 중' : 'int'에서 'void *'(으)로 변환할 수 없습니다.
...
아래와 같이 관련 헤더파일을(메모리릭.h) 헤더파일포함리스트의 제일 하단에 포함시켜주세요..
#include ...
#include ...
#include ...
#include "메모리릭.h"
int main()
{
...
return 0;
}
'C/C++ > 에러도우미' 카테고리의 다른 글
기본 클래스를 정의하지 않았습니다. (0) | 2010.06.15 |
---|---|
교차 포함 시 대량의 오류가 발생하는 경우 (3) | 2010.06.13 |
증가연산자 혹은 증감연산자 사용시 주의사항 (0) | 2010.04.26 |
소스 파일이 모듈을 빌드했을 때와 다릅니다. 현재 위치에 사용할 수 있는 소스 코드가 없습니다. (1) | 2010.04.23 |
ZeroMemory또는 memset시 유의사항 (0) | 2010.04.20 |
다음과 같은 구문을 활용해 메모리 릭을 검출하는 경우(혹시 아래와 같은 구문을 처음 보신다면 메모리릭검출을 검색해서 관련부분을 살펴보고 오세요.)
// 출력창으로 정보를 출력시킨다.
_CrtSetReportMode( _CRT_WARN, _CRTDBG_MODE_DEBUG );
_CrtSetReportMode( _CRT_ERROR, _CRTDBG_MODE_DEBUG );
_CrtSetReportMode( _CRT_ASSERT, _CRTDBG_MODE_DEBUG );
#define DEBUG_NORMALBLOCK new ( _NORMAL_BLOCK, __FILE__, __LINE__ )
#ifdef new
#undef new
#endif
#define new DEBUG_NORMALBLOCK
이런 케이스나
c:\test\test.cpp(9) : {228} normal block at 0x00666458, 4 bytes long.
Data: < > CD CD CD CD
이런 케이스로 메모리릭이 났다는 것을 알려준다.
{81253} normal block at 0x03BB0AD0, 16 bytes long.
Data: < > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD
첫번째의 경우 파일명, 라인까지 친절하게 알려주기 때문에 메모리릭을 잡는데 어려움이 없지만
두번째의 경우 파일명과 라인이 없어 다소 당황스러울 수도 있다.(당연히 나올거라 예상했을거에요...)
하지만 이를 해결하기 위한 기타 여러가지 정보들이 있다.
다시 두번째 케이스를 보면,
{81253} normal block at 0x03BB0AD0, 16 bytes long.
Data: < > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD
여기서 81253는 몇번째로 할당했는가를 나타낸다. 그리고 0x03BB0AD0 주소에서 릭이 발생했다는 것을 알 수 있다. 여기서 normal block은 new 또는 malloc중 하나로 동적할당 했다는 얘기고 총 16바이트가 릭이 발생했단 얘기다. 우리는 여기서 81253과 0x03BB0AD0 두가지 정보를 가지고 브레이크 포인트를 걸 예정이다.
첫번째 방법
_crtBreakAlloc = 81253;
그리고 디버깅을 해보면, 해당지점에 브레이크포인트가 걸린다. 콜스택으로 따라가면 어디에서 릭이 발생했는지 알 수 있다.
두번째 방법
ALT+F9를 눌러서 중단점 창을 연 후, 새로만들기>새데이터중단점에서 주소부분에 저 주소(0x03BB0AD0)를 입력하고, 적당한 바이트(위의 경우는 16)를 기입하고 확인
F5로 계속 진행하여 그 부분을 찾아낸다.
'C/C++ > 프로그래밍 일반' 카테고리의 다른 글
컴파일러에 암시 또는 힌트 (0) | 2010.07.08 |
---|---|
리다이렉션(< or >)을 활용한 파일입출력 (0) | 2010.05.31 |
ZeroMemory (1) | 2010.04.29 |
memset, memcpy, memmove, memcmp 멤형제들 (0) | 2010.04.29 |
c/c++에서의 일반변수, 포인터변수, 메모리에 대해 (2) | 2010.04.29 |
보통 데이터를 갖는 구조체들은 구조체에 값을 채우기전에 초기화작업을 많이 하고들 합니다. 보통 아래와 같은 구문을 주로 쓰는 편인데..
또는
ZeroMemory( &data, sizeof( data ) );
하지만, 구조체에 STL이 포함되어있는 경우는 각별한 주의를 요합니다. 이를 테면..
{
string str;
vector<int> nums;
};
실행중에는 별다른 이상이 없어보이지만, 디버깅을 하면, 메모리누수가 나는 것을 볼 수 있습니다.
많은 경우를 모두 테스트 해보지 않았지만, vector의 경우는 별다른 이상이 없었지만.. string의 경우는 메모리 누수가 발생합니다. str변수를 new를 통해 생성하지 않았음에도 말이죠. 사실 내부적으로도 동적 할당되구요. 차례대로 따라가다보시면 더 확실히 알 수 있습니다.
어쨌든 별생각없이 ZeroMemory한 결과였고, 실행중에 정상적으로 나오기 때문에 별다른 걸 못느꼈지만, 디버깅을 해보니 누수되었다고 나오더군요.
결론은 STL변수의 저장공간을 절대 0으로 초기화하지 말아야 합니다. 당연한 얘기일수도 있으나, 의외로 실수가 잦으니 항상 숙지하시기 바랍니다.
'C/C++ > 에러도우미' 카테고리의 다른 글
기본 클래스를 정의하지 않았습니다. (0) | 2010.06.15 |
---|---|
교차 포함 시 대량의 오류가 발생하는 경우 (3) | 2010.06.13 |
증가연산자 혹은 증감연산자 사용시 주의사항 (0) | 2010.04.26 |
소스 파일이 모듈을 빌드했을 때와 다릅니다. 현재 위치에 사용할 수 있는 소스 코드가 없습니다. (1) | 2010.04.23 |
메모리릭 검출 중에 황당한 오류를 접한 경우 (0) | 2010.04.21 |