c++ string이 메모리를 많이 써서 직접 만든 mini string
제목대로다. 
이걸 만든 자세한 내막은 이렇다.
c++에서 string을 엄청나게 많이 (1기가 정도치...객체가 몇 개 인지는 안세봐서 모름) 사용하다가
프로그램이 사용하는 메모리가 2기가를 돌파할 때쯤 메모리 부족으로 프로그램이 뻗어버렸다.

내가 뭐 잘못만들었나 싶어서 map 대신에 vetor를 쓰니 (key로 검색 기능은 없지만, vector에서 순차로 검색하기로 대체)
약간 사용량이 줄긴했다. vector의 길이도 reserve로 직접 일일이 최적화 해보고...

그래도 그런걸론 부족했다.
string도 vector기반으로 만들어진 것이니 내부에 reserve를 사용해서 일일이 최적화 해봤는데 놀라운 사실을 알아냈다.

reserve(24)를 하면 실제로 31을 잡는 것이다. 디버깅 모드에서 실제로 capacity가 몇인지 확인해보면 알 수 있다.
메모리를 예약하라고 지시한 수치보다는 약간 더 잡는다. 이 양은 정확히 어떤 규칙으로 계산하는지 모르겠다.
구글링 영어로 해봐도 쉽게 이에 대한 설명이나 해결책을 찾지 못했다.

뭐..어차피 string 클래스는 내부 문자열의 길이도 미리 잡아두고 있기 때문에 4bytes를 추가로 또 저장한다.
이런 저런 사용하지 않는 기능들을 가지느라 string이 추가 메모리를 사용하는걸 줄일 수 있지 않을까 싶어서
직접 미니멀한 string 클래스를 만들었다.

생각보다 custom string 클래스를 작성하는 것이 쉽진 않았지만 딱히 어렵지도 않았다.

몇 가지 아쉬운점은, mini인만큼 아쉬운게 많지만 그 중에서도 특히,
나의 miniString 클래스가 const char*로의 자동 형변환이 안된다는 것이다.
이는 string에서도 문제다(그래서 c_str()함수가 존재).
miniString에서는 str 맴버를 그냥 public으로 노출시켜놨다.

이 외에도 
string a, b;
miniString c;
a=b+c;
이렇게는 안된다. 가장 간단한 방법은
a=b+c.str;

이렇게 str맴버를 직접 사용하는 것.


---------------------------------------

그럼 도대체 miniString이 custom string으로서 가지는 기능이 대체 무엇인가 하면...
vector안에 miniString을 집어 넣어도 메모리 누수 없이 작동한다는 것이다.
생성자랑 파괴자와 복사(=연산자) 등 필수 함수들을 정의하고 있기 때문이다.



소스는 아래에 공개.
소스코드를 이쁘게 보이게 해보려고 구글에서 몇 가지 찾아봤는데 뭐 전부 귀찮은 방법들 뿐이라...



class miniString
{
public:
char *str;

miniString()
{
str=NULL;
}
miniString(const miniString &d)
{
str=str=new char[strlen(d.str)+1];
strcpy(str,d.str);
}
void set(const char *d)
{
if(str) delete[] str;
str=new char[strlen(d)+1];
strcpy(str,d);
}
void operator =(const string &d)
{
set(d.c_str());
}
void operator =(const miniString &d)
{
set(d.str);
}
~miniString()
{
if(str) delete[] str;
}

bool operator == (const char *pa)
{
if( strcmp(str,pa)==0 ) return true;
return false;
}
bool operator == (const string &pa)
{
return pa==str;
}

//operator const char*()
//{
// return str;
//}
operator string()
{
return string(str);
}
};




by 오린간 | 2012/05/19 21:08 | 프로그래밍 | 트랙백 | 덧글(0)
순환문 그 반복되는 연습. 순환문 만들기 노하우 2
1탄은 다음 링크.


1탄이 그닥 인기는 없었던거 같지만...그래도 내 블로그는 기본적으로 프로그래밍 블로그 ㅠㅠ
적기로 했으니 오늘 마저 2탄을 적어볼까한다.

---------------------------------------------------------------------------------------------------------------

순환문을 만들다 보면 생기는 일상다반사 중 하나가 2개나 3개씩 읽는 경우다.
예를 들어 10개의 요소가 있을 때 2개씩 읽는다 치면
1, 2번을 읽고
2, 3번을 읽고
...
9, 10번을 읽고 끝

이런 식으로 읽어서 처리하는 경우다.
이렇게 하면 순환을 9번 한다.
약간 다른 방법으로
1,2번을 읽고
3,4번을 읽고
....
이렇게 하면 5번을 순환한다.

하여간에 순환문은 비슷하다.

for(int a=0; a+1<(int)v.size(); a++ ) // 위에서 5번 순환하는 후자의 형태인 경우 a+=2 로 고치면 됨

여기서 주목해야하는 부분은 중앙에 a+1이다.

왜 a+1인가. a<v.size()-1 을 하지 않은건 왜일까?

이유는 오버플로우 방지용으로 작성하는 전형적인 if문의 모양과 같아서 직관성(일관성)이 높기 때문이다.
무슨 말인가 하면 주로 n번째 배열 요소에 접근할 때, 그 요소가 배열(백터)의 범위에 존재하는지, 접근해도 되는 값인지
검사하는 if문은 주로 다음의 형태를 띈다.
if( n < (int)v.size && check(v[n]) .... )

이러다 보면 n=a+1 인 상황에서는 a+1을 그대로 쓴다. 괜히 백터의 범위에 -1 연산을 하지는 않는다.

이거 시덥잖아 보여도 중요하다.

-------------------------------------------------

for의 일반적인 형태를 보면
for(int a=0; a<MAX; a++)
{
...
}

이런 식인데, 여기서 a++이 중괄호 {} 안에 나타나야 하는 경우도 있다.
그리고 어떤 경우는 for() 안에 a++이 없고 중괄호 안에서만 적는게 직관성이 좋은 경우도 있다.

예를 들면 2바이트 문자를 고려해서 문자를 처리하는 경우다.
지금 읽고 있는 문자가 1바이트면 a++, 2바이트면 a+=2를 하는 경우다.

그리고 사실 이런 형태로 순환문을 만들려면 for보단 while이 더 어울린다.
int a=0;
while(a<MAX)
{
...
a++;
}

참, 이런 경우에는 continue를 매우 조심해야한다.
a++이나 a+=2를 하지 않고 continue를 하면 무환순환되기 때문이다.
for()안에 a++을 적은 경우에는 continue를 해도 적용되다보니 착각해서 자주 실수하곤 했다.

-----------------------------------------------

이거도 사소한 팁인데
순환문을 작성 시작할 때 순환문의 마지막 중괄호를 내용 보다 먼저 적어두고 시작하는게 낫다.
툴마다 차이가 있는데 주로 자바쪽 IDE나 새로 나온 툴들은 {를 입력하면 자동으로 }도 입력해준다.

----------------------------------------
이건 순환문에 관한건 아니고 경고4996과 GCC이야기다.

4996이란 strcpy_s 같은 함수를 쓰라고 vc에서 경고할 때 나오는 경고번호다.
strcpy는 배열의 길이를 고려하지 않아서 에러를 낼 수 있으며 이것이 해킹?(크랙?)을 유발할 수 있다나 뭐라나.
그래서 안전하게 strcpy_s를 쓰라고 4996경고를 한다.
그래서 strcpy_s를 사용해서 아무 문제가 없으면 되는데
리눅스 GCC에선 strcpy_s함수가 존재하지 않는다는게 문제다.

그래서 만약에 GCC로 컴파일할 경우에 메크로로 strcpy_s를 정의한다면 어떨까 하고 생각해봤는데
왠지 그것도 영 걱정이였다.
그래서 내가 내린 결론은 이렇다.
GCC도 다 생각이 있어서 strcpy_s를 만들지 않았던게 아닐까 하고...
요즘은 그래서 그냥 특정 경고 끄기를 해서 4996을 없애고 strcpy를 사용하고 있다.

실제로는 strcpy보다 strcmp나 fopen 같은 함수 때문에 4996을 많이 만났다.
그러다보니 일일이 _s 함수들을 처리하는게 곤욕이였다.
대부분의 경우 나는 strcpy나 strcmp등의 함수를 사용할 때 길이 체크를 일일이 하거나
넉넉하게 동적으로 할당하곤 한다. 또는 strcpy_s와 비슷하게 동작하는 strcpy_sjc 같은 나만의 함수를 만들어서
사용하곤 한다. 왜냐하면 strcpy_s를 정의하는 나의 메크로를 GCC에서만 컴파일되게 다른 메크로로 꼬아버리면
소스가 이런 사소한(?) 문제로 쓸대없이 복잡해지는 것이 내 성격을 너무 긁어버렸기 때문이다.
(*앞서 말했지만 strcpy뿐만 아니라 4996은 상당히 다양한 함수에 걸쳐있다. 생각보다 해더파일이 난잡해진다.*)

그리고 추가적인 우려사항은 누군가 내 라이브러리를 사용하는 사람이 자신의 소스에서도 strcpy_s를 처리하는 
다른 메크로를 가지고 있다면 충돌의 가능성이 있다는 것이다.

--------------------------------------------------------------

이렇게 반복문에 관한 사소한 노하우는 끝이다.
1탄에 비해 2탄은 너무 사소한게 아닌가 싶다.
by 오린간 | 2012/05/14 17:04 | 프로그래밍 | 트랙백 | 덧글(0)
win32 api c++ 폴더 안 파일 리스트 뽑기
내가 분명히 예전에도 여러번 이걸 사용했는데 간만에 또 하려니 엄청 해맸다.
그래서 이번엔 확실히 블로그에 남기겠다!!

afx.h 였나? 이거랑 관련해서 MFC의 몇 가지 new연산자 같은게 기본 CRT랑 충돌이 어쩌구 하는
링크 버그 때문에 짜증나서 그냥 win32 api를 사용하는 방법이다.

그런데 이거도 기본 프로젝트 설정(win32 콘솔 프로그래밍으로 생성시) 으로 하면
wchar_t 가 어쩌고 하면서 귀찮은 일이 발생하는데
그냥 파일 리스트를 초 심플하게 vector<string>으로 바꿔주도록 해놨다. 물론 char로 (CHAR이 아니라)

소스도 올리겠지만 바로 여기다 소스를 적기도 하겠다.
#include <windows.h>
#include <stdlib.h>

#include <vector>
#include <string>
using namespace std;

void pushBackFile(vector<string> &vecStr, WIN32_FIND_DATA &fd)
{
if( fd.dwFileAttributes == FILE_ATTRIBUTE_ARCHIVE  )
{
char str[1000];
wcstombs(str, fd.cFileName, 1000);
vecStr.push_back(str);
}
}

void fileList(const char *pass, vector<string> &vecStr)
{
wchar_t wPass[1000];
mbstowcs(wPass, pass, 1000);

WIN32_FIND_DATA fd;
HANDLE hFind = FindFirstFile(wPass, &fd);


if(hFind  == INVALID_HANDLE_VALUE)
{
return;
}

pushBackFile(vecStr, fd);

while(FindNextFile(hFind, &fd)) 
{
pushBackFile(vecStr, fd);
}

FindClose(hFind);
}

by 오린간 | 2012/03/30 19:19 | 프로그래밍 | 트랙백 | 덧글(6)
계약 하기 전에 데모를 만들었는데
그리고 워크샵 한번해서 계약에 대해서 얘기를하고, 데모를 보더니 이런거는 좀 수정되면 좋겠다~ 그래서 내가 수정을 더해서 이메일로 라이브러리와 사용 예제를 보내고 참조로 교수님에게도 보내드렸다......

그랬더니 
교수님 왈 : 어 이거 거의 완성본.....


읭?? 아직 계약도 안했는데
내가 지금 무슨 짓을 한....엉? 잠깐. 설마?


아 약간 불안하네. 아주 약간만...쪼금
by 오린간 | 2012/03/19 16:08 | 혼자만의 잡담 | 트랙백 | 덧글(2)
< 이전페이지 다음페이지 >