도움받은 사이트 :

https://leeeegun.tistory.com/3

https://stackoverflow.com/questions/6946217/how-to-access-the-contents-of-a-vector-from-a-pointer-to-the-vector-in-c

 

첫번째 링크 글에서 2차원 벡터 사용법을 쉽게 익힐 수 있었다.

 

그렇게 생성한 2차원 벡터를 포인터로 넘길 일이 있었는데 뜻대로 안되어 살펴보니

포인터로 넘겨진 2차원 벡터는 행렬기호 (대괄호) 를 사용할 수 없고 at() 함수를 통해 접근해야 했다.

(위 스택오버플로우 링크 참조)

 

만약 대괄호를 계속 사용하고 싶으면  레퍼런스(Reference)로 넘겨 사용해야 한다.

 

깔끔하게 레퍼런스로 넘기면 되겠지만,

선언과 동시에 할당해야 하는 레퍼런스 특성상 사용하기 까칠한 상황이 발생했다.

 

어쨌든, 아래처럼 두가지의 경우를 정리해 본다.

#include <cstdio>
#include <vector>

using namespace std ;

int callByPointer(vector<vector<int> >* pVecData)
{
	printf("============== Call by Pointer\n") ;
	printf("Size : [%ld][%ld]\n", pVecData->size(), pVecData->at(0).size()) ;

	for(int ii = 0; ii < pVecData->size(); ii++)
	{
		for(int jj = 0; jj < pVecData->at(0).size(); jj++)
			printf("%d ", pVecData->at(ii).at(jj)) ;
		printf("\n") ;
	}
	return 1 ;
}

int callByReference(vector<vector<int> >& pVecData)
{
	printf("============== Call by Reference\n") ;
	printf("Size : [%ld][%ld]\n", pVecData.size(), pVecData[0].size()) ;

	for(int ii = 0; ii < pVecData.size(); ii++)
	{
		for(int jj = 0; jj < pVecData[0].size(); jj++)
			printf("%d ", pVecData[ii][jj]) ;
		printf("\n") ;
	}
	return 1 ;
}

int main()
{
	vector<vector<int> > vecData ;
	vector<int> vecField ;

	vecData.clear() ;
	vecField.clear() ;

	vecField.push_back(2) ;
	vecField.push_back(3) ;
	vecField.push_back(4) ;
	vecData.push_back(vecField) ;

	vecField.clear() ;
	vecField.push_back(5) ;
	vecField.push_back(6) ;
	vecField.push_back(7) ;
	vecData.push_back(vecField) ;

	printf("==============\n") ;
	printf("size : [%ld][%ld]\n", vecData.size(), vecData[0].size()) ;
	for(int ii = 0; ii < vecData.size(); ii++)
	{
		for(int jj = 0; jj < vecData[0].size(); jj++)
			printf("%d ", vecData[ii][jj]) ;
		printf("\n") ;
	}

	// by Pointer
	callByPointer(&vecData) ;

	// by Reference
	callByReference(vecData) ;
	return 1 ;
}

 

main() 함수의 출력값, callByPointer(), callByReference() 함수에서

동일하게 값을 출력함을 확인하였다.

 

인터넷 찾아보면 코드간 변환은 많이 있으나 MultiByte Char 과 WideChar의 설명은 흔치 않는 듯 하다.

이에 정리해 본다.

 

♣ MultiByte Characters

char ch[32] ; 와 같이 배열이 있다 치고, 이 배열에 한글을 입력한다. 라고 심플하게 기억하자.

char[0] 에 한글을 입력할 수 없다.

이에 여러 char 예를들어 char[0] ~ char[2] 와 같이 여러 바이트를 사용해 한글을 저장한다.

이를 MultiByte Char 라고 표현한다.

대표적으로 ANSI (CP949), UTF-8 이 있겠다.

 

 

 

 

♣ Wide Characters

넓은 캐릭터 ㅋ. Wide Char 의 경우, 위와 같이 가변길이가 아니라 모두 동일한 길이를 갖는다.

변수 Type 은 w_char 또는 TCHAR 이 될 것이다.

(TCHAR은 환경에 따라 64비트가 아닐 수도 있으나 대부분의 개발환경이 64비트이므로 TCHAR 도 포함했다.)

 

예를 들면 아래와 같다.

 

 

♣ 인코딩 선택

위 코드값 생성은 메모장을 추천한다.

메모장에서 텍스트 입력 후, 다른이름으로 저장하면 아래 인코딩 방식이 나온다.

 

원하는 타입으로 저장 후, Hex 코드를 보면 된다.

참고로, 나는 윈도우에서 HxD 프로그램을 사용해 Hex 코드를 본다.

무료이고 회사에서도 쓸 수 있다.

 

 

♣ 인코딩 변환

Windows API 는 인코딩 변환에 두 함수를 제공한다.

 - WideCharToMultiByte ( )

 - MultiByteToWideChar ( )

 

위 개념을 이해했다면 이제 함수명을 보고 이게 어떤 의미인지 파악이 쉬울 것이며 아래처럼 정리할 수 있다.

 

UTF-8 -> Unicode : MultiByteToWideChar ( ) 함수 사용

ANSI -> Unicode : MultiByteToWideChar ( ) 함수 사용

 

Unicode -> UTF-8 : WideCharToMultiByte ( ) 함수 사용

Unicode -> ANSI : WideCharToMultiByte ( ) 함수 사용

 

여기서 의문점이 생긴다.

그렇다면 UTF-8 에서 ANSI 는 어떻게 변환하는가, 또 그 반대는 어떻게 변환하는가?

한번에는 변환이 안되고 아래와 같이 Unicode 를 한번 거쳐야 한다 (라고 알고 있다.)

 

UTF-8 -> Unicode -> ANSI : 

ANSI -> Unicode -> UTF-8

 

함수 호출은 MultiByteToWideChar ( ) -> WideCharToMultiByte ( ) 가 된다.

 

 

코드 변환하는 예제는 인터넷에서 찾으면 무궁무진하며

무작정 복붙하지 말고 위 개념을 익힌 후, 코드를 보면 바로 이해가 갈 것이다.

 

삽질은 나 하나로 충분하다. ㅋㅋ

 

'삽질미학 > C,C++' 카테고리의 다른 글

2차원 벡터를 포인터로 넘겨 사용하기  (0) 2024.02.13
함수 strtok() 의 대안, strsep()  (0) 2018.07.12
[C++] 삼각형 구조의 하향식 길찾기  (0) 2017.07.18
[C++] 조합 구하기  (0) 2017.07.13
[C++] 순열 구하기  (0) 2017.07.12

도움받은 사이트 :

 - 행복한 코딩세상 (http://downman.tistory.com/231)

 - Stack OverFlow (https://stackoverflow.com/questions/8512958/is-there-a-windows-variant-of-strsep)

 

 

아래와 같이 콤마로 구분되어있는 문자열은 strtok() 함수를 사용하여 쉽게 문자열을 구분할 수 있다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <cstdio>
#include <cstring>
 
int main()
{
        char szText[256] ;
        char *pSep ;
 
        strcpy(szText, "name,age,address") ;
 
        pSep = strtok(szText, ",") ;
        printf("1 : %s\n", pSep) ;
 
        pSep = strtok(NULL, ",") ;
        printf("2 : %s\n", pSep) ;
 
        pSep = strtok(NULL, ",") ;
        printf("3 : %s\n", pSep) ;
 
        return 1 ;
}

 

만약 아래와 같이 내용없는 토큰이 연달아 나타난다면,

"name,age,,,,address"

안타깝게도 strtok 는 내용없는 부분을 그냥 무시해버린다.

 

무시하지 않도록 하기 위해 strsep() 함수를 쓸 수 있다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <cstdio>
#include <cstring>
 
int main()
{
        char szText[256] ;
        char *pSep ;
        char *pText;
 
        strcpy(szText, "name,age,,,,address") ;
 
        pText = szText ;
        pSep = strsep(&pText, ",") ;
        printf("pSep : %s\n", pSep) ;
 
        pSep = strsep(&pText, ",") ;
        printf("pSep : %s\n", pSep) ;
 
        pSep = strsep(&pText, ",") ;
        pSep = strsep(&pText, ",") ;
        pSep = strsep(&pText, ",") ;
 
        pSep = strsep(&pText, ",") ;
        printf("pSep : %s\n", pSep) ;
 
        return 1 ;
}

내용없는 token 을 skip 하기 위해 strsep 함수를 그냥 세번 호출한 부분을 볼 수 있다.

 

문제는 이 함수는 Linux 에서 제공을 하기때문에 Visual C++ 개발환경에선 strsep() 함수를 제공하지 않는다.

 

이에 아래와 같이 strsep 함수를 직접 만들어 쓰면 된다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
char* mystrsep(char** stringp, const char* delim)
{
        char* start = *stringp;
        char* p;
 
        p = (start != NULL) ? strpbrk(start, delim) : NULL;
 
        if (p == NULL)
        {
                *stringp = NULL;
        }
        else
        {
                *p = '\0';
                *stringp = p + 1;
        }
 
        return start;
}

 

Vector의 2차원 배열 표시 도움받은 곳 : http://sosal.kr/524

 

오일러 프로젝트 풀다가 아래와 같은 구조를 생성할 일이 있어

짜긴 했는데, 이런 구조를 뭐라 하는지 모르겠네요.

 

일단 TriangleTree 라고 명하고 클래스를 만들어보았습니다.

- triangleTree.h

- triangleTree.cpp

 

그리고 main.cpp

에서 함수 호출의 예를 볼 수 있습니다.

 

 

 

 

triangleTree.h

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#ifndef _TRIANGLETREE_H_
#define _TRIANGLETREE_H_
 
#include <vector>
 
class CTriangleTree
{
public :
        int init(int width) ;
        int getNext(int* pList) ;
        int destroy() ;
 
private :
        int loop() ;
 
public :
 
private :
        int m_width ;
        int m_max ;
 
        std::vector<std::vector<int> >  m_data ;
        std::vector<int>        m_direct ;
        std::vector<int>        m_value ;
 
 
        int m_status ;
        int m_row ;
        int m_col ;
 
};
 
#endif
 

 

triangleTree.cpp

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
#include "triangleTree.h"
 
#include <cstdio>
 
#define LEFT    0x01
#define RIGHT   0x02
 
#define MOVE_FIRST_CHILD        0X04
#define MOVE_SECOND_CHILD       0X05
#define MOVE_PARENT             0X06
 
using namespace std ;
 
int CTriangleTree::init(int width)
{
        m_max = width ;
 
        m_status = MOVE_FIRST_CHILD ;
        m_row = 0 ;
        m_col = 0 ;
 
        m_data.clear() ;
        m_direct.clear() ;
        m_value.clear() ;
 
        m_direct.assign(width, LEFT) ;
 
        m_data.assign(width, vector<int>(width, 0)) ;
 
        for(int ii = 0; ii < m_max; ii++)
        {
                for(int jj = 0; jj <= ii; jj++)
                        m_data[ii][jj] = jj ;
        }
 
        m_value.assign(width, 0) ;
 
        return 1 ;
}
 
int CTriangleTree::getNext(int* pList)
{
        if(m_row  == m_max-1 && m_col == m_max-1)
                return 0 ;
 
        int ii ;
        int flagBreak = 0 ;
 
        while(1)
        {
                if(flagBreak)
                        break ;
 
                switch(m_status)
                {
                case MOVE_FIRST_CHILD :
                        m_row++ ;
                        m_value[m_row] = m_data[m_row][m_col] ;
                        m_direct[m_row - 1] = LEFT ;
 
                        m_status = (m_row < m_max-1) ? MOVE_FIRST_CHILD : MOVE_SECOND_CHILD ;
 
                        if(m_row == m_max-1)
                        {
                                for(ii = 0; ii <= m_row ; ii++)
                                        pList[ii] = m_value[ii] ;
                                return 1 ;
                        }
                        break ;
 
                case MOVE_SECOND_CHILD :
                        m_col++ ;
                        m_value[m_row] = m_data[m_row][m_col] ;
 
                        m_direct[m_row - 1] = RIGHT ;
                        m_status = (m_row < m_max-1) ?  MOVE_FIRST_CHILD : MOVE_PARENT ;
 
                        if(m_row == m_max-1)
                        {
                                for(ii = 0; ii <= m_row ; ii++)
                                        pList[ii] = m_value[ii] ;
                                return 1 ;
                        }
                        break ;
                case MOVE_PARENT :
                        if(m_direct[m_row-1] == RIGHT)
                                m_col-- ;
                        m_row-- ;
 
                        if(m_row == 0)
                        {
                                m_status = MOVE_SECOND_CHILD ;
                                m_row++ ;
                                break ;
                        }
 
                        m_status = (m_direct[m_row-1] == LEFT) ?  MOVE_SECOND_CHILD : MOVE_PARENT ;
                        break ;
                }
        }
 
        return 0 ;
}
 
int CTriangleTree::destroy()
{
        m_data.clear() ;
        m_direct.clear() ;
        m_value.clear() ;
 
        return 1 ;
}
 

 

main.cpp

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include "triangleTree.h"
 
#include <cstdio>
 
using namespace std ;
 
int main()
{
        CTriangleTree triTree ;
 
        const int MAX = 5 ;
 
        triTree.init(MAX) ;
        int value[MAX] ;
 
        while(1)
        {
                if(!triTree.getNext(value))
                        break ;
 
                for(int ii = 0; ii < MAX; ii++)
                        printf("%d ", value[ii]) ;
                printf("\n") ;
 
        }
 
        return 1 ;
}
 

 

조합을 구하려다 한참을 삽질하고,

결국 로제타 코드에서 큰 도움을 받았다.

( https://rosettacode.org/wiki/Combinations#C.2B.2B )

 

어찌나 쉽고 심플하게 짜놨는지... 허망~

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <algorithm>
#include <iostream>
#include <string>
 
void comb(int N, int K)
{
    std::string bitmask(K, 1); // K leading 1's
    bitmask.resize(N, 0); // N-K trailing 0's
 
    // print integers and permute bitmask
    do {
        for (int i = 0; i < N; ++i) // [0..N-1] integers
        {
            if (bitmask[i]) std::cout << " " << i;
        }
        std::cout << std::endl;
    } while (std::prev_permutation(bitmask.begin(), bitmask.end()));
}
 
int main()
{
    comb(5, 3);
}
 

 

위 코드를 참고하여 써먹기 쉽게 아래처럼 클래스화 시켜 보았다.

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
#include <cstdio>
 
#include <vector>
#include <algorithm>
#include <string>
 
 
using namespace std ;
 
class CCombination
{
public :
        CCombination(vector<int>* vecInt, int r) ;
 
        int getNext(vector<int>* vecCombi) ;
 
private :
        string m_bitmask ;
        vector<int>* m_vecInt ;
 
};
 
CCombination::CCombination(vector<int>* vecInt, int r)
{
        m_vecInt = vecInt ;
 
        m_bitmask.clear() ;
        m_bitmask.resize(r, 1) ;
        m_bitmask.resize(vecInt->size(), 0) ;
}
 
 
int CCombination::getNext(vector<int>* vecCombi)
{
        vecCombi->clear() ;
 
        int size = m_bitmask.size() ;
 
        for(int ii = 0; ii < size; ii++)
        {
                if(m_bitmask[ii])
                        vecCombi->push_back((*m_vecInt)[ii]) ;
        }
 
        if(!prev_permutation(m_bitmask.begin(), m_bitmask.end()))
                return 0 ;
 
        return 1 ;
}
 
int main()
{
        vector<int> vecInt ;
        vector<int> vecCombi ;
 
        int n = 6 ;
        int r = 4 ;
 
        for(int ii = 1; ii <= n; ii++)
                vecInt.push_back(ii) ;
 
        CCombination combination(&vecInt, r) ;
 
        int flagBreak ;
 
        while(1)
        {
                flagBreak = combination.getNext(&vecCombi) ;
 
                for(int ii = 0; ii < r; ii++)
                        printf("%d ", vecCombi[ii]) ;
                printf("\n") ;
 
                if(!flagBreak)
                        break ;
        }
 
        return 1 ;
}
 
 
 

뭔가 썩 마음에 들진 않는데 딱히 개선안이 떠오르지 않는다. 허망함 때문인가? ㅋㅋ

시간이 지나면 떠오를 때가 있겠지.

도움받은 사이트 : 나무위키 (https://namu.wiki/w/%EC%88%9C%EC%97%B4(%EC%88%98%ED%95%99))

 

개요 :

서로 다른 n개의 원소에서 r개를 중복없이 골라 순서에 상관있게 나열하는 것을 n개에서 r개를 택하는 순열이라고 한다.

 

ex) 4개의 원소 : 1, 2, 3, 4

이중 2개를 골라 나열하면, n = 4, r = 2.

 

1 2

1 3

1 4

2 1

2 3

2 4

3 1

3 2

3 4

4 1

4 2

4 3

 

C++ 에선 STL 에 next_permutation 이란 함수가 있지만 위와 같이 nPr 타입을 지원하진 않는다.

이에 위와 같이 nPr 타입의 순열을 출력하는 함수를 간단히 구현해 보았다.

 

(Warning : 처음 초기값이 오름차순으로 입력되어야 한다. 1,2,3,5,8 ~~~ 등 )

 

reverse() 함수도 STL 에서 제공하는 함수이다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include <cstdio>
 
#include <vector>
#include <algorithm>
 
using namespace std ;
 
int permutation(int n, int r)
{
        vector<int>             vecInt ;
        vector<int>::iterator   vecIntIter ;
 

        for(int ii = 1; ii <= n; ii++)

                vecInt.push_back(ii) ;
 
        while(1)
        {
                for(int ii = 0; ii < r; ii++)
                        printf("%d ", vecInt[ii]) ;
                printf("\n") ;
 
                reverse(vecInt.begin() + r, vecInt.end()) ;
 
                if(!next_permutation(vecInt.begin(), vecInt.end()))
                        break ;
        }
 
        return 1 ;
}
 
 
int main()
{
        permutation(4, 2) ;
 
        return 1 ;
}
 

 

위 실행으로 순열 결과는 얻었지만 응용하기엔 애매하다.

 

이에 아래와 같이 살짝 변형하였다.

 

permutation() 함수의 vector<int>* 부분은 템플릿을 사용하여 일반화시킬 수 있지만

가독성이 떨어져 특정 타입으로 선언하였다.

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include <cstdio>
 
#include <vector>
#include <algorithm>
 
using namespace std ;
 
int permutation(vector<int>* vecInt, int r)
{
        reverse(vecInt->begin() + r, vecInt->end()) ;
        return next_permutation(vecInt->begin(), vecInt->end()) ;
}
 
 
int main()
{
        vector<int>             vecInt ;
        vector<int>::iterator   vecIntIter ;
 
        int n = 4 ;
        int r = 2 ;
 
        for(int ii = 1; ii <= n; ii++)
                vecInt.push_back(ii) ;
 
        while(1)
        {
                for(int ii = 0; ii < r; ii++)
                        printf("%d ", vecInt[ii]) ;
                printf("\n") ;
 
                if(!permutation(&vecInt, r))
                        break ;
        }
 
        return 1 ;
}
 

 

roughTimer_C_170622.zip

roughTimer_CPP_170622.zip

 

 

제목 : Rough Timer

언어 : C, C++

OS : Ubuntu 14.04

컴파일러 : gcc, g++

라이센스 : 누구나, 어디서나 쓸 수 있습니다. (누가 쓰겠냐마는...)

 

usleep() 함수를 이용해 타이머를 만들어 보았다. (첨부파일 참조)

 

예를 들어 2초짜리 타이머를 등록하면,

쓰레드에서 100ms 마다 1씩 카운트하여 2초가 됐을 때 콜백함수를 호출하는 방식이다.

 

고로, 정확도는 떨어지며 100ms 이하 타이머는 사용할 수 없다.

정확도 보다도 "약 몇초 후에" 식으로 운영되는 타이머라면 심플하게 쓸 수 있을 것이다.

 

 

이에 Rough Timer 로 명명하였다.

 

C++ 코드는 STL List 를 사용하였고,

C  코드는 간단히 linked-list 함수를 만들어 사용하였다.

 

코드 안에 main() 함수에서 간단한 테스트 코드를 볼 수 있다.

 

(주의사항 : 타이머 발동 시, 호출되는 콜백함수는 최대한 빨리 빠져나와야 한다.

 그렇지 않으면 전체 타이머 시간과 다른 타이머에 영향을 준다.)

 

 

 

msgQueue_CPP_170616.zip

 

 

컴파일 환경 : Ubuntu 14.04

 

(추후, C 기반 포팅작업 필요함.)

 

모듈의 독립성을 높이기 위해 메시지큐를 사용하여 task 를 처리하는 틀을 작성한다.

 

예를 들어 Core 부분과  UI Display 부분이 있을 때,

Core는 UI의 존재를 모르도록 구성한다.

 

Core 는 자신의 task 를 메시지큐에 전달하면 main 에서 이를 판별하여

이 후 작업(UI 갱신 등)을 진행하도록 한다.

 

이와 같은 작업을 위해 코드를 작성하였으며 추후 투입될 작업에 맞춰

모듈과 task 정의를 추가한다.

select() 함수를 예제보고 가져다 쓰기만 하다가 뭔가 수정만 하면 잘 안되어

이대로는 안되겠다 싶어 개념을 좀 더 파악하기로 하였다.

 

♣ fd_set 구조체

fd_set 구조체는 File Descriptor (이하 FD) 를 저장하는 구조체이다.

안에 내용을 보면 그냥 배열로 여기면 편하다.

 

구조체 내용은 OS마다 조금씩 다른 듯 하다.

내가 쓰는 Ubuntu 14 에선 /usr/include/sys/select.h 에서 위 구조체를 확인할 수 있었다.

 

♣ FD_SET

fd_set 구조체에 2와 5의 FD 를 저장한다고 하면,

두번째 비트와 다섯번째 비트가 1로 변경된다.

값 저장은 FD_SET 매크로를 쓴다.

 

fd_set testFds ;

FD_SET(2, &testFds) ;

FD_SET(5, &testFds) ;

 

위 testFds 구조체의 배열값을 출력해 보면,

0 1 0 0 1 0 0 0 0 ~~~

위와 같이 2번째와 5번째 비트가 1 로 세팅된다.

 

♣ FD_ZERO

언급했듯이 fd_set 구조체의 주요 변수는 배열이다.

fd_set testFds ; 와 같이 선언만 하고 안에 배열값을 찍어보면 쓰레기값이 있을 수 있다.

 

이에 구조체를 초기화 해야 하며 아래와 같이 한다.

FD_ZERO(&testFds) ;

 

위에 FD_SET 예제에서도 변수 선언 후, 초기화를 해 주어야 한다.

 

fd_set testFds ;

FD_ZERO(&testFds)

FD_SET(2, &testFds) ;

FD_SET(5, &testFds) ;

 

♣ FD_CLR

FD_SET(2, &testFds) ;

FD_SET(3, &testFds) ;

FD_SET(5, &testFds) ;

 

위와 같이 입력 후, testFds 는 아래와 같이 세팅된다.

0 1 1 0 1 0 0 0 0 ~~~

 

이 때 만약 3번 FD 를 지우고 싶으면 FD_CLR 매크로를 쓴다.

FD_CLR(3, &testFds) ;

 

결과는 아래와 같다. 세번째 값이 0으로 초기화 되었다.

0 1 0 0 1 0 0 0 0 ~~~~

 

♣ FD_ISSET

이 매크로는 아래 select 함수와 함께 설명한다.

 

 

♣ select()

select 함수는 fd_set 구조체에 할당된 FD 의 이벤트가 발생하면

이를 감지하고 어떤 FD 이벤트가 발생했는지 알려준다.

 

ex)

fd_set testFds ;

FD_SET(2, &testFds) ;

FD_SET(5, &testFds) ;

 

select(6, &testFds, NULL, NULL, NULL) ;

 

이는 FD 이벤트는 6 미만값만 체크하며,

6 미만 중 2와 5 FD 의 이벤트가 발생했을 때만 깨어난다.

 

만약 2번 FD 의 이벤트가 발생하여 select 함수가 깨어나고,

select 함수 이후에 testFds 를 찍어보면,

 

0 1 0 0 0 0 0 0 0 ~~~~~~~

식으로 값이 변경된다. -> 이걸 몰라 한참 헤맸다.

 

즉 testFds 에 2, 5 FD 를 저장한 후,

저장된 FD (2,5) 중 이벤트가 발생하면,

해당 이벤트만 1 로 남기고 모두 0 으로 세팅한다.

 

이제 어떤 FD의 이벤트가 발생했는지 체크하여 FD_ISSET 매크로를 사용한다.

 

if(FD_ISSET(2, &testFds))

{

  // 2번 FD 가 발생했다.

}

 

if(FD_ISSET(5, &testFds))

{

  // 5번 FD 가 발생했다.

}

위와 같이 해당 FD에 대한 후처리를 진행하면 된다.

 

 

♣ 틀 정립

select 함수는 위 예제처럼 testFds 를 변경시킨다.

그럼 select 함수 전이나 후로 매번 testFds 를 다시 세팅해 주어야 한다.

 

이에 아래와 같은 틀이 될 것이다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
fd_set readFds ;
FD_ZERO(&readFds) ;
FD_SET(2, &readFds) ;
FD_SET(5, &readFds) ;
 
while(1)
{
      // select 함수에서 fd_set 이 변경되니 원본 대신 복사본을 select 함수에 전달한다.
      checkFds = readFds ;
 
      printf("sleep - select()\n") ;
      select(6, &checkFds, NULL, NULL, NULL) ;
      printf("wake up - select()\n") ;
 
      if(FD_ISSET(2, &checkFds))
      {
          // 2번 FD 이벤트에 대한 사후 처리.
      }
 
      if(FD_ISSET(5, &checkFds))
      {
          // 5번 FD 이벤트에 대한 사후 처리.
      }
}

 

♣ 정리

이런 저런 코드를 보면서 원본 (readFds) 를 checkFds 로 복사하는 이유를 몰랐는데

select() 함수 내에서 fd_set 구조체 값을 변경하기 때문이다.

 

이를 위와 같이 정리하며 아래 내용이 빠져있다.

  • select() 함수의 리턴값 처리
  • select() 함수의 첫번째 인자 max FD 값의 처리
  • select() 함수의 세번째, 네번째 인자 writeFds, exceptFds 에 대한 처리.
  • select() 함수의 다섯번째 인자 타이머에 대한 처리.

 

도움받은 사이트 : http://blog.naver.com/dolzzo/130131251201

 

뭔가 간단한 프로그램을 짜고 있는데 프로그램 종료할 때마다 segmentation fault 가 뜨길래

디버깅해보니 육안상 이상이 없었다.

 

gdb 로 코어 뜨고 한참을 삽질한 끝에야 원인을 찾을 수 있었다.

 

나처럼 조인시위키 (https://www.joinc.co.kr/w/man/3/pthread_join) 를 통해 쓰레드를 익혔으면

아래 형태를 많이 봤을 것이다.

 

int status ;

pthread_join(thread, (void**)&status) ;

 

이 코드가 32bits OS 에선 이상이 없는데 64 bits OS 로 넘어오면서 문제를 일으킨다.

 

sizeof(void**) 값은 8인데 여기에 4Bytes 짜리 int 타입의 변수를 넣었기 때문에 메모리 충돌을 일으킨 것이다.

(64bits Linux 로 넘어온지 3년쯤 되는데 그동안 왜 아무일 없었던거지?)

 

해결책으론 아래와 같이 int 대신 long 을 쓰거나,

 

long status ;

 

아니면 아예 void* 로 선언하는 경우가 있겠다.

(앞으론 아래처럼 써야겠다.)

 

void* status ;

pthread_join(thread, &status) ;

 

참고로, 정보문화사의 Beginning Linux Programming (3판) 의 예제를 보면

아래와 같이 status 값을 확인하고 있다.

 

printf("status : %s\n", (char*)status) ;

 

 

초딩 1학년인 딸아이가 학교에선 뺄셈 들어가는데 아직 덧셈도 잘하지 못해

안되겠다 싶어 여러 문제를 만들어 봄.

 

 - A+B 가 10보다 작은 경우만 수집함. -> 총 55가지인가 나옴.

 - 결과를 랜덤으로 섞음.

 - 몇장 만들 것인지 설정할 수 있음.

 

 - 추가로 빼기 경우도 만들어봄.

 - 결과가 음수인 경우는 배제. 큰 수의 최대값은 9임.

 

Ubuntu 14.04 에서 컴파일 함.

 

결과물 txt 파일을 MS-Word 로 복사해 출력하여 교재로 활용함.

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
#include <cstdio>
#include <cstdlib>
 
#include <algorithm>
#include <vector>
 
#define SEED_FILE "seedValue.dat"
 
#define PAGE_NUMBER     20    // 생성할 페이지 수
 
#define ADD            // 덧셈 예제를 생성함.
//#define SUB         // 뺄셈 예제를 생성함.
 
// ADD, SUB 둘 다 define 되어있거나, 
// 아무것도 define 되어있지 않은지 체크함.
#if defined(ADD) && defined(SUB)
#error "defined both ADD and SUB."
#endif
 
#if !defined(ADD) && !defined(SUB)
#error "NOT Defined anything."
#endif
 
// srand() 함수에 사용될 seed 값을 증가함.
// 이전값을 기억하여 랜덤 결과가 겹치지 않도록 함.
unsigned int getSeed()
{
        FILE* fp = fopen(SEED_FILE, "rt") ;
        if(!fp)
        {
                FILE* fp = fopen(SEED_FILE, "wt") ;
                fprintf(fp, "1") ;
                fclose(fp) ;
                return 1 ;
        }
 
        unsigned int seed ;
        char szText[128] ;
 
        fgets(szText, sizeof(szText), fp) ;
        seed = atoi(szText) ;
 
        fclose(fp) ;
 
        return seed + 1 ;
}
 
// 예제에 사용될 샘플을 수집하여 vector에 저장함.
int makeList(std::vector<int>* pVecInt)
{
        int result ;
        for(int ii = 0; ii <= 9; ii++)
        {
                for(int jj = 0; jj <= 9; jj++)
                {
#ifdef ADD
                        result = ii + jj ;
 
                        if(result >= 10)
                                continue ;
#elif defined SUB
                        result = ii - jj ;
 
                        if(result < 0)
                                continue ;
#endif
 
                        result  = (ii * 10) + jj ;
                        pVecInt->push_back(result) ;
                }
        }
 
        return 1 ;
}
 
// 수집한 샘플을 파일로 저장함.
int makeFile(std::vector<int>* pVecInt, int seed, FILE* fp)
{
 
        int a, b ;
        int cnt = 0 ;
        const int ROW = 4 ;
        std::vector<int>::iterator      vecIntIter ;
 
 
        fprintf(fp, "Seed Index : %d\r\n", seed - 1 ) ;
 
        vecIntIter = pVecInt->begin() ;
        for(; vecIntIter != pVecInt->end(); vecIntIter++)
        {
                a = *vecIntIter / 10 ;
                b = *vecIntIter % 10 ;
 
 
                if(cnt % ROW != 0)
                        fprintf(fp, "      ") ;
 
#ifdef ADD
                fprintf(fp, "%d + %0d = ", a, b) ;
#elif defined SUB
                fprintf(fp, "%d - %0d = ", a, b) ;
#endif
 
                if(cnt % ROW == ROW - 1)
                        fprintf(fp, "\r\n") ;
 
                cnt++ ;
        }
 
        return 1 ;
}
 
int main()
{
        std::vector<int>                        vecInt ;
        static char szName[128] ;
 
        unsigned int seed = getSeed() ;
        srand(seed) ;
 
#ifdef ADD
        printf("ADD mode : %d\n", seed) ;
        sprintf(szName, "addTest_%d-%d.txt", seed, seed - 1 + PAGE_NUMBER) ;
#elif defined SUB
        printf("SUB mode : %d\n", seed) ;
        sprintf(szName, "subTest_%d-%d.txt", seed, seed - 1 + PAGE_NUMBER) ;
#endif
 
        FILE* fp = fopen(szName, "wt") ;
 
        for(int ii = 1; ii < PAGE_NUMBER; ii++)
        {
                seed++ ;
 
                if(seed == 0x8fffffff)
                        seed = 1  ;
 
                FILE* fpSeed = fopen(SEED_FILE, "wt") ;
                fprintf(fpSeed, "%d", seed) ;
                fclose(fpSeed) ;
 
                vecInt.clear() ;
 
                makeList(&vecInt) ;
 
                // 수집한 샘플을 섞음.
                random_shuffle(vecInt.begin(), vecInt.end()) ;
 
                makeFile(&vecInt, seed, fp) ;
 
                fprintf(fp, "\r\n") ;
                fprintf(fp, "\r\n") ;
 
        }
 
        fclose(fp) ;
 
        return 1 ;
}
 

tcpTest_C_170616.zip

tcpTest_CPP_170616.zip

 

**** 수정 이력

* 2017-06-16 : 종료 시, pipe() 함수에서 얻어진 File Descriptor로 처리하도록 함.

********************

 

 

첨부파일 압축을 풀면 server, client 두개의 디렉토리가 생성된다.

각각 디렉토리에서 컴파일 후 실행하여 TCP Server, Client 간 연결을 확인한다.

 

정보문화사의 Beginning Linux Programming 3판에서

15장 소켓, 다중 클라이언트 부분을 크게 참고하였고 적당히 수정하였다.

 

's' + [enter] 를 누르면 상대방에 데이터를 보내고,

'q' + [enter] 를 누르면 프로그램을 종료한다.

(이 부분은 main.cpp 참조)

 

- 종료 시, recv() 함수에서의 행업 이슈가 없다.

- 각 단의 시작 순서에 상관 없이 잘 동작한다. (client 를 먼저 실행해도 상관없다.)

- 연결 후, 서버단을 죽이고 다시 시작해도 재연결이 잘 이루어진다.

- 연결 후, 클라이언트단을 죽이고 다시 시작해도 재연결이 잘 이루어진다.

 

 

개인 보관용 목적의 글이므로,

 

 

이글은 예전 제 싸이월드 블로그에 작성한 글을 티스토리로 옮긴 것입니다.
원문 : http://cy.cyworld.com/home/21147242/post/55078597BC95739A07C68401
원문 작성일 : 2015.3.12

============================================================

 

임베디드 보드에서 유니코드를 처리할 일이 있어
일단 리눅스에서 테스트를 하였습니다.
별 탈 없이 문자열(ex) 한글) 잘 뿌리는 거 확인하고
테스트 보드로 포팅하여 테스트했더니 엇~ 문자열이 안나오는겁니다.
테스트보드에선 유니코드를 지원 안하나봅니다.
그래서 아래와 같이 테스트를 해 보았습니다.
1. 윈도우에서 메모장을 열고 한글을 입력합니다.
2. "다른이름으로 저장" 하게 되면 하단에 아래 그림과 같이 4가지 타입으로 저장할 수 있습니다.
3. 4개의 파일로 따로 저장합니다.
ex) test_ANSI.txt, test_Unicode.txt, test_Unicode_BE.txt, test_UTF-8.txt

 

 

그리고 생성한 4개의 txt 파일을 보드에 복사하여 열어봤습니다.
했더니 UTF-8 만 정상으로 나오고 나머지는 다 깨지네요.
아하~ 테스트보드는 UTF-8만 지원하나봅니다.
그럼 Unicode를 UTF-8 로 변환하여 출력하면 되겠군요.
Unicode ->UTF-8 로의 변환을 그림으로 표시하면 아래와 같습니다.
(도움받은 사이트 : http://en.wikipedia.org/wiki/UTF-8 )

 

위 사이트에 들어가보면 unicode 값이 0xffff 값보다 더 큰 경우에도 설명이 되어있는데
일단 그 범위까지 쓸일이 없기에 위 범위만 처리하였습니다.
이에 아래와 같이 변환하는 함수를 만들어 보았습니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
void UnicodetoUTF8( const wchar_t * pUnicode, char * szText)
{
        int uniLen = wcslen(pUnicode) ;
 
        for ( int ii = 0; ii<uniLen; ii++)
        {
                if ( 0<= pUnicode[ii]&&pUnicode[ii]<= 0x7f)
                {
                        szText[0] = pUnicode[ii]&0xff ;
                        szText++ ;
                }
                else if ( 0x80<= pUnicode[ii]&&pUnicode[ii]<= 0x7ff)
                {
                        szText[0] = ((pUnicode[ii]>>6)&0x1f) | 0xc0 ;
                        szText[1] = (pUnicode[ii]&0x3f) | 0x80 ;
                        szText += 2 ;
                }
                else if ( 0x800<= pUnicode[ii]&&pUnicode[ii]<= 0xffff)
                {
                        szText[0] = ((pUnicode[ii]>>12)&0x0f) | 0xe0 ;
                        szText[1] = ((pUnicode[ii]>>6)&0x3f) | 0x80 ;
                        szText[2] = (pUnicode[ii]&0x3f) | 0x80 ;
                        szText += 3 ;
                }
                else
                {
                        printf( "ERROR~!! pUnicode>0xffff, 0x%x\n" , pUnicode[ii]) ;
                }
        }
        szText[0] = '\0' ;
 
        return ;
}
 

그럼, 즐프하세요~ ^^

 

 

 

 

 

 

 

 

 

 

 

 

 

 

이글은 예전 제 싸이월드 블로그에 작성한 글을 티스토리로 옮긴 것입니다.
원문 : http://cy.cyworld.com/home/21147242/post/5500EDA6EA95739A07C68401
원문 작성일 : 2015.3.12

============================================================

 

list 컨테이너에서 조건에 맞는 값을 찾고자 할 때 find_if() 함수를 쓴다.
조건값이 특정값이라면 함수객체(펑크터)를 사용해 쉽게 구현이 되지만
조건값을 변수 형태로 취하려면 어댑터 개념을 적용해야 한다.

C++ 기초 플러스, C++ Standard Library 책을 눈알 빠지게 봐도 뭔소린지 모르겠다.
결국 SOEN(http://www.soen.kr) 에서 도움을 받았다.
(SOEN ->C/C++ 강좌 ->4부.C++고급 ->38장 함수객체 ->바인더)
한국인이 쓴 책이 문장도 좋고 이해가 잘 될 때가 분명히 있다.

아래 코드는 보관용 수준으로 작성한 예제이다.
- list 에 4개의 값을 저장하였다.
- 이 중 나는 number 값이 1003인 이터레이터를 가져오고 싶다.
- 1003이 아닌 다른 값을 가져오고 싶다면, filter 변수값을 변경하면 된다.

아래 예제는 number가 1003인 구조체값을 가져와 그 구조체의 major 값을 찍었다.
원하는대로 값을 가져오고 있다.

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#include <cstdio>
#include <list>
#include <algorithm>
#include <functional>
 
using namespace std ;
 
 
struct Program
{
        int major ;
        int number ;
        int pid ;
} ;
 
class FindProgram : public binary_function<Program, int , bool >
{
public :
        bool operator() ( const Program& p, int b) const
        {
                return (p.number == b) ;
        }
} ;
 
int main()
{
        list<Program> listProgram ;
        list<Program> ::iterator listProgramIter ;
 
        Program program ;
 
        program.major = 7 ;
        program.number = 1001 ;
        program.pid = 0x20 ;
        listProgram.push_back(program) ;
 
        program.major = 7 ;
        program.number = 1002 ;
        program.pid = 0x21 ;
        listProgram.push_back(program) ;
 
        program.major = 9 ;
        program.number = 1003 ;
        program.pid = 0x22 ;
        listProgram.push_back(program) ;
 
        program.major = 11 ;
        program.number = 1004 ;
        program.pid = 0x23 ;
        listProgram.push_back(program) ;
 
        int filter = 1003 ;
 
        listProgramIter = find_if(listProgram.begin(), listProgram.end(), bind2nd(FindProgram(), filter)) ;
 
        if(listProgramIter != listProgram.end())
        {
                printf("val : %d\n", listProgramIter->major) ;
        }
 
        return 1;
}
 


 

이글은 예전 제 싸이월드 블로그에 작성한 글을 티스토리로 옮긴 것입니다.
원문 : http://cy.cyworld.com/home/21147242/post/5440C0C849D5739A07C68401
원문 작성일 : 2014.10.17

============================================================

 

github : https://github.com/hahaite/Big_Integer

 

File : bigInt_170713.zip

 

오일러 프로젝트 (http://euler.synap.co.kr/) 를 풀다가
엄청나게 큰 수를 계산해야 하는 상황이 발생했다.
이에 제목과 같은 걸 함 짜보자싶어 구현하게 되었다. (첨부파일 참조)
덕분에 자릿수 제약이 없는 정수 계산이 가능하였다. ex) 1000!
큰 수의 계산에 촛점을 두었기에 많은 횟수의 연산은 속도가 느릴 수 있다.

간단한 사용법은 아래와 같다.

CBigInt A, B, C ;
A = 12345 ; // int 형 입력 가능.
B = "111234574734892783" // 매우 큰 수는 string 형으로 입력함.
C = A + B ;
printf("sum : %s\n", C.c_str()) ;

 

기타 사칙연산 증가,감소연산, 크기 비교 등이 가능하다.

 

추가로 Euler Project를 풀면서 몇몇 필요한 아래 기능이 추가되었다.
 - n! (Factorial) 계산.
 - string 뒤집기
 - palindrome 인지 체크
 - string 정렬
 - 조합(combinations) 계산.
 - 분수 계산.
 - 제곱 (pow) 계산.

 

====================================================
기타 예제는 첨부파일의 main.cpp 에 있다.
테스트하고 싶은 부분을 #if 0 ->#if 1 로 바꾸고 컴파일 한 후 결과를 확인할 수 있다.
표준 함수를 사용하였으므로 개발환경의 영향을 받지 않으리라 본다.
테스트는 Fedora13, GCC 4.4.5 기반에서 테스트를 진행하였다.
================================

뭐 누가 사용할까 싶긴 한데요.
혹시나 사용하다 버그 발견이나 수정사항 있으면 댓글로 알려주세요.
참, 이 코드는 FREE~!! 입니다.

+ Recent posts