2017. 3. 29. 19:21

안녕하세요. 게임 개발자 놀이터입니다.


이번 포스팅에서는 변수 선언시 저장되는 영역을 적어보려고합니다.


크게 4개 또는 3개의 영역이라 표현하는데요


순서대로 


텍스트, 데이터, 스택(Stack), 힙(Heap)


입니다. 또는 3가지로 나눌땐 텍스트와 데이터를 하나로 합쳐서 보기도 한다네요.


그럼 순서대로 적어보겠습니다.


텍스트 영역

프로그램의 실행 코드가 존재하는 영역입니다.

 즉, 내가 작성한 코드들이 이곳에 위치합니다.


텍스트는 비교적 간단하네요


데이터 영역

    • 전역 변수나 정적 변수(static)가 이곳에 기록됩니다.
    • 데이터 영역내에서도 3가지로 분류가 가능합니다
      • 읽기 전용 영역
        • Const나 상수 문자열 등이 이곳에 속합니다.
      • 읽기/쓰기 영역
        • 전역 변수중 초기화가 된 변수들입니다.
      • 초기화가 되지 않은 영역 (BSS (Block Started by Symbol))
        • 전역 변수이면서 초기화가 되지 않고 선언되거나 static으로 선언된 변수들입니다.


스택 영역(Stack)

    • 지역 자동 변수 (지역 auto변수) 입니다.
    • 메모리를 따로 설정하지않아도 함수 시작(스코프영역 진입)에 자동으로 생성되고 함수 끝(스코프영역 벗어남)에 자동으로 파괴됩니다.
    • 스코프 영역안에 잡힌 주소값을 가리키는 포인터 변수도 auto변수 이기 때문에 이곳에 위치합니다.
      • (단, 주소값을 가르킬뿐, 주소값의 영역은 다릅니다.)


힙 영역(Heap)

    • 일반적으로 malloc 처럼 동적으로 메모리를 할당받을때 이곳의 영역이 사용됩니다.
    • 이곳은 자동 파괴가 되지 않습니다.



이렇게 정리하고 보면 되게 간단한 4가지인데, 항상 필요할때 기억이 나질 않네요.


읽어주셔서 감사합니다.


Posted by 시리시안
2016. 12. 5. 13:15

안녕하세요. 게임개발자 놀이터 입니다.


이번 포스팅에선 C++의 예약어중 하나인 inline에 대해 포스팅 하려고합니다.


C++ 책을 보다보면 꼭 나오는 예약어중 하나인데요.


쉽게 말하자면, 인라인 함수는 컴파일된 함수의 코드가 프로그램의 코드안에 직접 삽입이 되는겁니다.


즉, 컴파일러가 함수를 호출하는 과정이 사라져 버리는것이죠.


코드를 볼까요?


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include<iostream>
 
using namespace std;
 
inline void Inlinefuction()
{
    cout<<"*** Inlinefuction() ***"<<endl;
}
 
 
int main()
{
 
    Inlinefuction();

    return 0;
}
cs


이렇게 선언한 코드를 컴파일 하게되면


1
2
3
4
5
6
7
8
9
10
11
#include<iostream>
 
using namespace std;
 
int main()
{
 
    cout<<"*** Inlinefuction() ***"<<endl;
 
    return 0;
}
cs


위와 같이 바뀌게 됩니다. 어떤가요?


좀더 확실한 결과를 보기 위해 어셈블리 코드로 확인 해보겠습니다.


Visual C++에서 어셈블리 파일을 생성할떄 /Ob1 이라는 추가 명렁 옵션을 주셔야 어셈블리 코드가 정상적으로 출력됩니다.

(진짜 몰라서 엄청 고생했네요...)


먼저 인라인이 적용 되지 않은 어셈블 코드중 메인 함수를 보면


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
; 12   : {
 
    push    ebp
    mov    ebp, esp
 
; 13   :     Inlinefuction();
 
    call    ?Inlinefuction@@YAXXZ            ; Inlinefuction
 
; 14   :     return 0;
 
    xor    eax, eax
 
; 15   : }
 
    cmp    ebp, esp
    call    __RTC_CheckEsp
    pop    ebp
    ret    0
cs


위와 같이 나오게 됩니다. 그후 Inline이 적용된 함수의 어셈블 코드를 보면


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
; 12   : {
 
    push    ebp
    mov    ebp, esp
    push    esi
 
; 13   :     Inlinefuction();
 
    mov    esi, esp
    mov    eax, DWORD PTR __imp_?endl@std@@YAAAV?$basic_ostream@DU?$char_traits@D@std@@@1@AAV21@@Z
    push    eax
    push    OFFSET ??_C@_0BI@FMEKNHNP@?$CK?$CK?$CK?5Inlinefuction?$CI?$CJ?5?$CK?$CK?$CK?$AA@
    mov    ecx, DWORD PTR __imp_?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A
    push    ecx
    call    ??$?6U?$char_traits@D@std@@@std@@YAAAV?$basic_ostream@DU?$char_traits@D@std@@@0@AAV10@PBD@Z ; std::operator<<<std::char_traits<char> >
    add    esp, 8
    mov    ecx, eax
    call    DWORD PTR __imp_??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@P6AAAV01@AAV01@@Z@Z
    cmp    esi, esp
    call    __RTC_CheckEsp
 
; 14   :     return 0;
 
    xor    eax, eax
 
; 15   : }
 
    pop    esi
    cmp    ebp, esp
    call    __RTC_CheckEsp
    pop    ebp
    ret    0
cs


이렇게 나오게 됩니다.


13번째 줄을 보면 확연히 차이가 나는걸 보실수 있습니다.


음.. 보기 어렵군요?


함수를 좀더 보기 쉽게 바꿔보겠습니다.


소스코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include<iostream>
 
using namespace std;
 
int Inlinefuction(int a, int b)
{
    return a+b;
}
 
 
int main()
{
    int n;
    n = Inlinefuction(1,2);
    return 0;
}
cs


Inline이 적용하지 않은 어셈블리 코드

1
2
3
4
5
6
7
8
9
10
11
12
; 13   :     int n;
; 14   :     n = Inlinefuction(1,2);
 
    push    2
    push    1
    call    ?Inlinefuction@@YAHHH@Z            ; Inlinefuction
    add    esp, 8
    mov    DWORD PTR _n$[ebp], eax
 
; 15   :     return 0;
 
    xor    eax, eax
cs


Inline이 적용된 어셈블리 코드

1
2
3
4
5
6
7
8
9
10
; 13   :     int n;
; 14   :     n = Inlinefuction(1,2);
 
    mov    eax, 1
    add    eax, 2
    mov    DWORD PTR _n$[ebp], eax
 
; 15   :     return 0;
 
    xor    eax, eax
cs



14 번의 코드를 보면 확 차이가 느껴지실껍니다.

가장 크게 어셈블리 코드상에서 call이 불리지 않게됩니다.

또 지금 코드상에선 push도 호출되고 있지 않네요.



일반 함수는 호출되는 과정을 보면, 우선 매개변수를 스택이 집어넣고, 함수를 호출합니다. 그후 함수내 리턴값을 위한 스택의 push, pop 도있고, 리턴후 스택 정리를 위한 pop이 있습니다.

이러한 함수 호출은 스택에 접급 하기 때문에 즉, 메모리에 접근 하기 때문에 속도가 저하 될 수 있습니다.

함수 호출에 대한 자세한 이야기는 다른 포스팅에서 다루겠습니다.



인라인을 쓰게되면 가장 먼저 속도와 메모리 면에서 효과가있습니다.

메모리는 위에 적은 것처럼 함수를 부르기 위해 스택에 넣었다 빼는 과정이 없어지기 때문이고, 그에따라 속도 면에서 더 빨라진답니다.


하지만 단점은 너무 반복해서 사용하게 될경우 어셈블리 코드가 길어진다는 것입니다. push와 call로 끝나는 함수가 여러번 호출되면서 여러번의 내용이 다 복사되서 어셈블리 코드로 적히게 되면 그 길이는 장난 아니겠죠?



결론


음.. 제 짧은 지식으로 내린 결론은 

함수 구문이 짧고, 호출이 적은 함수는 inline화 시키자는 겁니다. 크게 영향을 주지않는 함수 호출을 줄이자는거죠..!!


잘 모르는 분야라 댓글로 지적 해주시면 감사하겠습니다.






Posted by 시리시안
2016. 2. 25. 16:49

c언어에서의 파일입출력은

1. 파일을 연다

2. 파일에 입출력 작업을 한다.

3. 파일을 닫는다.

로 요약됩니다.


입출력 할 때 파일포인터가 필요하기 때문에

FILE *f; 와 같은식으로 파일 포인터를 선언해준다.


1. 파일 열기

파일을 열기 위해서는 fopen() 함수를 사용하게 됩니다.

따로 경로가 없다면 현재 소스파일이있는 그 경로를 나타냅니다.

파일명은 확장자 까지 포함한 풀 네임이며, 모드가 있는데,

w : 쓰기 모드, 만약 파일이 존재 하지 않는다면 파일을 새로 만든다.

r : 읽기 모드, 만약 파일이 존재 하지 않는다면 NULL을 리턴한다.

a : 추가 모드. 파일이 존재 하지 않는다면 파일을 새로 만든다.

w+ : 읽기/쓰기

r+ : 읽기/쓰기

a+ : 읽기/추가

b : binary


2. 입출력 작업

입출력을 하기 위해서는 파일입출력 함수들이 이용됩니다.

문자 : fgetc(), fputc();

문자열 : fgets(), fputs();

fscanf(), fprintf(); 


간단한 입출력 예제

1
2
3
4
5
6
7
8
#include <stdio.h>
int main()
{
    FILE *f;
    f=fopen("a.txt","w");
    fprintf(f,"Hello World");
    fclose(f);
}
cs

 

이렇게 입력후 실행하면!?

도스창엔 아무것도 안뜨게 됩니다.

왜냐하면 파일에 작업을 하고있기 때문이죠


소스가 있는 a.txt파일이 보일 것입니다.

텍스트 파일을 열어보면 이와같이 Hello World라는 문구가 출력되어 있습니다.



3. 파일 닫기

위 소스들을 잘 살펴보면 항상 

fclose(f); 라는 함수가 소스의 끝부분에 쓰여 있습니다.

바로 이 함수가 파일을 닫겠다는 의미입니다

Posted by 시리시안
2016. 2. 25. 13:10

기본적으로 많이 사용하는 형식의 데이터형만 기본 데이터로써 정의되고있고, 나머지는 프로그래머가 필요에따라 정의해서 사용할 수 있도록 만들수있습니다.


그래서 기본 데이터 형인 char, int, float, double등을 제공하고 나머지는 프로그래머가 배열, 포인터,구조체, 공용체, 혹은 typedef 같은 문법을 사용해 직접 정의 해서 사용합니다.


배열과 같은 무법으로 사용자 정의 데이터형을 만들게 되면 동일한 데이터 형만을 그룹지을 수 있습니다. 단순한 형태만 정의 가능하다는 단점이 있지만, 이를 보완하기 위해 만들어 진것이 구조체 입니다.


구조체는 서로 다른 데이터형을 하나의 데이터로 군집화 하여 사용 할 수 있도록 하는 사용자 정의 데이터 형입니다.


1. 구조체 정의하기

 

구조체를 정의 할 때에는 다음과 같이 사용합니다.


struct 구조체명 {

데이터형 변수명;

...

};

이렇게 선언된 구조체명이 새로 쓸수있는 데이터형의 이름입니다. 즉, int char처럼 사용가능합니다.


구조체는 struct 키워드를 앞에 명시한 다음에 사용할 구조체명을 적어서 정의합니다. 이 때 구조체

     내부를 구성하는 데이터는 미리 정의되어 있어야 하며, 각 데이터는 세미콜론(" ; ") 키워드로 

분리해야합니다. 

예를 들어 이름, 나이, 키, 몸무게와 같은 인적사항을 저장하는 People 이라는 구조체를 정의하면 다음과 같습니다.


1
2
3
4
5
6
7
 
    struct People {
        char name[20];
        int age;
        double height;
        double weight;
    };
cs

    위에서도 말했듯이 구조체 내부의 데이터는 미리 정의된 데이터형이여야하기 때문에 char, int, double

    등과 같은 기본 데이터형 외에 다른 구조체나 사용자가 따로 정의한 데이터형을 사용하고 싶으면

    이 구조체보다 앞서 정의되어 있어야합니다.

 

    일반적이지는 않지만 한번만 선언해서 사용하는 경우 아래와 같이 구조체명을 생략해서 사용하는 경우도 있습니다.

즉, 구조체를 정의함과 동시에 data 라는 변수를 선언하여 사용하기 때문에 이 데이터형을 다시 사용할 필요가 없다면 구조체명도 필요없기 때문에 생략 가능합니다.

 

 

1
2
3
4
5
6
    struct {
        char name[20];
        int age;
        double height;
        double weight;
    } data;
cs

 

 

2. 구조체 사용하기

 

    정의된 구조체는 구조체명을 이용하여 변수를 선언하듯이 선언해주면 되는데, C 언어에서는 구조체를

    이용하여 변수를 선언할 때에는 반드시 구조체명 앞에 "struct" 키워드를 붙여주어야합니다.

 

    // People 구조체를 사용하여 data 변수를 선언한다.

    struct People data;

 

    C++ 언어로 구조체를 선언할 때에는 struct 키워드를 생략하여 선언할 수 있지만 C 언어의 경우에는

    구조체를 선언할 때마다 struct 키워드를 명시해야 하기때문에 (굉장히 불편합니다.)

해결법으로는 아래와 같이 typedef 명령어를 이용하여 구조체를 또다른 데이터 타입으로 정의하여 사용하기도 합니다.

 

    typedef struct People PEOPLE;

 

    이렇게 선언하면 C 언어에서도  People 구조체를 사용하여 변수를 선언할때 struct 키워드를

    적지 않게되어 좀더 편리하게 사용할수 있습니다.

 

    PEOPLE data;  // struct People data;

 

    하지만, 구조체 정의 따로 typedef 정의 따로 사용하는 방식이 프로그램을 이해하기에 불편할수도

    있기 때문에 이것을 아래와 같이 한번에 사용하여 구조체를 정의하기도 합니다.

 

1
2
3
4
5
6
7
8
 
 // struct people { ... } 구조체를 PEOPLE 이라는 데이터형으로 정의한다.
    typedef struct People{
        char name[20];
        int age;
        double height;
        double weight;
    } PEOPLE;
cs

 

최근에는 C++ 를 사용하는 사람들이 많아짐에따라 struct 가 생략가능해지면서 typedef 을 사용하는 형식도 점차 줄어들고 있습니다.    (Class가 있으니..)

   

구조체로 선언한 데이터형도 기본데이터형(char, int, ...)과 동일한 데이터 형이기 때문에 변수 선언 배열, 포인터 문법을 아래와 같이 그대로 사용할수 있습니다.

 

PEOPLE data;      // 일반 변수

PEOPLE list[20];  // 배열

PEOPLE *p;         // 포인터

 

이렇게 선언된 구조체 변수는 아래의 코드처럼 구조체의 요소에 접근할 때에는 " . " 를 이용하여 구조체의 "변수명.요소명" 과 같은 형식을 사용합니다.

     

1
2
3
4
5
6
PEOPLE one;
 
    strcpy(one.name, "운영진");
    one.age = 23;
    one.height = 179.9;
    one.weight = 70;
cs

 

3. 구조체의 크기와 메모리 배열 ( struct member alignment )

 

구조체의 크기는 일반적으로 구조체를 구성하는 데이터형의 크기를 합산한 값입니다.


    Windows 32비트 운영체제는 레지스터의 크기, 데이터 버스의 크기, 데이터 처리 단위, 포인터의 크기

    등이 모두 4 바이트로 되어 있어서 해당 크기로 연산하고, 처리하는 것이 속도가 더 빠릅니다. 그래서 

    컴파일 옵션에 따라서 구조체를 해석할때 그 배열을 짝수 또는 4의 배수로 재구성하는 작업

    수행하기 때문에 실제로 구조체가 구성되는 것이 조금 다를 수 있습니다.

 

1
2
3
4
5
6
7
typedef struct Test {
        char a;       // 1 바이트의 데이터 크기를 가짐
        int b;          // 4 바이트의 데이터 크기를 가짐
        short int c;  // 2 바이트의 데이터 크기를 가짐
        int d;          // 4 바이트의 데이터 크기를 가짐
        char e;       // 1 바이트의 데이터 크기를 가짐
    } TEST;
cs

 

위와 같이 정의된 TEST 구조체를 선언하면 12 바이트의 메모리가 할당된다고 생각할 수도 있지만

구조체를 구성하고 저장하는 단위가 4 바이트이기 때문에 b 나 d 같은 4 바이트 이상의 크기를 가지는

변수가 4의 배수 주소 값에 위치하도록 아래와 같은 형태로 메모리가 사용되어 20 바이트의 메모리가 할당됩니다.

 

그렇기때문에 쓸데없는 메모리의 낭비를 막기 위해서는 아래와 같이 데이터를 구성하는 순서를변경하여 구조체를 정의하는 것이 좋습니다.

 

   

1
2
3
4
5
6
7
typedef struct Test {
        int b;          // 4 바이트의 데이터 크기를 가짐
        int d;          // 4 바이트의 데이터 크기를 가짐
        short int c;  // 2 바이트의 데이터 크기를 가짐
        char a;       // 1 바이트의 데이터 크기를 가짐
        char e;       // 1 바이트의 데이터 크기를 가짐
    } TEST;
cs

 

   

 

이처럼 구조체를 정의할 때 어떻게 배치하는가에 따라 구조체의 크기가 변경될 수 있기때문에 메모리를 낭비하지 않도록 주의해야합니다,

프로그래밍을 하다가 구조체의 크기를 명시해야하는 경우에는 수치 값이 아닌 sizeof 매크로를 사용하여 구조체의 크기를 반환받아 사용해야합니다. 

Posted by 시리시안
2016. 2. 23. 08:41


포인터 *


포인터란 어떠한 값을 저장하는게 아닌 어떠한 값의 주소(Address)를 저장하는 것 입니다


어떠한 값의 주소는 컴퓨터 메모리의 주소를 의미합니다.

1
2
3
4
5
6
7
8
#include<stdio.h>
 
void main()
{
    int num = 0;
    int *pnum = &num;
    //pnum은 num의 주소값을 가지고 있다.
}
cs


포인터 변수를 선언하기 위해서는 변수이름 앞에 *을 붙여서 선언하면 그 변수는 포인터 변수가 됩니다.


대체로 포인터인걸 알리기위해 변수병 앞에 소문자로 p를 붙이기도 합니다. 하지만 이건 코드 스타일이니.. 각자 알아서..


아직 이해가 잘 안되실텐데, 포인터 변수를 이용하여, 주소값과 포인터의 관계를 나타낸 예제입니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include<stdio.h>
 
int main(void)
{
    int *pnum;         // 포인터 변수 선언
    int num = 10;     // 일반변수 선언 & 초기화
    pnum = &num;     // num의 주소값을 pnum에 저장
 
    printf("num의 값: %d\n", num);
    printf("num변수의 주소:%p\n", &num);
    printf("pnum변수의 값 :%p\n", pnum);
    printf("pnum이 가리키는 값: %d\n"*pnum);
 
/*
결과값
num의 값: 10
num변수의 주소:001919fC
pnum변수의 값 :001919fC
pnum이 가리키는 값: 10
*/
}

cs


밑에 주석처리한 결과값을 보면 pnum은 num의 주소값을 가리킨다는것을 알수있습니다.

즉 pnum이 가진 주소값의 위치로 찾아가면 num의 값을 얻을수있다는 것입니다.

주소값의 위치를 찾기위해선, 포인터 선언때 사용했던 * 연산자를 변수 명 앞에 적어주면 됩니다.


그럼 여기서 num의 값이 변화한다면 어떻게 될까요?

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
#include<stdio.h>
 
int main(void)
{
    int *pnum;         // 포인터 변수 선언
    int num = 10;     // 일반변수 선언 & 초기화
    pnum = &num;     // num의 주소값을 pnum에 저장
 
    printf("num의 값: %d\n", num);
    printf("num변수의 주소:%p\n", &num);
    printf("pnum변수의 값 :%p\n", pnum);
    printf("pnum이 가리키는 값: %d\n"*pnum);
    printf("num값 증가 +10 \n");
    num+=10;
    printf("pnum이 가리키는 값: %d\n"*pnum);
 
/*
결과값
num의 값: 10
num변수의 주소:001919fC
pnum변수의 값 :001919fC
pnum이 가리키는 값: 10
num값 증가 +10
pnum이 가리키는 값: 20
*/
}

cs


주석 처리된 출력값을 보면 마지막에 num의 값이 증가했는데 pnum이 가리키는 값 또한 증가한걸 볼수있습니다.

당연하겠죠?

이해가 안된다면, pnum이 가지고있는 값은 주소값이며, *pnum으로 가리키는 주소로 갔더니 값이 있어서 그걸 출력한것뿐입니다.


집주소를 알고있지만, 같은 주소에 다른사람이 살고있는것과 같겠죠?




◎ 포인터의 초기화


포인터의 초기값은 무엇으로 해야할까요? 

1
2
3
4
5
6
7
#include<stdio.h>
 
int main(void)
{
    int *pnum;         // 포인터 변수 선언
    *pnum = 100;
}

cs

위와 같이 코딩해 실행한다면 어떻게 될까요?

물론 컴파일러의 도움을 받아서 컴파일을 해주지 않겠지만, 이론대로라면 초기화 되지않은 포인터 변수는 쓰레기값.. 어딘지 모르는 주소값을 가지고있어서, 컴퓨터가 사용하는 어느 일정 부분의 메모리를 갑자기 100으로 덮어씌울수도있고, 그에따라 치명적인 오류를 뱉어 낼수도있습니다.


따라서 포인터 변수의 초기화는 NULL 또는 0 으로 하면 됩니다.

1
2
3
4
5
6
7
#include<stdio.h>
 
int main(void)
{
    int *pnum01 = null;         // 포인터 변수 선언
    int *pnum02 = 0;         // 포인터 변수 선언
}
cs

NULL은 아스키 코드값으로 0을 나타내며, 포인터 변수에 들어간 0은 0번째 주소를 가리키는게 아닌, "아무것도 없다." 라는 뜻을 지니게 됩니다.






Posted by 시리시안
2016. 2. 23. 07:58

저번 포스트에 앞내용을 그대로 가져오겠습니다.


C언어 연산자. 

C연어 연산자는 C언어의 아주 기초적인 내용입니다.

아주 기초적이기 때문에 소홀히 하기 쉬운 부분이기도 합니다.

C언어에서 연산자가 상당히 많은 중요한 역할을 하고 있다 할 수 있지만, 주목받지 못하는 이유는 이해 하기가 너무 쉽고, 소프트웨어 관련 프로그램에서는 문법보다 많은 비중을 차지하지 못하기 때문입니다.

그럼에도 연산자를 알아야 하는 이유는, 당연히 C언어 코딩 과정이 게임을 만드든, 뭘 만드든 연산자와 함수를 통해서 이루어지기 때문이죠.

특히, 비트연산자는 하드웨어와 관련된 신호를 처리할 때 도움이 되는 개념으로 꼭 알아야 합니다.

 

 

※ 연산자 종류


분류 

연산자 

 대입 연산자 

 

 산술 연산자 

 +, -, *, /, %

 복합 대입 연산자

 +=, -=, *=, /=, %=

 증감 연산자 

  ++, --

 관계 연산자 

  >, <, ==, !=, >=, <=

 논리 연산자 

  &&, ||, !

 조건 연산자

  ? :

 비트 논리 연산자 

  &, |, ^, ~

 비트 이동 연산자 

 >>, <<


대입 연산자 부터 조건 연산자 까지는 이전 포스팅에서 작성 되었습니다.


http://silisian.tistory.com/35


◎ 비트 논리 연산자


비트 논리 연산자는 총 &, |, ^, ~ 4개를 가지고있습니다.


비트 논리 연산자 

구분 

& 

 비트 곱 (AND)

 비트 합 (OR)

^ 

 베타적 비트 합 (XOR)

 비트 부정 (NOT)


◎ 비트 곱은 비트 라는 것을 제외하고 생각하면 곱셈연산과 같습니다. 단, 비트별로 곱합니다.

◎ 비트 합은 비트 라는 것을 제외하고 생각하면 덧셈연산과 같습니다. 단, 비트별로 덧셈하며 결과값은 참/거짓(1/0)으로만 표현합니다.

◎ 베타적 비트 합은 비트별로 연산하며 같으면 거짓을 다르면 참을 반환합니다. 비트별 != 연산이라 생각하면 간단합니다.

◎ 비트 부정은 Not연산이며, 비트 보수 연산자라고 불리기도 합니다. 자기자신의 비트별 !연산이라 생각하면 간단합니다.


◎ 비트 곱 (AND)

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
#include<stdio.h>
 
void main()
{
 
    /*
    비트 곱은 비트별로 연산 한다느넛만 빼면 논리 곱과 똑같습니다.
    A(1100) 과 B(1010)을 비트 곱 한다 하면
    차례대로 
    1&1 = 1            (1*1 = 1)
    1&0 = 0            (1*0 = 0)
    0&1 = 0            (0*1 = 0)
    0&0 = 0            (0*0 = 0)
    이 나오게 됩니다. 오른쪽 처럼 생각하면 간단하죠?
    */    
 
    //예제 소스
    
    char Data = 0x86;         //16진수인 0x86은 2진법으로 표현하면 1000 0110 입니다.
    char BitMask = 0xf2;    //16진수인 0xf2는 2진법으로 표현하면 1111 0010 입니다.
    char Result = Data & Bitmask;    //이 2개의 수를 비트곱 연산을 하게되면, 값은 1000 0010이 나오게됩니다.
    // 즉 0x82가 나오게 됩니다.
 
    /*
    연산
    1 & 1 = 1
    0 & 1 = 0
    0 & 1 = 0
    0 & 1 = 0
    0 & 0 = 0
    1 & 0 = 0
    1 & 1 = 1
    0 & 0 = 0
    */
}
cs



◎ 비트 합 (OR)

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
#include<stdio.h>
 
void main()
{
 
    /*
    비트 합은 비트별로 연산 한다는것만 빼면 논리 합과 똑같습니다.
    A(1100) 과 B(1010)을 비트 합 한다 하면
    차례대로 
    1|1 = 1            (1||1 = 1)
    1|0 = 1            (1||0 = 1)
    0|1 = 1            (0||1 = 1)
    0|0 = 0            (0||0 = 0)
    둘중 하나라고 비트값이 1 이면 1을 반환합니다.
    논리연산자중 ||을 생각해서 둘중 하나라고 참이면 참을 반환한다. 라고 생각하면 간단합니다.
    */    
 
    //예제 소스
    
    char Data = 0x86;         //16진수인 0x86은 2진법으로 표현하면 1000 0110 입니다.
    char BitMask = 0xf2;    //16진수인 0xf2는 2진법으로 표현하면 1111 0010 입니다.
    char Result = Data | Bitmask;    //이 2개의 수를 비트곱 연산을 하게되면, 값은 1111 0110이 나오게됩니다.
    // 즉 0xf6가 나오게 됩니다.
 
    /*
    연산
    1 | 1 = 1
    0 | 1 = 1
    0 | 1 = 1
    0 | 1 = 1
    0 | 0 = 0
    1 | 0 = 1
    1 | 1 = 1
    0 | 0 = 0
    */
}
cs


◎ 베타적 비트 합 (XOR)

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
#include<stdio.h>
 
void main()
{
 
    /*
    베타적 비트 합은 비트별로 연산 한다는것만 빼면 논리연산자중 != 과 똑같습니다.
    A(1100) 과 B(1010)을 베타적 비트 합 한다 하면
    차례대로 
    1^1 = 1            (1!=1 = 0)
    1^0 = 1            (1!=0 = 1)
    0^1 = 1            (0!=1 = 1)
    0^0 = 0            (0!=0 = 0)
    두개의 값이 서로 다르다면 1 같으면 0을 반환합니다.
    논리연산자중 !=을 생각해서 둘이 다르다면 참을 반환 한다고 생각하면 간단합니다.
    */    
 
    //예제 소스
    
    char Data = 0x86;         //16진수인 0x86은 2진법으로 표현하면 1000 0110 입니다.
    char BitMask = 0xf2;    //16진수인 0xf2는 2진법으로 표현하면 1111 0010 입니다.
    char Result = Data ^ Bitmask;    //이 2개의 수를 비트곱 연산을 하게되면, 값은 0111 0100이 나오게됩니다.
    // 즉 0x74가 나오게 됩니다.
 
    /*
    연산
    1 ^ 1 = 0
    0 ^ 1 = 1
    0 ^ 1 = 1
    0 ^ 1 = 1
    0 ^ 0 = 0
    1 ^ 0 = 1
    1 ^ 1 = 0
    0 ^ 0 = 0
    */
 
    //여기서 특이한점은, 베타적 비트합을 2번 연산 하게되면 원래 값으로 돌아가게 됩니다.
 
    Result = Result ^ Bitmask;    // (0111 0100) ^ (1111 0010) = 1000 0110
 
    //이처럼 XOR 즉 베타적 비트 합은 암호화의 자주 사용 됩니다.
}
cs

◎ 비트 부정 (NOT)

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
#include<stdio.h>
 
void main()
{
 
    /*
    비트 부정은 자기자신의 비트별로 연산 한다는것만 빼면 논리연산자중 ! 과 똑같습니다.
    논리연산자중 앞에 !을 붙이면 참과 거짓이 바뀌듯 비트값앞에 !을 붙이게되면 비트의 0과 1이 바뀌게 됩니다.
    따라서 A(1100)를 ~A 한다 하면
    차례대로 
    ~1 = 0            
    ~1 = 0            
    ~0 = 1            
    ~0 = 1            
    1은 0으로 0은 1로 연산하게 됩니다.
    */    
 
    //예제 소스
    
    char Data = 0x86;         //16진수인 0x86은 2진법으로 표현하면 1000 0110 입니다.
    char Result = ~Data;    //Data를 부정(~) 하게되면 0111 1001이 됩니다.
    // 즉 0x79가 나오게 됩니다.
 
    /*
    연산
    ~1 = 0
    ~0 = 1
    ~0 = 1
    ~0 = 1
    ~0 = 1
    ~1 = 0
    ~1 = 0
    ~0 = 1
    */
}
cs



◎ 비트 이동 연산자


비트 이동 연산자는 총 << 과 >> 가지고있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include<stdio.h>
 
void main()
{
 
    /*
    비트 이동 연산자는 비트의 위치를 왼쪽 또는 오른쪽으로 온긴다고 생각하면 됩니다.
    A(1101)를 B = A<<2; 라 하면 B의 값은 0100이 됩니다.
    A를 왼쪽으로 2번 쉬프트 하면 11 0100 이 됩니다. 하지만 범위를 초과한 비트는 버려지게 됩니다.
    따라서 0100만 남아서 B의 값이 됩니다. ( A값은 변하지 않습니다. )
    */    
 
    //예제 소스    
    char Data = 0x76;         //16진수인 0x86은 2진법으로 표현하면 0111 0110 입니다.
    char Result01 = Data<<2;    //Data를 <<2 하게되면 1101 1000이 됩니다.
    char Result02 = Data>>2;    //Data를 >>2 하게되면 0001 1101이 됩니다.
    char Result03 = Data<<8;    //Data를 <<8 하게되면 0000 0000이 됩니다.
}
cs



참고 자료 1 : https://ko.wikipedia.org/wiki/%EB%85%BC%EB%A6%AC_%EC%97%B0%EC%82%B0

참고 자료 2 : http://www.tipssoft.com/bulletin/board.php?bo_table=FAQ&wr_id=598

참고 자료 3 : https://msdn.microsoft.com/ko-kr/library/336xbhcz.aspx





 



'프로그래밍 > Language' 카테고리의 다른 글

try, throw, catch 를 이용한 예외 처리 방법  (0) 2016.02.23
포인터에 대하여  (0) 2016.02.23
C언어 연산자에 대하여  (0) 2016.02.23
상수란 무엇인가?  (0) 2016.02.22
변수란 무엇인가?  (0) 2016.02.22
Posted by 시리시안
2016. 2. 23. 06:52

C언어 연산자

C연어 연산자는 C언어의 아주 기초적인 내용입니다.

아주 기초적이기 때문에 소홀히 하기 쉬운 부분이기도 합니다.

C언어에서 연산자가 상당히 많은 중요한 역할을 하고 있다 할 수 있지만, 주목받지 못하는 이유는 이해 하기가 너무 쉽고, 소프트웨어 관련 프로그램에서는 문법보다 많은 비중을 차지하지 못하기 때문입니다.

그럼에도 연산자를 알아야 하는 이유는, 당연히 C언어 코딩 과정이 게임을 만드든, 뭘 만드든 연산자와 함수를 통해서 이루어지기 때문이죠.

특히, 비트연산자는 하드웨어와 관련된 신호를 처리할 때 도움이 되는 개념으로 꼭 알아야 합니다.

 

 

※ 연산자 종류


 분류

연산자 

대입 연산자 

산술 연산자 

+, -, *, /, %

복합 대입 연산자 

 +=, -=, *=, /=, %=

증감 연산자 

 ++, --

관계 연산자 

 >, <, ==, !=, >=, <=

논리 연산자 

 &&, ||, !

조건 연산자

 ?:

비트 논리 연산자 

 &, |, ^, ~

비트 이동 연산자 

 >>, <<


 

◎. 대입 연산자는 오른쪽 값을 왼쪽으로 넣는것을 말합니다. 관계 연산자인 ==과 대입 연산자는 = 헷갈릴 수 있으므로 주의할것.

. 복합 대입 연산자는 왼쪽값에 오른쪽 값을 산술후 대입하는걸 말합니다. a+=1; 은, a에 1을 먼저 더하고(산술) 후에 값을 다시 a에 대입합니다

. 증감 연산자에 선 연산 후 증감, 선 증감 후 연산으로 나뉩니다. ++a 와 a++의 차이점을 알아두세요!

. 관계 연산자는 2개의 값을 비교할떄 사용합니다. 결과값은 1 또는 0 즉 참 또는 거짓으로 반환됩니다.

. 논리 연산자는 참과 거짓을 연산자에 맞춰서 연산하여 결과값을 1 또는 0 즉, 참 또는 거짓을 반환합니다.

. 조건 연산자는 관계연산자로 나온 결과값에 따라 반환값을 달리합니다.

 

 


 

. 대입 연산자


1
2
3
4
5
6
7
8
#include<stdio.h>
 
void main()
{
    int a;
    a = 1;    // 대입 연산자
}
 
cs

오른쪽 값을 왼쪽으로, 자료형에서 자동형변환이 일어날 때도 오른쪽에서 왼쪽순이었다.

 

. 복합 대입 연산자

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include<stdio.h>
 
void main()
{
    int a;
    a = 0//대입 연산자
 
    //복합 대입 연산자 
    a += 1;    //( a = a+1)
    a -= 1;    //( a = a-1)
    a /= 1//( a = a/1)
    a *= 1//( a = a*1)
}
 
cs

 

오른쪽값과 왼쪽값을 연산해서 왼쪽에 넣는다.

 

 

. 증감 연산자

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include<stdio.h>
 
void main()
{
    int a;
    int b = 0;
    int c = 0;
    a = 0//대입 연산자
 
    //증감 연산자
    a++;    // a = a+1
    a--;    // a = a-1
 
    //앞뒤의 차이
    a = 1;
    b = ++a;        // 증감 연산자가 앞에 있다면 선 연산후 대입 즉 a에 +1이 되며 b에는 2값이 들어간다.
    c = a++;        // 증감 연산자가 뒤에 있다면 선 대입후 연산 즉 c에는 2값이 들어가며 a는 3이 된다.
}
 
cs

 



. 관계 연산자

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include<stdio.h>
 
void main()
{
    int a = 1;
    int b = 0;
 
    //관계 연산자
    // >, <, ==, !=, >=, <=
    //참이면 1반환 거짓이면 0을 반환한다.
 
    int result =0;
 
    result = a<b ;     //a가 b보다 작다. 거짓     result값 0
    result = a>b ;     //a가 b보다 크다. 참         result값 1
    result = a==b ;     //a와 b가 같다.      거짓    result값 0
    result = a!=b ;     //a와 b가 같지않다.참     result값 1
    result = a>=b ;     //a가 b보다 크거나 같다. 참 result값 1
    result = a<=b ;     //a가 b보다 작거나 같다. 거짓 result값 0
}    
cs

 

 

. 논리 연산자

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<stdio.h>
 
void main()
{
    int a = 1;
    int b = 0;
    
    //논리 연산자
    //&&, ||, !
    //논리 연산자는 참과 거짓을 연산자에 맞춰서 연산하여 결과값을 1 또는 0 즉, 참 또는 거짓을 반환합니다.
 
    int result = 0;
 
//&&연산자, And 연산자
    result = a == b && a>b;    // a 와 b가 같으면서 a가 b보다 크다  / 거짓 result값 0
    result = a != b && a>b;    // a 와 b가 같지않으면서 a가 b보다 크다  / 참 result값 1
 
//||연산자, Or 연산자
    result = a == b || a>b;    // a 와 b가 같거나 a가 b보다 크다  / 참 result값 1    
    //a==b는 거짓이여도. a>b가 참이기에 or연산자는 참을 반환한다.
    result = a == b || a<b;    // a 와 b가 같거나 a가 b보다 작다  / 거짓 result값 10
    //a==b는 거짓ㅣ며. a<b도 거짓이기에 or연산자는 거짓을 반환한다.
 
//!연산자, Not 연산자
    result = !(a>b);    // a가 b보다 크다 의 값은 참이나, !가 있기에 반대값인 거짓을 반환한다. result값 0
    result = !(a<b);    // a가 b보다 작다 의 값은 거짓이나, !가 있기에 반대값인 참을 반환한다. result값 1
}
cs



. 조건 연산자

실제로 if문때문에 잘 쓰이지 않는.. 불쌍한 연산자..

알맞게 사용한다면 코드가 간결해지고, 난무한다면 가독성이 떨어진다는 전설의 연산자

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
#include<stdio.h>
 
void main()
{
    int a = 1;
    int b = 0;
    
    //조건 연산자
    // ? :
    // 관계연산자로 나온 결과값에 따라 반환값을 달리합니다.
    
// 사용법
// (조건)? (참):(거짓);
// ?앞에있는 조건의 반환값에 따라 (참) 또는 (거짓)을 실행시키거나 반환한다.
 
    //    예제
    int result = 0;
 
    result = (a>b)? 10 : -10;    //a>b의 값이 참이므로 :기준 왼쪽 값을 대입하여, result값은 10이 된다.
 
    result = (a==b)? 10 : -10;    //a==b의 값이 거짓이므로 :기준 오른쪽 값을 개입하며 result값은 -10이 된다.
 
    //또한 이렇게 함수 자체를 넣을수도있습니다.
    (a<=b)? printf("a<=b는 참입니다") : printf("a<=b는 거짓입니다");
    //물론 위의 값은 a<=b가 거짓이므로 : 기준 오른쪽 인 printf("a<=b는 거짓입니다")가 실행되게 됩니다.
}
cs


 

.. 비트 연산자


비트 연산자는 내용이 길어 질수있기 때문에, 

다음 포스팅에서 작성하겠습니다.



참고 : http://baeseonha.tistory.com/category/C-programing/C%EC%96%B8%EC%96%B4%20%EA%B8%B0%EC%B4%88





'프로그래밍 > Language' 카테고리의 다른 글

포인터에 대하여  (0) 2016.02.23
C언어 연산자에 대하여 - 비트연산  (0) 2016.02.23
상수란 무엇인가?  (0) 2016.02.22
변수란 무엇인가?  (0) 2016.02.22
#Pragma의 사용법  (0) 2016.02.22
Posted by 시리시안
2016. 2. 22. 21:51

변수란 무엇인가?

변수는 C언어의 기본 용어 중의 하나입니다.

지글 말하는것은 기초적인 변수 선언 방법을 말하는 것이 아니라, 변수의 기본적인 정의와 특성 이야기 하려고 합니다.

변수는 기초적인 것이므로, C언어를 조금이라도 배우거나 공부했다면 알겠지만, 그래도 이곳에서 정리를 하려 합니다. 

 

※ 변수의 정의와 변수 선언.

 

- 변수란, 데이터를 임시로 저장할 메모리 공간을 의미한다.

- 변수는 선언과 동시 초기화, 선언 후 초기화 모두 가능한다.

- 변수는 프로그램이 종료될 때, 저장된 데이터가 사라진다.

- 변수 선언은 정수형, 실수형, 문자형, 문자열형으로 나누어야 한다.

(int, char, double, char ..[], 문자열형은 고유 함수를 통해 변수에 저장하거나, 상수로 출력하는 방법이 있습니다. 기본 자료형으로 문자열을 변수에 저장하려는 경우 에러가 발생한다. 배열을 이용하거나, strcpy 함수 등을 써야 합니다.)


tip. 만약, 변수가 초기화되지 않아 데이터가 저장되지 않는다면, 쓰레기값이 들어간다.



밑의 구문을 보고 해석해보자.


int value= 10;

printf("%d", value);

"4 byte 정수형 변수에 10이라는 데이터를 저장하겠다. 그리고 10진수 정수로 value라는 변수에 저장된 데이터를 출력하겠다"

 

char string= 'a';

printf("%c", b);

"1 byte 문자형 변수에 a라는 데이터를 저장하겠다. 그리고 문자형으로 string에 저장된 데이터를 출력하겠다."

 

double dc= 3.1;

printf("%lf", c);

"8 byte 실수형 변수에 3.1이라는 데이터를 저장하겠다. 그리고 실수형으로 dc에 저장된 데이터를 출력하겠다."

 

 

- 변수는 초기화하고 나서 재정의가 가능합니다. 변수는 말 그대로 임의로 저장된 메모리 공간에 지나지 않으므로, 그 속에 저장되는 데이터 값은 언제든 변환이 가능합니다..


출처 참고 : http://baeseonha.tistory.com/entry/C%EC%96%B8%EC%96%B4%EA%B8%B0%EC%B4%88-%EB%B3%80%EC%88%98-%EC%83%81%EC%88%98-literal%EC%83%81%EC%88%98-symbolic%EC%83%81%EC%88%98

'프로그래밍 > Language' 카테고리의 다른 글

C언어 연산자에 대하여  (0) 2016.02.23
상수란 무엇인가?  (0) 2016.02.22
#Pragma의 사용법  (0) 2016.02.22
전처리기에 대하여  (0) 2016.02.22
C언어를 배워야 하는 이유  (0) 2016.02.22
Posted by 시리시안
2016. 2. 22. 21:29

#pragma

#pragma 는 ‘컴파일러에게 말하는 전처리기 명령’ 이라고 보시면 됩니다.

즉, #include 나 #define 처럼 전처리기에 의해 컴파일 이전에 처리되지만, 그 명령은 컴파일러에게 전달되기 때문이죠.

사실 pragma 는 C 언어의 기본 키워드라고 하기 보다는, 컴파일러에 종속적인 키워드라고 하는 것이 맞습니다. pragma 를 사용하는 문법은 컴파일러 마다 다르고 딱히 통일 된 것이 없기 때문입니다.



#pragma pack 사용


    - 기본적인 윈도우 환경에서의 데이터 정렬은 4byte를 기준으로 이루어지게 됩니다. 

 

struct A{ 

          char a;

          short b;

   };


위와 같은 구조체가 있다. 윈도우는 4byte가 가장 작은 메모리 할당량이다.

그러므로 어떠한 변수 하나를 잡기 위해서 무조껀 4byte를 기준으로 계산하게 된다.


위와 같은 구조체, 즉 short와 char을 선언하게 된다면, short = 2byte, char = 1byte 이므로 윈도우는 4byte를 할당 받아서

short에게 먼저 2byte 할당하고 남는 2byte중에서 char를 할당하게 된다.

즉, 3byte를 사용하기 위해서 4byte의 메모리를 사하게 되는 것이다.


하지만, 이것또한 컴파일러에 따라 달라 질 수 있다.Visual Studio는 기본적으로 8byte를 사용한다.

결국 위 구조체를 할당하기 위해서는 8byte가 필요하게 되는 것이다.


이에 따른 해결법으로 #Pragma pack을 사용 할 수 있는데,


#pragma pack(push, 1)

구조체

#pragma pack(pop)


위와 같은 방식으로 선언을 해준다면, 데이터 정렬이 변하게 되는 것이다. 위의 선언 (pack(push,1) 은 데이터 정렬 기준을 1byte로 한다는 것이다.

즉, struct A는 위의 환경에서는 3byte만이 필요하게 되는 것이다.

 메모리가 필요할 경우 무조건 1byte씩을 할당을 하게 되므로 불필요한 공간이 없이 필요한 공간만을 할당하게 되는 것이다.


주의 할점을 그후 #pragma pack(pop) 을 하지 않을 경우는 #pragma pack(push, 1) 로 선언된 이후의 변수 선언에 전부 적용이 되는 것이다. 

이것에 신경을 잘 쓰지 않으면 프로젝트가 엉망이 될 수 있다.

무조껀 1바이트씩하는게 좋은게 아니다. 주의!!!! 


예) 프로젝트 중간에 pack을 했다면 같은 값을 pack이 된 곳과 안된곳의 값이 다르게 표현이 되게 된다. 

이유는 구조체 자체의 메모리 할당이 서로 다르기 때문에 생기게 되는 곳이다. 

가장 유용하게 사용되는 부분은 네트워크 통신에 사용되는 packet을 정의할 때 가장 유용하게 사용된다. network packet은 서버 클라이언트에서 같이 사용하기 때문에 문제가 되는 점은 발견되지 않으며, network packet의 사이즈를 적절하게 해주기 때문에 더욱 유용한 것이다.



#pragma once 사용


이 명령은 컴파일러로 하여금 이 파일이 오직 딱 한 번만 include 될 수 있다는 것을 말해줍니다.


하지만 맨처음 말한것처럼 #pragma는  "컴파일러 마다 다르고 딱히 통일 된 것이 없기 때문입니다." 지원이 되는 컴파일러인지 확인하고 사용합시다.




참고 : http://itguru.tistory.com/103




'프로그래밍 > Language' 카테고리의 다른 글

C언어 연산자에 대하여  (0) 2016.02.23
상수란 무엇인가?  (0) 2016.02.22
변수란 무엇인가?  (0) 2016.02.22
전처리기에 대하여  (0) 2016.02.22
C언어를 배워야 하는 이유  (0) 2016.02.22
Posted by 시리시안
2016. 2. 22. 21:06

전처리기

 

- #include <stdio.h> 입출력 전처리기 호출

- #define 치환(정의) 역할

- #ifndef 매크로상수 재정의 방지, 조건부 실행

- #if () 조건부, 조건식에 따라 전처리를 수행

 

전처리기는 전에 처리하는 기능이 있다고 해서, 전처리기 이다. C언어 초반에 #include<stdio.h>를 코딩하고,

본격적으로 프로그래밍을 시도하는데, 그것의 의미를 알아두면, 전처리기를 보다 쉽게 이해할 수 있다.

 

 

> # include <stdio.h>

 

요약하면, "전처리기(#)야 include라는 파일에 있는 stdio.h 헤더파일을 미리 실행해죠" 이다.

stdio.h 는 include라는 파일에 있는 헤더파일 -C언어 함수들이 압축되어 있는 파일이다- 이다.

전처리기는 이 파일을 컴파일러가 기능을 수행하기 전에 미리 처리한다.

 

 



 

 

위의 visual studio 12.0의 include 파일에 들어있는 stdio.h 파일이다.

그럼, stdio.h 파일이 무엇이길래 먼저 처리하도록 지시하는 것일까?

 

stdio.h 파일은 Standard Input Output 의 입출력 해더파일이다.

컴파일러가 인간의 언어를 기계어로 바꾸는 과정을 수행하기 전에, 이 코드를 해석 가능하게 하기 위한

입출력 -printf(), scanf() 등- 문법을 미리처리하도록 하는 것이다.

 

tip. main()함수를 호출하고 종료시키는 일을 하는 것은 운영체제이다.

운영체제는 window xp, unix 등이고, 컴퓨터와 프로그램 사이를 원활하게 해주는 인터페이스(interpace) 역할을 한다.

 

 

> # define

 

#define으로 시작하는 전처리 문장을 매크로라고 한다.

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include<stdio.h>
 
#define byte int
#define name "조원우"
#define add(x,y) x*y
 
void main()
{
 
    byte a,b;
 
    scanf("%d,%d",&a,&b);
 
    printf("%s",name);
 
    printf("%d",add(a,b));
 
}
 
 
cs


 

1. #define byte int

"byte를 int 자료형으로 정의하겠다"를 의미한다. 즉,  int a; 라고 변수선언해야 할 것을, byte a; 라고 선언해도 이상이 없다는 것이다.

 

2. #define name "조원우"

"name을 조원우로 정의하겠다"를 의미한다. 즉, printf("%s", "조원우"); 인 것을, printf("%s", name); 으로 코딩해도 이상 없다.

 

3. #define add(x, y) x*y

"add(x, y)라는 함수는 x와 y를 곱한 것으로 정의한다" 를 의미한다. printf("%s", add(x, y)); 를 코딩하면, 곱한 값이 나온다.

 

#define은 여러가지 역할을 수행한다. 상수를 치환하고(2번), 자료형 -int, char, double- 을 정의하고(1번), 함수를 정의한다(3번). 나아가 #if, #ifndef 는 매크로 정의가 됬는가를 여부로 조건부 전처리를 수행한다.

 

 


 

 

> # if ()

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include<stdio.h>
 
#define DEFINES 1
 
void main()
{
    int num01 = 1, num02 = 2;
 
#if(DEFINES==1)
    printf("%d",num01*num02);
#elif(DEFINES ==0)
    printf("%d",num01/num02);
#else
    printf("%d",num01+num02);#
#endif
}
 
 
cs


 

1. #define DEFINES 1

"DEFINES 를 1로 정의한다"를 의미한다.

 

2. #if(DEFINES ==1)

"DEFINES 가 1이라면, printf() 를 수행해라"를 의미한다. 미리 정의된 DEFINES 의 값에 따라,

 #if 전처리기가 수행 될 수도 안 될 수도 있다.

 

3. #elif(DEFINES ==0)

"위의 조건이 다르고, DEFINES ==0이라면 아래 printf()를 수행해라"를 의미한다.

 

조건식이 참이면 해당되는 코드를 수행하고, 조건식이 거짓이면 건너띄는 구조이다.

 

 

> # ifndef

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include<stdio.h>
 
#ifndef MUL
 
#define MUL
#endif
 
 
void main()
{
    int num01 = 1, num02 = 2;
 
#ifdef MUL
    printf("%d",num01/num02);
#endif
 
#ifndef MUL
    printf("%d",num01*num02);
#endif
 
}
 
 
cs

 

 

1. #ifndef MUL

"매크로 상수 MUL이 정의되어 있지 않다면 MUL을 만들어라" 를 의미한다. 아직, 치환 단계가 아닌, 만들기만 한 것이다.

 

2. #ifdef MUL

"매크로 상수 MUL이 정의되어 있다면, MUL을 다음과 같이 치환해라" 를 의미한다. MUL은 1*2로 2로 치환될 것이다. 이 함수는 MUL이 num*num1이라고 정의하지 않지만, 자동으로, MUL을 인식하기 때문에 따로 정의할 필요가 없다.

 

3.#ifndef MUL

"매크로 상수 MUL이 정의되어 있지 않다면, 다음과 같이 MUL을 정의해라" 의미한다.

 

 

전처리기는 크게 3가지 유형이다. 헤더파일을 전처리하는 것, 메크로를 처리하는 것, 조건부 처리하는 것 이렇게 3가지 유형이다. 

프로그램 상, 절대 변하지 않을 수치를 쉽게 치환 하고 싶거나,

 함수를 간단하게 정의할 때나,

 이런 것들을 재정의 하여 오류가 뜨지 않도록 코딩 할 때,

 전처리기가 필요하다.



출처 및 참고 : http://baeseonha.tistory.com/entry/C%EC%96%B8%EC%96%B4%EA%B8%B0%EC%B4%88-%EC%A0%84%EC%B2%98%EB%A6%AC%EA%B8%B0%EB%9E%80

'프로그래밍 > Language' 카테고리의 다른 글

C언어 연산자에 대하여  (0) 2016.02.23
상수란 무엇인가?  (0) 2016.02.22
변수란 무엇인가?  (0) 2016.02.22
#Pragma의 사용법  (0) 2016.02.22
C언어를 배워야 하는 이유  (0) 2016.02.22
Posted by 시리시안