Const

const 키워드는 해당 변수를 상수로 만들어준다. (상수는 선언과 동시에 초기화를 해줘야하며 변경이 불가능하다.)

어떤 값(객체의 내용)이 불변이어야 한다는 제작자의 의도를 컴파일러 및 다른 프로그래머와 나눌 수 있는 수단.

 

- 클래스 외부에서는 전역 혹은 네임스페이스 유효 범위의 상수를 선언하는데 사용 가능.

- 파일, 함수, 블록 유효 범위에서 static으로 선언한 객체에도 const를 붙일 수 있음.

- 클래스 내부의 경우에는, 정적 멤버 및 비정적 데이터 멤버 모두를 상수로 선언 가능.

- 포인터에도 const 키워드 사용 가능.

 

const (포인터)

 

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
    char greeting[] = "Hello";
    char greeting2[] = "Nice";
 
    char* p = greeting;
 
    //쉽게 생각해서 const가 * 왼쪽에 있으면 const char*를 한 묶음으로 보면된다.
    //*은 해당 포인터가 가리키고 있는 주소값의 Value니까 (여기서는 *p는 'H')
    //결론적으로 p가 가리키고 있는 주소값의 Value를 변경하지 못한다고 생각하면 된다.
 
    const char* p1 = greeting;
 
    p1 = greeting2;    //가능.
 
    *(p1) = 'S';    //Error.
    *(p1+1= 'C';    //Error.
 
    //const가 * 오른쪽에 있으면 const p를 한묶으로 보면 된다.
    //p는 문자 'H'의 주소값이니까 p의 주소값을 변경하지 못한다고 생각하면 된다.
    //즉, p가 다른 주소값을 가리키도록 하지 못하므로
 
    char* const p2 = greeting;
 
    p2 = greeting2;    //Error.
 
    *(p2) = 'S';    //가능.
    *(p2+1= 'C';    //가능.
 
    //위에 두가지 case 모두 불가능.
    const char* const p3 = greeting;
 
cs

 

const (함수)

 

1. 함수 반환에 const.

 - 안전성이나 효율을 포기하지 않고도 사용자측의 에러 돌발 상황을 줄이는 효과를 볼 수 있다.

 - 예를 들어, 연산자 오버로딩 사용 시 if ((a*b) == c)를 if ((a*b) = c)와 같은 실수를 방지할 수 있다.

 

2. 함수의 매개 변수에 const

 - 함수 내에서 객체를 수정할 일이 없으면 무조건 const로 선언하라.

 

3. 멤버 함수 뒤에 const

 - "해당 멤버 함수가 상수 객체에 대해 호출될 함수이다"라는 사실을 알려주는 것이다.

 - 이 기능이 중요한 이유

   1) 클래스의 인터페이스를 이해하기 좋게 하기 위함.

   2) 상수 객체에서만 호출할 수 있도록 하기 위함.

     (c++ 프로그램의 실행 성능을 높이는 핵심 기법 중 하나는 객체 전달을 '상수 객체에 대한 참조자'로 전달하는 것.)

     (전달된 '상수 객체'에서 사용할 멤버 함수가 있어야 하기 때문이다.)

 - const 키워드가 있고 없고의 차이만으로도 함수 오버로딩이 가능하다.

 

const (객체)

 

const 객체는 객체의 멤버 변수를 어떠한 경우에도 수정할 수 없으며 const 멤버 함수만 호출 할 수 있다.

위에서 말했 듯이 상수 객체에 대한 참조자로 전달할 때 프로그램 실행 성능이 높아진다.

 

비트수준 상수성

 - 어떤 멤버 함수가 그 객체의 어떤 데이터 멤버도 건드리지 않아야(정적 멤버 제외) 그 멤버 함수가 'const'임을 인정하는 개념.

 - C++에서 정의하고 있는 상수성이 비트 수준 상수성.

 - 그러나 하기와 같이 const 객체임에도 불구하고 멤버 변수의 주소값을 얻어 멤버 변수의 값을 바꿀 수 있는 결함이 있다.

 - 컴파일러는 비트수준 상수성을 기준으로 하기 때문에 멤버 함수 내에서 값을 변경한 것이 아니기 때문에 컴파일러 단에서는

   모든 규칙이 지켜졌다고 보게 된다.

 

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
class CMyString
{
public:
    CMyString(char* pString)
    {
        int nLength = strlen(pString);
        m_pString = new char[nLength];
        
        strcpy(m_pString, pString);
    }
 
private:
    char* m_pString;
 
public:
    char& operator[](int nIndex) const
    {
        return m_pString[nIndex];
    }
};
 
 
int _tmain(int argc, _TCHAR* argv[])
{
 
    const CMyString cstrVal("Hello");
 
    char* pVal = &cstrVal[0];
 
    *pVal = 'J'//문자열이 "Jello"으로 변경.
}
cs

 

 - 위와 같은 상황이 발생하지 않기 위해서는 반환형으로 const char&를 해줘야함.

 

논리적 상수성

 - 비트수준 상수성을 보완하는 대체 개념.

 - 컴파일러는 비트수준 상수성을 기준으로 동작하기 때문에 프로그래머는 논리적 상수성을 기준으로 프로그램을

   작성해야 한다.

 - 상수 멤버 함수라고 해서 객체의 한 비트도 수정할 수 없는 것이 아니라 일부 몇 비트 정도는 바꿀순 있되,

   그것을 사용자 측에서 알아채지 못하게만 하면 상수 멤버 자격이 있다는 것.

 - 상수 객체지만 mutable 키워드를 사용하면 멤버 변수의 값을 변경할 수 있게 된다.

 

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
class CMyString
{
public:
    CMyString(char* pString)
    {
        m_nLength = strlen(pString);
        m_pString = new char[m_nLength];
        
        strcpy(m_pString, pString);
    }
 
private:
    char* m_pString;
    mutable int m_nLength; //mutable 키워드로 멤버 변수 선언.
 
public:
    const char& operator[](int nIndex) const
    {
        return m_pString[nIndex];
    }
};
 
 
int _tmain(int argc, _TCHAR* argv[])
{
    const CMyString cstrVal("Hello");
 
    return 0;
}
cs

 

 

코드 중복 피하기

 

만약 위와 같은 코드에서 operator[] 함수 내에 코드가 엄청나게 길었다고 가정해보자.

상수 객체는 반드시 const char& operator[](int nIndex) const를 호출해야 하고

비상수 객체는 char& operator[](int nIndex) 함수를 호출하게 될 것이므로 두 함수는 모두 있어야 한다.

그런데 만약 이 두 함수의 내용이 같다면, 이러한 함수가 여러 개라면 코드의 크기는 엄청나게 될 것이다.

(컴파일 시간, 유지보수, 코드 크기 등 문제 발생..)

 

그렇다고 반환값을 통일시켜버리면 위의 비트수준 상수성 예시처럼 const의 안정성이 지켜지지 않는다.

 

해결 방법.

 - 두 함수의 차이점은 단지 반환 타입에 const가 붙어 있냐 없냐만 차이가 있다. ( const char&, char& )

 - 비상수 operator[] version 함수에서 상수 operator[] 함수를 호출하는 것!! (casting을 이용)

   (상수 함수에서 비상수 함수를 호출하는 것은 애초에 비상수 함수에서 무슨 값을 변경할지 모르기 때문에

    안정성이 보장되지 않는다.)

 - 실제 동작 소스 코드는 상수 operator[] 함수 내에 있는다. (위랑 같은 이유)

   (상수, 비상수 두개 모두 사용하는 것이므로 소스 코드 변경 사항이 없어야하므로!!)

 - 캐스팅은 일반적으로 좋지 않은 생각이지만.. 많은 양의 코드 중복도 보통 문제가 아님.

 

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
class CMyString
{
public:
    CMyString(char* pString)
    {
        m_nLength = strlen(pString);
        m_pString = new char[m_nLength];
        
        strcpy(m_pString, pString);
    }
 
private:
    char* m_pString;
    mutable int m_nLength; //mutable 키워드로 멤버 변수 선언.
 
public:
    //상수 version operator[]
    const char& operator[](int nIndex) const
    {
        //엄청난 크기의 코드가 들어있다고 가정.
 
        return m_pString[nIndex];
    }
 
    //비상수 version operator[]
    char& operator[](int nIndex)
    {
        return 
            const_cast<char&>(
                    static_cast<const CMyString&>
                        (*this)[nIndex]
            );
    }
};
 
int _tmain(int argc, _TCHAR* argv[])
{
    const CMyString cstrVal("Hello");
    CMyString strVal("Nice");
 
    cstrVal[0= 'R';  //Error C3892
    strVal[0= 'F';   //가능
 
    return 0;
}
cs

 

 1) 상수 operator[] 함수에 코드를 작성한다.

    - 상수 객체는 상수 operator[] 함수를 그대로 사용하게 되므로 문제 없다.

 

 2-1) 비상수 operator[] 함수에서는 캐스팅을 통해 상수 operator[]를 호출한다.

 2-2) 상수 operator[]로부터 받은 상수형 인자를 비상수 형으로 변환하여 최종 반환한다.

   - 애초에 상수 버젼 함수에 소스 코드가 들어가져 있으므로 안정성 보장.

   - 그러나 만약 반대의 경우라면 코드 소스가 비상수 함수에 들어가게 되고 이는 코드의 변경이 가능해지기 때문에

     안정성이 보장되지 않는다. (애초에 비트수준 상수성에 위배되기도 함.)

   - 그리고 상수 함수를 비상수 함수로 바꾸게 되면 모든 재앙의 씨앗이 되므로 절대로 해선 안된다. (중요)

 

 2-3) 소스 검토

   - static_cast<const CMyString&>(*this)[nIndex]   (비상수 객체에서 상수 객체로 변환)

    · 만약 그냥 operator[]를 사용했으면 재귀함수에 빠지게 되므로 casting을 통해 상수 operator[]를 호출한다.

    · const를 붙이는 캐스팅은 안전한 타입 변환이므로 static_cast만 써도 좋다.

    · 변환 형을 static_cast<const CMyString> 으로만 한다면 상수형 operator[] 함수를 호출하는 객체는 strVal 객체가 아닌

      임시 객체가 되버리므로 상수형 operator[] 함수에서 동작되는 부분들은 실제 객체와 아무 연관이 없어져버리게 된다.
      그러므로 반드시 형변환시에는 const CMyString& 참조형으로 변환해야만 한다.

 

3줄 요약

 

 - const를 붙여 선언하면 컴파일러가 사용상의 에러를 잡아내는데 도움을 준다.

 

 - 컴파일러는 비트수준 상수성만 지키기 때문에, 프로그래머는 논리적 상수성을 사용해서 코드를 작성해야 한다.

 

 - 상수 멤버 및 비상수 멤버 함수가 동일한 코드라면 코드 중복을 피하기 위해, 비상수 버젼이 상수 버젼을 호출하도록

   만드는게 좋다.

* 상수 사용 시에는 #define 보다 const 또는 enum을 우선 생각해라.

 

#difine ASPECT_RATIO 1.653 을 선언한 경우 우리에겐 ASPECT_RATIO라는 기호식 이름으로 보이지만

컴파일러에겐 보이지 않는다.

컴파일러에게 넘어가기 전에 선행 처리자가 이름을 밀어버리고 1.653 숫자 상수로 바꾸어 버리기 때문이다.

 

문제점

 - 숫자 상수로 대체된 코드에서 컴파일 에러가 발생하게 되면 에러 메시지에는 ASPECT_RATIO가 아닌 1.635이 적혀있기 때문에

   에러 부분을 찾기가 어려워 진다.

 

해결법

 - 매크로 대신 상수를 선언하라.

 - const double AspectRatio = 1.653;

 

이점

 - 언어 차원에서 지원하는 상수 타입의 데이터이기 때문에 컴파이러 눈에도 보이고 기호 테이블에도 들어가진다.

 - 최종 코드의 크기가 작아질 수 있다.

   (#define을 사용했을 경우 코드에 ASPECT_RATIO가 나올때마다 1.653으로 바뀌면서 코드 안에 1.653 사본이 등장 횟수만큼 들어가지만,

    상수 타입의 AspectRatio는 사본이 딱 한 개만 생기기 때문이다.)

 

상수 사용 시 주의점.

 1. 상수 포인터 (constant pointer)

  - 포인터는 꼭 const로 선언해주어야 하고, 포인터가 가리키는 대상까지 const로 선언해라.

  - const char* const strName - "Scott" 보다는 const std::string strName("Scott")이 더 좋다.

 

 2. 클래스 멤버로 상수를 정의 하는 경우.

  - 정수류(각종 정수 type, char, bool)로 선언된 정적(static) 클래스 멤버 상수 변수는 클래스 내부에서 초기화 가능.

  - 실수류 float, double의 static 멤버의 경우는 정의 시점에 초기화 가능.

  - const 멤버 변수는 이니셜라이저에서만 초기화 가능.

 

  - static 정수류 멤버 변수는 멤버 배열 변수의 인자로 사용 가능.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//Player.h File의 선언 부
class Player{
public:
    Player();
    virtual ~Player();
private:
    static const bool m_bool = true;
    static const int m_int = 5;
    static const short m_short = 11;
    static const double m_double;
 
    const int m_int2;
public:
    int dScores[m_int];
};
 
//Player.cpp File의 정의 부
const double Player::m_double = 1.35;
 
Player::Player() : m_int2(10)
{
 
}
cs

 

* 매크로 함수를 만드려면 Inline 함수를 우선 생각해라.

 

문제점

 - 괄호로 떡칠해야 된다.

 - 하기와 같은 소스 코드일 경우 전혀 엉뚱한 결과가 나온다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#define CALL_WITH_MAX(a,b) (a)>(b)? (a) : (b)
 
int _tmain(int argc, _TCHAR* argv[])
{
    int a = 10, b =5;
 
    CALL_WITH_MAX(++a, b);
 
    printf("a = %d, b = %d\n", a, b);
    
    a = 10;
    b = 5;
    int bbb = CALL_WITH_MAX(++a, b+10);
 
    printf("a = %d, b = %d\n", a, b);
}
cs

 

 - 첫 번째 CALL_WITH_MAX 호출 시 a가 2번 증가하게 된다. (비교되는 부문에서 1번 증가 반환하는 부문에서 1번 증가)

 - 두 번째 CALL_WITH_MAX 호출 시 a가 1번 증가하게 된다. (비교되는 부문에서 1번 증가)

 

해결법

 

 - 매크로 함수 대신 Inlnie 함수를 사용하라.

 

 - Inline 함수는 일반 함수보다 처리 속도가 빠른 이점이 매크로 함수와 동일하다.

 

 - 매크로 함수처럼 괄호로 분칠할 필요없고, 괴현상이 발생하지 않는다.

라이브러리 (Library)

 

함수, 데이터, 타입 등 여러가지 프로그래밍 요소들의 집합이며 보통 lib 확장자를 가진다.

자주 사용되는 함수를 매번 직접 작성해서 사용하는 것은 비생산적인 작업이므로 표준화할 수 있는 함수를 미리 만들어서 모아 놓은 것이 라이브러리이다.

 

이 라이브러리를 프로그램에 링크시키는 방식에 정적 링크와 동적 링크가 있는데 동적 링크 방식을 사용하면 DLL이 된다.

 

 정적 링크 (Static Link)

 

  컴파일 시 라이브러리의 코드를 실행 파일에 복사한다. 때문에 실행 파일의 크기는 커지지만 실행 파일은 단독 실행 파일이 된다.

  컴파일이 끝나면 라이브러리 파일(.lib)이 없어도 프로그램을 실행시킬 수 있다.

 

 동적 링크 (Dynamic Link)

 

  실행 시에 라이브러리가 실행 파일에 연결된다.

  실행 파일에는 호출할 함수의 정보만 포함되고 실제 함수 코드는 복사되지 않으므로 실행 파일의 크기가 작아진다.

  실행 파일에는 실제 라이브러리 코드가 없기 때문에 프로그램 실행 시 DLL이 반드시 있어야 한다.

 

 동적 링크 장점

 

  여러 프로세스가 메모리에 있는 하나의 DLL 복사본을 공유하여 하나의 DLL을 동시에 사용할 수 있다.

  반면, 정적 연결 라이브러리를 사용하여 빌드된 응용 프로그램의 경우 Windows는 각 응용 프로그램에 대해 하나의 라이브러리 코드

  복사본을 메모리에 로드해야 한다.

 

  디스크 공간을 절약할 수 있다. 여러 응용 프로그램이 디스크에 있는 하나의 DLL 복사본을 공유할 수 있다.

  반면, 정적 연결 라이브러리를 사용하여 빌드된 응용 프로그램의 경우에는 각 응용 프로그램마다 별도의 복사본으로서 실행 가능한

  이미지에 링크되는 라이브러리 코드가 있다.

 

  DLL을 보다 쉽게 업데이트 할 수 있다. DLL의 함수가 변경되어도 이 함수의 인수 및 반환 값이 변경되지 않았으면 그 함수를 사용하는

  응용 프로그램을 다시 컴파일하거나 링크할 필요가 없다.

 

  반면, 정적으로 링크되는 개체 코드의 경우에는 함수가 변경되면 응용 프로그램을 다시 링크시켜야 한다.

 

DLL 관리

 

DLL은 가상 메모리에 한 번 로드되면 다시 로드 되지 않는다.

DLL이 메모리에서 삭제되어야 할 시기는 DLL을 사용하는 모든 응용 프로그램이 종료되었을 때이다.

윈도우즈는 DLL별로 사용 카운트(참조 카운트)라는 것을 이용하여 해당 DLL을 사용하는 응용 프로그램이 실행될 때마다 카운트를

1 증가시키고 응용 프로그램이 종료될 때마다 카운트를 1 감소시킨다.

그러다 카운트가 0이 되었을 때 메모리가 삭제된다.

 

DLL Export, Import

 

 Export

 

  함수를 제공하는 DLL에서는 자신이 제공하고자 하는 함수에 대한 정보를 밖으로 공개해야 하며 이 동작을 Export라 한다.

  __declspec은 함수에 대한 정보를 제공하는 선언문이며 Export할 함수 원형 앞에 집어넣으면 된다.

 

1
2
3
4
extern "C" __declspec(dllexportint Add(int a, int b)
{
    return a+b;
}
cs

 

 Import

 

  반대로 DLL을 사용하는 응용 프로그램에서는 어떤 DLL에 있는 어떤 함수를 사용하겠다고 선언해야 하는데 이 동작을 Import라고 한다.

  응용 프로그램에서 사용할 DLL 함수를 아래와 같이 Import하여 사용할 수 있다.

 

1
extern "C" __declspec(dllimportint Add(intint);
cs

 

Import Library

 

DLL을 사용할 프로그램은 자신이 Import할 함수가 어느 DLL에 있는지 알아내기 위해 Import Library를 사용한다.

Import Library는 DLL에서 Export 되는 함수에 대한 정보는 물론 이 함수의 코드가 어떤 DLL에 정의되어 있는지에 대한 정보를 가지고 있다.

단, 함수에 대한 정보만 가지고 있을 뿐 함수의 코드는 가지고 있지 않다.

그래서 응용 프로그램은 링크 시에 Import Library(.lib)를 링크해야 하며 Import Library가 지정하는 DLl 파일을 실행시에 읽어와야 한다.

 

Import Library는 함수에 대한 위치 정보만 가지고 있으므로 컴파일 할때만 필요하며 프로그램 실행과는 아무 상관이 없다.

그래서 DLL 배포시에는 .dll File만 배포하면 된다.

 

MyProject 라는 DLL 생성 프로젝트를 컴파일하면 MyProject.dll (DLL)과 MyProject.lib (Import Library) 두 개의 파일이 생긴다.

 

참고 자료

http://stdesignstar.tistory.com/tag/dll

 

 

'Programming > Knowledge' 카테고리의 다른 글

컴퓨터에 수가 저장되는 방법.  (0) 2017.04.20

<Client>

 

5. Server 연결. (Server에 전화 걸기.)

 · SOCKADDR_IN 구조체 선언. (주소 패밀리와 IP, 포트 번호를 쉽게 참조할 수 있도록 만들어진 구조체)

 · connect 함수 호출. (Server에 접속 요청)

 

  - SOCKADDR_IN 구조체

   sin_family : 프로토콜 체계를 정의한다. (인터넷 기반 [IPv4]의 경우는 AF_INET)

   sin_addr : Server의 IP 주소 정보를 넣어준다.

   sin_port : Server의 Port 정보를 넣어준다.

 

  - connect 함수

   Parameter 1 : 미리 생성한 socket을 넣어준다.

   Parameter 2, 3 : 미리 생성한 SOCKADDR_IN 구조체 정보를 넣어준다.

 

   Return : 정상적으로 연결되지 않을 경우 SOCKET_ERROR 값을 반환한다. (winsock2.h에 상수값으로 정의되어 있음.)

 

1
2
3
4
5
6
7
8
9
10
11
12
13
    SOCKADDR_IN ServerInfo;
 
    ServerInfo.sin_family = AF_INET;
    ServerInfo.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
    ServerInfo.sin_port = htons(2011);
    
    int Result = connect(m_Socket, (SOCKADDR*)&ServerInfo, sizeof(ServerInfo));
    if(Result == SOCKET_ERROR)
    {
        closesocket(m_Socket);
        WSACleanup();
        return false;
    }
cs

 

6-1. Data 송신 (Server에게 말하기)

 · send 함수 호출. (Block 함수이므로 해당 일이 끝날때까지 리턴되지 않고 대기한다.)

 

  - send 함수

   Parameter 1 : 미리 생성한 socket을 넣어준다.

   Parameter 2 : 보낼 Data를 넣어준다. (char형의 포인트이므로 구조체던지 int형이던지 적절한 캐스팅 연산자를 사용해 어떤 데이터든지 보낼 수 있다.)

   Parameter 3 : Parameter 2의 크기.

   Parameter 4 : 함수 호출 시에 여러가지 옵션을 설정할 수 있다. (특별한 옵션을 주지 않으면 0을 넣어준다.)

 

   Return : 정상 송신 시 데이터를 보낸 양만큼의 Byte 크기를 반환 / 에러 발생 시 SOCKET_ERROR를 반환.

 

6-2. Data 수신 (Server의 말 듣기)

 · recv 함수 호출. (Block 함수이므로 해당 일이 끝날때까지 리턴되지 않고 대기한다.)

 

  - recv 함수

   Parameter 1 : 미리 생성한 socket을 넣어준다.

   Parameter 2 : Data를 담을 Bufer를 넣어준다. 
                     (char형의 포인트이므로 구조체던지 int형이던지 적절한 캐스팅 연산자를 사용해 어떤 데이터든지 받을 수 있다.)

   Parameter 3 : Server로부터 받을 Data의 크기를 모르기 때문에 넉넉히 잡아주는 것이 좋다.

   Parameter 4 : 함수 호출 시에 여러가지 옵션을 설정할 수 있다. (특별한 옵션을 주지 않으면 0을 넣어준다.)

 

   Return : 정상 수신 시 받은 데이터 양만큼의 Byte 크기를 반환 / 에러 발생 시 SOCKET_ERROR를 반환.

 

* Block 함수 주의점 (send, recv, accept 함수)

  Server 측에서 어떤 Client에게 데이터를 전행할 때, Client 측에서 recv함수로 Server가 보낸 Data를 받지 않는다면 대기 상태로 들어가게 된다.

  send, recv 함수는 해당 동작이 끝날때까지 리턴되지 않고 대기하는 Block 함수이기 때문이다.

  Block 함수를 사용할 때는 Server & Client 프로그램 간의 실행구조를 잘 생각해야 한다.

 

 

 

'Programming > Socket' 카테고리의 다른 글

[API] 소켓 통신 2 <Server/Client 공통>  (0) 2017.05.22
[API] 소켓 통신 1 <Server/Client 흐름도>  (0) 2017.05.22

- API SOCKET 사용하기.

 

C++에서 소켓 통신을 하기 위해서 API에서 제공하는 SOCKET을 이용하면 된다.

 

<Server / Client 공통>

 

 1. Header File 추가 & Lib 연결.

  · winsock2.h Header File Include.

  · ws2_32.lib Library 링크.

   (프로젝트마다 링크를 시켜줘야 하는 불편함이 있기 때문에 하기와 같이 프로그램으로 선언해두면 자동으로 링크가 된다.)

 

1
2
#include <winsock2.h>
#pragma comment(lib, "ws2_32.lib")
cs

 

 2. Socket 변수 선언

  · SOCKET형 변수.

  · WSDATA형 변수. 

 

1
2
SOCKET m_Socket;
WSADATA m_wsaData;
cs

 

3. ws2_32.lib 초기화.

  · WSAStartup 함수 호출. (Window Socket을 사용하겠다고 System에 알려주는 함수)

  · WSAStartup 함수는 ws2_32.lib를 초기화 해주어 Window Socket을 사용할 수 있도록 해준다.

  · WSAStartup 함수를 호출해야 Winsock 관련 함수를 사용할 수 있다.

  · Winsock 종료 시에는 반드시 WSACleanup() 을 호출해야만 한다.

 

  - WSAStartup 함수.

  Parameter 1 : 사용할 소켓의 버전이 2.2라는 것을 알려준다.

  Parameter 2 : WSDATA 구조체 변수의 주소값을 넘겨주면 해당 변수에 초기화된 라이브러리 정보가 채워진다.

 

  Return : 정상적으로 초기화가 되면 0 값을 반환.

 

1
2
3
4
5
6
    if(WSAStartup(MAKEWORD(2,2), &m_wsaData) == INVALID_SOCKET)
    {
        printf("WSAStartup() failed : %d", WSAGetLastError());
        WSACleanup();
        return false;
    }
cs

 

4. Socket 생성. (전화기 장만)

  · Socket을 만들어주는 함수.

 

  - socket 함수.

  Parameter 1 : 주소 패밀리(Address Family)를 적어준다. (Internet Protocol 4 (IPv4)를 사용한다면 AF_INET)

  Parameter 2 : 소켓 타입을 적어준다. (SOCK_STREAM : TCP 방식 / SOCK_DGRAM : UDP 방식)

  Parameter 3 : Protocol (0 값을 넣어주면 그에 맞는 Protocol을 사용하도록 한다.)

 

  Return : 성공적으로 Socket이 만들어지면 SOCKET형 데이터를 반환. / 실패 시 INVALID_SOCKET 반환.

 

1
2
3
4
5
6
    m_Socket = socket(AF_INET, SOCK_STREAM, 0);
    if(m_Socket == INVALID_SOCKET)
    {
        printf("socket() failed : %d", WSAGetLastError());
        return false;
    }
cs

 

'Programming > Socket' 카테고리의 다른 글

[API] 소켓 통신 4 <Client>  (0) 2017.05.22
[API] 소켓 통신 1 <Server/Client 흐름도>  (0) 2017.05.22

- SOCKET 이란?

 

두 프로그램이 네트워크를 통해 서로 통신을 수행 할 수 있도록 양쪽에 생성되는 링크의 단자.

두 소켓이 연결되면 서로 다른 프로세스끼리 데이터를 전달 할 수 있다.

 

- Server / Client 흐름도.

 

Server/Client는 전화기에 비율할 수 있다.

 

Server : 전화를 받는 사람.

Client : 전화를 거는 사람.

 

Server는 먼저 실행중인 상태여야 하고 Client의 접속을 허락하고 관리할 수 있어야 한다.

즉, Server는 Client에게 전화가 올 때까지 기다리고 있다가 Client가 전화를 걸면 전화를 받는다.

 

<Server>

 

 1. 전화기가 장만한다. (socket 함수)

 2. 전화 번호를 할당 받아야 한다. (bind 함수)

 3. 전화가 걸려올 수 있도록 전화기를 잭에 연결한다. (listen 함수)

 4. Client로부터 전화가 걸려오면 받는다. (accept 함수)

 5. Client와 대화를 주고 받는다. (send, recv 함수)

 6. 대화가 끝나면 전화를 끊는다. (closesocket 함수)

 

<Client>

 

 1. 전화기가 장만한다. (socket 함수)

 2. Server에 전화를 건다. (connect 함수)

 3. Server와 대화를 주고 받는다. (send, recv 함수)

 4. 대화가 끝나면 전화를 끊는다. (closesocket 함수)

 

참고 내용.

 http://cafe.naver.com/nevernding

'Programming > Socket' 카테고리의 다른 글

[API] 소켓 통신 4 <Client>  (0) 2017.05.22
[API] 소켓 통신 2 <Server/Client 공통>  (0) 2017.05.22

컴퓨터가 메모리에 데이터를 저장하는 방법.

 

메모리에 Data가 적재되는 방법으로는 Little Endian 방식과 Big Endian 방식이 있다.

 

Big Endian 방식.

 · 사람과 동일하게 앞에서부터 순차적으로 데이터를 저장하는 방식.

 · 모토롤라 관련 CPU, IBM or 썬마이크로시스템즈의 컴퓨터들이 사용하는 방식.

 · Java의 자바 가상 머신은 운영체제나 하드웨어 상관없이 항상 Big Endian을 사용.

 

 

 ex) 0x12345678 값을 저장한다면 하기와 같은 방식으로 메모리에 저장된다.

 

주소

0x0001

0x0002

0x0003

 0x0004

Data

0x12

0x34

0x56

0x78

 

 

Little Endian 방식.

 

 · Big Endian 방식과 반대로 메모리에 역순으로 데이터를 저장하는 방식

 · Intel 사의 칫셋과 호환이 되는 컴퓨터들이 사용하는 방식.

 

 ex) 0x12345678 값을 저장한다면 하기와 같은 방식으로 메모리에 저장된다.

 

주소

0x0001

0x0002

0x0003

 0x0004

Data

0x78

0x56

0x34

0x12

 

현재 사용중인 컴퓨터에서 어떤 방식으로 저장되는지 확인해보기.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include "stdafx.h"
 
using namespace std;
 
int _tmain(int argc, _TCHAR* argv[])
{
    int i = 0x12345678;
 
    int* p = &i;
    
    char* p1 = (char *)&i;
    char* p2 = (p1 + 1);
    char* p3 = (p2 + 1);
    char* p4 = (p3 + 1);
 
    printf("%p : %x\n", p, *p);
    printf("%p : %x\n", p1, *p1);
    printf("%p : %x\n", p2, *p2);
    printf("%p : %x\n", p3, *p3);
    printf("%p : %x\n", p4, *p4);
 
    return 0;
}
cs

 

<결과>

 

주소

0x002DF928

0X002DF929

0X002DF92A

 0x002DF922B

Data

0x78

0x56

0x34

0x12

 

현재 사용중인 lenovo T530 노트북은 Little Endian 방식임을 알 수 있다.

(결과 주소는 PC마다 다르게 나올 수 있음.)

 

 

컴퓨터가 음수를 처리하는 방법.

 

컴퓨터는 첫 번째 비트를 부호 표현을 위해 따로 배정한다.

이를 부호 비트(signed bit)라 부르며 부호 비트가 0이면 양수, 1이면 음수를 나타내게 된다.

 

논리 회로가 음수를 표현하는 방법은 대표적으로 3가지가 있다.

 

 1. 부호 절대값 방법.

  부호 비트를 제외한 수를 양수 값으로 읽고, 마이너스를 붙이는 방법.

  +3 = 00000011₂

  -3 = 10000011₂

  사람이 사용하기에는 편하지만, 이진수 계산이 안되기 때문에 컴퓨터에선 사용할 수 없다.

 

 2. 1의 보수 방법.

  양수 값의 비트를 반전시켜 음수를 표현하는 방법.

  +3 = 00000011₂

  -3 = 11111100₂

  값이 0인 경우에는 00000000₂ 과 11111111₂ 두 값이 모두 0을 나타낼 수 있기 때문에 컴퓨터에선 사용하지 않는다.

 

 3. 2의 보수 방법.

  1의 보수 방법에서 1을 더하는 방법.

  +3 = 00000011₂

  -3 = 11111101₂

  1의 보수 방법과 다르게 11111111₂이 -1 값을 의미하므로 0과 구분된다.

 

위와 같은 이유로 컴퓨터는 2의 보수 방법으로 음수를 표현한다.

'Programming > Knowledge' 카테고리의 다른 글

DLL (Dynamic Link Library) 1  (0) 2017.05.26

하기 내용은 이재규 교수님의 "C++로 배우는 자료구조와 알고리즘"을 보고 공부한 내용입니다.

문제 시 삭제하도록 하겠습니다.

 

알고리즘(Algorithm)의 정의

 - 주어진 문제를 해결하기 위한 방법

 - 어떻게 할 것인가 해결적 측면.

 

자료구조 (Data Structure)

 - 알고리즘의 객체.

 - 배열, 스택, 큐, 트리 ...

 

알고리즘 선택

 - 하나의 문제에 대해 여러 알고리즘 존재.
   · 예를 들어, 서울에서 부산을 가능 방법은 여러 가지가 있다. (버스, 기차, 자가용 등..)

 

 - 절대적인 최상의 알고리즘은 없다.
   · 주어진 환경에 따라 알고리즘의 효율성은 달라진다.
   · 예를 들어, 출퇴근 시간에는 자동차보다 지하철이 더 빠르게 갈 수 있다. 그러나 차가 없는 저녁 시간에는 자동차가 더 빠를 수 있다.

 

 - 속도와 자원의 상관관계
   · 속도가 빠른 알고리즘은 자원을 많이 차지한다.
   · 메모리가 별로 없는 임베디드 시스템에서는 자원을 적게 차지하는 알고리즘을 선택하는 것이 좋고,

     PC와 같은 충분한 메모리와 고성능 CPU를 가지고 있다면 자원을 많이 차지하더라도 속도에 효율성이 있는 알고리즘을 선택하는 것이 좋다.

 

 - 단순한 알고리즘이 좋다.

   · 지나치게 속도만을 생각하는 것은 좋지 않다.

   · 알고리즘 부분의 사용빈도에 따라 선택하는 것이 좋다.

 

 

알고리즘의 유형

 - 1 (상수)

  · 입력자료수와 관계없이 일정한 실행시간 (데이터가 100개이든 1000개이든 실행 시간은 동일)

  · 해쉬 검색 알고리즘 등

 

 - log N

  · Divide & Conquer 방법 사용 시 (문제를 분할하고 그 분할된 문제를 해결하는 방법)

  · 이진검색, 이진트리검색 등

 

 - N

  · Scan 방법 사용 시 (자료가 100개 들어오면 100초 걸린다면 1000개 일때는 1000초 걸린다.)

  · 선형검색 등 (데이터베이스에서 검색하는 것이 이런 유형)

 

 - N log N

  · Divide & Conquer & Merge 방법 사용 시 (문제를 분할하고 정복하고 다시 합치는데 사용하는 방법)

  ·  병합정렬 등

 

 - N²

  · 이중루프

  · 삽입정렬, 선택정렬 등

 

 - N³

  · 삼중루프

 

하기 그래프는 각 유형 별 결과 그래프이다.

자료 수가 적을 때는 차이가 나지 않지만 자료 수가 늘어남에 따라 실행 결과의 격차는 커진다.

그러므로 적절한 알고리즘을 찾는 것은 매우 중요하다.

 

                        <이재규 교수님 강의 자료 첨부>

+ Recent posts