#include <stdio.h>


위의 코드는 C언어를 코딩할 때, 정말 수없이 봐오던 문장이다.

위를 통해서 유추할수 있는 전처리기의 특징은

1) #을 적는다

2) 세미콜론; 이 없다

3) 한줄씩 선언되며 #뒤에 어떤 명령이 온다.


짐작했듯이 #뒤에는 include명령 말고도 다양한 명령이 올수가 있다.

대표적인 전처리기의 명령 몇가지만 소개하자면 아래와 같다.



- 파일 처리를 위한 전처리기 문 : #include


- 형태 정의를 위한 전처리기 문 : #define, #undef


- 조건 처리를 위한 전처리기 문 : #if, #ifdef, #ifndef, #else, #elif, #endif


- 컴파일 옵션 처리를 위한 전처리기 문 : #pragma

http://blog.naver.com/since860321?Redirect=Log&logNo=130163293294



이중에서 오늘은 조건처리에 대해서 아래의 지식인 답변을 통해서 이해해보자.





질문

c++ #ifdef #ifndef #endif 이런거 설명좀요 .. 내공25

tto**** 
질문 486건 질문마감률61%
 
2012.03.12 01:20
10
답변
 
3
 
조회
 
8,444

#ifdef

#ifndef

#endif

 

이것들에 설명좀 해주세여요

 

#define 이 되있다면

?

안되있따면?

 

#endif ??

 

제가 보통 사용하는

 

그냥 공통적으로 필요한 헤더를 모아놓고

 

한번만 선언해놓거나 구조체를 모아놔서

 

#define 이란 header 만 하나 추가시켜놓고 거기에 몰아넣어서

 

그냥 선언하는 식으로 사용할떄

 

#ifndef __DEFINES_H__

#define __DEFINES_H__

 

....

 

#endif

 

이런식으로 쓰곤했는데 사실 이게 pragma once인가 그거

 

한번만 선언? 인가.. ( 자세힌 모르지만요 ) 하게 만든다고 하기 위해 한다고 하던데

 

고수님들 소스를 보면

 

#ifdef 소스~~~

#else

 소스~~~

#endif

소스~~~

막 이런식으로 저런게 되게 cpp 에도 잇꼬 header 에도 잇고

 

저런거 어떻게 사용하죵 ??



질문자/네티즌 채택

re: c++ #ifdef #ifndef #endif 이런거 설명좀요 ..

rantsu 
답변채택률96.7%
 
2012.03.12 02:19
답변 추천하기

질문자 인사

진심 감사합니다 아 이런 훌륭한 답변들이 .. !

님이 질문하신것을보고 "전처리기"라고 합니다.

전처리기에 대한 간단한 예를 들자면요.

윈도우를 보면 홈에디션, 프로페셔널 등등 각종 버젼이 있지요?
마이크로소프트에서 윈도우 버전을 만들때 홈에디션 따로, 프로페셔널 따로 만들까요? 
그렇지는 않겠지요?

이와 마찬가지로 님이 만약에 인터넷에서 파일을 다운받는 프로그램을 만들었습니다.
여기에 속도제한을 두고 싶어요. 
무료로 배포할때는 최고 속도를 10으로 돈주고 사는 사람에게는 최고 속도를 100으로 하고 싶습니다.

#ifdef PRO_VER
#define MAX_SPEED 100
#else
#define MAX_SPEED 10
#endif

이렇게 해두고 제어코드는 다음과 같이 하면 되겠지요?
if( speed > MAX_SPEED )
    speed = MAX_SPEED; // 최고속도를 초과하면 강제적으로 최고속도로 되돌림.

자.. 이렇게 작성을 마친후에...

유료버전을 만들기 위해 컴파일러 옵션에서 PRO_VER를 참으로 지정을 해서 컴파일을 합니다.
#ifdef PRO_VER
#define MAX_SPEED 100 <- 여기에 해당되어 컴파일
#else
#define MAX_SPEED 10
#endif
그럼 이 실행파일은 최고속도로 100을 갖게 됩니다.

무료버전을 만들기 위해 PRO_VER를 거짓으로 해서 컴파일을 합니다.
#ifdef PRO_VER
#define MAX_SPEED 100
#else
#define MAX_SPEED 10 <- 여기에 해당되어 컴파일
#endif

자~ 불필요하게 소스코드를 수정하지 않고, 컴파일러 옵션 설정만으로도 유료, 무료버전을 별도로 만들었습니다. 편리하죠?


이밖에 어떤 프로그램을 윈도우 버전, 리눅스 버전을 따로 만들어 배포하고 있습니다.
근데 C코드이기 때문에 대부분이 중복되고, 극히 일부분만 다르기 때문에 코드를 따로 관리하기가 힘듭니다.
그럼 어떻게 하면 되죠?

#ifdef WINDOWS
// 윈도우일때 필요한 코드
#else
// 리눅스일때 필요한 코드
#endif

동일한 코드는 그냥 놔두고, 극히 일부분만 다른 코드만 이런식으로 처리를 해줍니다.
그럼 컴파일할때 역시 코드 수정없이 윈도우버젼, 리눅스버젼 프로그램을 따로 만들 수 있는 겁니다.

뭐.. 이밖에도 통신환경을 설정한다거나 윈도우 버전별로 다른 처리를 하는 등등..
온갖 처리에 전처리기를 사용하게 됩니다.

이해가 되셨는지요?



아주 명쾌하다. 간단한 예제를 통해서 어떤식으로 사용되고있는지 까지 설명을 아우른다.

아무래도 답변자는 제대로 프로그래밍 언어를 아는듯 하다 ㅎ

암튼 몇가지 명령을 모아서 본다면 아래의 표와 같다.



 형식

내용 

 #if 

 ~이 참이라면 

 #ifdef

 ~이 정의되어 있지 않다면

 #ifndef

 ~이 정의되어 있지 않다면

 #else

 #if나 #ifdef에 대용된다

 #elif

 else if의 의미

 #endif

 #if, #ifdef, #infdef가 끝났음을 의미


지시어설명
#include <파일이름>미리 정의된 include 폴더에서 파일을 찾는다.
#include "파일이름"현재의 소스 코드가 저장되어 있는 폴더에서 먼저 찾고,
파일이 없다면 미리 정의된 include 폴더에서 파일을 찾는다.

#error


컴파일러 오류 메시지를 발생


#import


파일을 추가한다.


#elif


Else if


#else


Else


#ifndef


식별자가 정의되어 있지 않으면 참


#ifdef


식별자가 정의되어 있으면 참


#endif


#findef나 #ifdef, #else에 대한 전처리기 부분을 종료


#line


내부 행 넘버를 변경한다.


#undef


식별자를 정의하지 않는다.


#progma

링크할 때 헤더 파일이 한 번만 포함되게 한다







: c++ #ifdef #ifndef #endif 이런거 설명좀요 ..

easyflowinc 
답변채택률34.2%
 
2012.03.12 11:16
답변 추천하기
안녕하세요.

선행 처리기(preprocessor)란 소스프로그램이 컴파일되기 전에 미리 소스프로그램에 대해 일련의 작업을 하는것입니다.

예를들면, 프로그램 상에 기술된 약식표현을 모두 원래의 표현대로 바꾸어주는 역할을 하거나, 다른 프로그램 내에 포함시키는 등의 역할을 합니다. 일반적인 C컴파일러(compiler)는 소스프로그램 전체를 선행 처리기로 일단 처리한 다음 계속해서 컴파일(compile)하고 마지막으로 링크(link)하여 실행 가능한 프로그램(.EXE 파일)을 생성합니다.

 

컴파일러의 입장에서 보면 선행 처리기에 의해서 먼저 처리된 중간 프로그램을 소스프로그램의 일종으로 취급합니다.

즉, 선행 처리기는 목적 프로그램(기계어 프로그램)을 생성하는 것이 아니라, 선행 처리기가 하는 작업은 원시 프로그램을 컴파일하기 좋도록 가공하는 작업이라 할 수 있습니다. 다시 말해서 선행 처리기는 원시 프로그램 내의 모든 선행 처리기 지시어를 컴파일 할 수 있는 C 언어의 문장으로 바꾸어 주는 작업을 수행합니다.

 

선행 처리기의 대표적인 지시어는

#define

#include

#if

#else

#endif

...

등 여러가지가 있습니다.


위와같은 선행처리기 명령들은 조건적인 컴파일을 가능하게 해 줍니다.

큰 프로그램일수록 조건부 컴파일을 많이 합니다.


#ifdef OPTION

 

#include "aaa.h"

#define bbb 5


#else


#include "ccc.h"

#define ddd 15


#endif


위의 프로그램은 이전에 배운 조건문과 비슷한 구조이므로 쉽게 파악할 수 있습니다.

마지막에 endif 는 블럭이 끝남을 알리는 선행 처리기 명령입니다


#ifdef지정어는 그 다음에 오는 명칭,

1. (OPTION)이 선행처리기로 정의되었으면, 그 지점 다음에서 #else 전 까지 사용합니다.

2. (OPTION)이 선행처리기로 정의되어있지 않으면,  #else 다음부터 #endif 전까지 사용합니다.

 

다시말해, OPTION이 정의되었는가에 따라 소스코드를 선택하여 컴파일 할 수 있도록 합니다.


#if SYS == "AndroidPC"

#include "Android.h"

#endif


이러한 조건 컴파일의 용도는 주로 프로그램의 호환성을 높이기 위하여 많이 사용합니다.

파일의 앞 부분에 있는 몇 개의 중요한 정의만 바꾸면 다른 시스템을 위한 다른파일을 포함하여 사용할 수 있습니다.

출처 : http://kin.naver.com/qna/detail.nhn?d1id=1&dirId=1040101&docId=147544009&qb=I2lmZGVm&enc=utf8&section=kin&rank=1&search_sort=0&spq=0&pid=RxRH4lpySo8ssZxVfxKsssssssK-050019&sid=UxU5IgpyVo8AAExsNIE


네이버 지식iN. 


다른 전처리기와 관련 예제코드들:

http://blog.naver.com/pointer98?Redirect=Log&logNo=150036254254


명확한 typedef의 사용법 : 

http://blog.naver.com/pure_evil?Redirect=Log&logNo=60196169024





  

'Engineering > C Language' 카테고리의 다른 글

[C언어] 비트연산자와 16진법  (0) 2014.02.27
[C++] 구조체와 클래스  (0) 2014.02.24
[C언어] 메모리구조와 동적할당  (0) 2014.02.21
[본문스크랩] enum의 사용법  (1) 2014.01.28
[C언어] 구조체사용하기  (0) 2014.01.16

임베디드에서 진법연산과 비트연산은 기본중의 기본이다.


코드를 해석하다보면 특히 비트연산이 자주 등장하는데, 이를 이해하고 사용하기 위해서는 16진법에 대해서 알아야 한다.


예를 들어, 이진수 00110011을 16진수로 표현한다면


4개의 비트씩 묶어서 16진수로 표현된다.

        


            0 0 1 1      0 0 1 1 

         0X   3              3        = 33의 의미는 이진수를 16진법으로 표현한 숫자일뿐이며, 이를 해석할때 이진수로 변환하고

                                           MSB & LSB의 순으로 써내려가면 8비트의 원래 이진수값을 알수가 있다.



즉 정리하자면,


10진수 32+16+2+1 = 51은 이진수로 00110011 이며, 이를 16진수로 표현하면 0x33이 된다.



C 언어에서는 16진수를 표시하기 위해서는 숫자 앞에 0x 를 붙인다.



'Engineering > C Language' 카테고리의 다른 글

[C언어] 전처리기의 개념  (0) 2014.03.04
[C++] 구조체와 클래스  (0) 2014.02.24
[C언어] 메모리구조와 동적할당  (0) 2014.02.21
[본문스크랩] enum의 사용법  (1) 2014.01.28
[C언어] 구조체사용하기  (0) 2014.01.16

구조체는 연관있는 데이터를 하나의 그룹으로 믂기위해서 등장하였다.


C++에서도 구조체를 선언할수있다.


struct Smartphone Samsung;

struct Smartphone Apple;


이와 같이 선언이 가능하다. struct 키워드는 typefed를 통해서 축약이 가능하다.

예를들자면,


#include <iostream>


#define  LEN  30


using namespace std;



struct Smartphone 

{

char phonename[LEN]; //핸드폰 이름

int version;

int price; 

}


void showphone(const Smartphone  &phone)

{

cout<<"핸드폰명:"<<phone.phonename<<endl;

cout<<"버전명:"<<phone.version<<endl;

cout<<"현재가격:"<<phone.price<<"dollar"<<endl<<endl<<;


}


int main(void)

{

Smartphone apple={"apple, 2, 800}

showphone(apple);


}


매우 간략한 예제이지만, 구조체의 사용법과 간단히 함수의 동작에 대해서 이해가 가능할것으로 보인다.

여기서 하나의 의문을 가질수가 있다.

만약 구조체안에다가 함수를 삽입하면 어떻게 될까??





#include <iostream>


#define  LEN  30


using namespace std;



struct Smartphone 

{

char phonename[LEN]; //핸드폰 이름

int version;

int price; 


void showphone() //매개변수는 불필요 해졌다.

{

cout<<"핸드폰명:"<<phonename<<endl; //연산의 대상에 대해서 정보가 불필요해졌다.

cout<<"버전명:"<<version<<endl;      //변수에 대한 직접접근이 가능하기 떄문이다.

cout<<"현재가격:"<<price<<"dollar"<<endl<<endl<<;


}


}


int main(void)

{

Smartphone apple={"apple", 2, 800}

apple.showphone(apple);

return 0;

}



이로써 구조체는 대충 이해가 되었다. 그렇다면, 클래스와 구조체의 차이점은 무엇일까??


> 선언법과 초기화법이 다르다.

> 클래스의 변수와 함수에 대한 접근법이 다르다.  (접근제어 지시자의 사용)





* 접근제어 지시자


C++에서 접근제어 지시자는 아래와같이 3가지의 형태가 존재한다.


1) public : 어디서든 접근허용

2) protected : 상속관계에 놓여있을 때, 유도 클래스에서의 접근허용

3) private : 클래스 내 (클래스 내에 정의된 함수)에서만 접근허용


먼저 1)과 3)번에 관해서 아래의 예를 통하여 이해해 보자.


#include <iostream>



#define  LEN  30


using namespace std;



class Smartphone 

{


private:

char phonename[LEN]; //핸드폰 이름

int version;

int price; 

public:

void initphone(char *ID, int v, int p);

void showphone();

}


void Smartphone::initphone(char *NAME, int v, int p)

{

strcpy(phonename, NAME);  //phonename공간에 NAME의 내용을 복사한다.

version = v;

price = p;

}


void Smartphone::showphone() //매개변수는 불필요 해졌다.

{

cout<<"핸드폰명:"<<phonename<<endl; //연산의 대상에 대해서 정보가 불필요해졌다.

cout<<"버전명:"<<version<<endl;      //변수에 대한 직접접근이 가능하기 떄문이다.

cout<<"현재가격:"<<price<<"dollar"<<endl<<endl<<;


}



int main(void)

{

Smartphone apple;

apple.initphone("apple", 2, 800);

apple.showphone(apple);

return 0;

}


private 과 public의 쓰임을 이해하기 위해서 위의 예제를 먼저 따라가보자.

Smartphone 이라는 클래스가 선언되었고, 클래스는 private 과 public으로 구분된다.

public은 어디서든 접근이 가능하며, private 는 해당하는 범위 내에서만 접근이 가능하다.


따라서 위의 예제에서는 private 으로 접근을 하기 위해서 같은 클래스내에 public으로 선언된 initphone 함수를 통해서

초기화를 수행한다. 왜냐하면 private으로 변수나 함수가 선언되면 main함수에서 직접적으로 접근이 불가능하기 떄문이다. 

따라서 public으로 선언된 함수를 통하여 같은 클래스내의 private으로 접근하여 초기화를 수행한다. 


구조체의 경우는 Private이 없다. 기본적으로 구조체는 모든 변수와 함수는 public으로 선언되었다고 생각하면 된다.

만약 클래스에서 접근제어 지시자를 쓰지 않으면 구조체와는 반대로 모두 private으로 선언되었다고 봐야한다.


[용어의 정리]


위에서 말한 변수는 "객체"라고 표현된다.

클래스를 구성하는 변수는 "멤버변수"라고 부르며,

클래스를 구성하는 함수는 "멤버함수"라고 부란다.


그리고 C++에서는 H(헤더파일), CPP(정의파일) 로 코드들이 구분되어 다루어진다.


예를들어, 

Smartphone.h    : 클래스의 선언을 담는다

Smartphone.cpp : 클래스의 정의(멤버함수의 정의)를 담든다.


따라서, 위의 예제를 파일로 나눠본다면 아래와 같이 된다.


[Smartphone.h]


#ifndef __SMARTPHONE.H__

#define __SMARTPHONE.H__


#define  LEN  30


class Smartphone 

{


private:

char phonename[LEN]; //핸드폰 이름

int version;

int price; 

public:

void initphone(char *ID, int v, int p);

void showphone();

}



[Smartphone.cpp]


#include <iostream>

#include "Smartphone.h"


using namespace std;

void Smartphone::initphone(char *NAME, int v, int p)

{

strcpy(phonename, NAME);  //phonename공간에 NAME의 내용을 복사한다.

version = v;

price = p;

}


void Smartphone::showphone() //매개변수는 불필요 해졌다.

{

cout<<"핸드폰명:"<<phonename<<endl; //연산의 대상에 대해서 정보가 불필요해졌다.

cout<<"버전명:"<<version<<endl;      //변수에 대한 직접접근이 가능하기 떄문이다.

cout<<"현재가격:"<<price<<"dollar"<<endl<<endl<<;


}



[applephoneMain.cpp]


#include "Smartphone.h"


int main(void)

{

Smartphone apple;

apple.initphone("apple", 2, 800);

apple.showphone(apple);

return 0;

}














프로그램의 실행시 메모리의 구조는 OS를 가진 PC의 경우 아래와 같이 4가지 형태로 구성된다.


1)코드영역


2)데이터영역


3)힙영역 FIFO


4)스택영역 LIFO


갑자기, 옛날이야기를좀 하자면,

내가 C언어를 직관적으로 이해하게 되었고, 관심을 가지게 된것은 MCU를 다루면서 부터이다.

MCU를 장난감처럼 다루기 위해서는 입력과 조작의 도구로써, 프로그래밍 언어는 필수였었다.

물론 처음에는 남들이 작성해놓은 코드를 그대로 복사해서 넣어보고 동작하는것을 보고 좋았지만..

후에 내가 원하는 시스템을 설계하기 위해서는 프로그래밍 코드의 작성과 이해없이는 절대 제어가 불가능 했었다.

그래서 하나씩 코드에 의문을 가지게 되었으며

특히, 윤성우님이 저술한 열혈 C프로그램밍이란 책은 매우 쉬운 이해가 가능하도록 도와주었다.


대학교라는 특수한 교육공간에서 전공에서 배우는 프로그래밍언어수업보다 스스로하는 자기학습이 더 많은 이해와

프로그래밍에 대한 직관력을 길러주었다. 머 그래서 학교의 주입식 교육을 비판하자는 것은 아니고..

암튼 다시 돌아가서 아래는 MCU에 주입하는 프로그램을 코딩할때 내가 자주 고민하던 문제들이다.

 [아트메가 시리즈를 사용했으며, Avr Studio라는 컴파일러를 사용헸을때의 경우이다.]


>현재 코딩하는 코드는 어디에 저장될까? (플래쉬메모리-응용프로그램 섹션) 라는 고민을 했었다.

>분명 MCU에서 제공하는 레퍼런스 메뉴얼의 커맨드들은 모두다 16진수의 메모리를 가지고 있던데..이들은 어디에 어떻게 선언이 되어있는걸까?..

>그럼 코드내에 선언되는 변수는 어디에 저장이 될까?  

 : 전역과 정적변수는 데이터영역

 : 지역과 매개변수는 스택영역-이곳에서도 생성과 소멸이 이루어짐

                                           지역변수도 중괄호가 끝나면 소멸되기에.

 : 프로그래머가 변수들의 생성과 소멸을 정하는 경우 힙영역

>MCU의 시동시에는 컴퓨터와 같이 부팅이 있을까??

 :  부트 프로그램 섹션.

>잠깐 잠깐 빠르게 연산되는 변수들과 처리는 어디에서 빠르게 처리되는걸까?

 : ALU 산술처리 연산장치, 이들의 처리된 결과값들은 상태 레지스터에 기록된다. 

   제대로 명령이 처리되었다면 프로그래머는 상태 레지스터의 값을 읽어서 맞는지 판단하면된다. (예를들면 If문을 확인)



이와 같이 각 선언되는 변수나 상수에 따라서 저장되는 메모리의 영역은 다르다. 왜냐하면 비슷한 성향의 데이터들을 묶어서 저장해놓으면 관리와 접근속도가 당연히 빠르기 때문이다. 


그래서, 대부분의 메모리할당은 전역변수와 지역변수, 즉 데이터영역과 스택영역으로 처리가 된다.

하지만 이들로 해결이 되지 않는 상황이 발생할수가 있다. 그래서 힙영역이 필요하다.

아래는 힙영역이 필요한 이유 이다.


[실패한 예제]

#include <stdio.h>



char * inputName(void)

{

char name[20];

printf("what is your name?");

gets(name);

return name;

}


int main(void)

{

char * name1;

char * name2;

name1 = inputName();

printf("name1 = %s \n", name1);

name2 = inputName();

printf("name2 = %s \n", name2);

return 0;

}


의 예제의 경우, 포인터로 구성된 함수와 변수들은 지역함수로 선언된 InputName 함수로 인하여

원하는 결과를 보여주지 못한다. 왜냐하면 지역변수로 선언된 함수는 종료후 변수값들이 소멸되기 때문이다.



따라서, 이를 해결하기위해선, 변수가 할당이 되면 전역변수와 마찬가지로 함수를 빠져나가도 소멸되지 않는 성격의 변수가 필요하다. => 이는 힙 영역의 메모리를 사용할 경우 해결이 가능하다. 

후에 C++로 넘어갈 경우 메모리 구조에 대한 직관적인 이해는 반드시 필요할 것이다. 미리미리 공부하자.


>힙영역의 메모리 공간할당과 해제는 malloc 와 free 함수로 할수 있다.


void * malloc(size_t size);  // 힙역역으로의 메모리 공간 할당

void free(void * ptr); // 힙 영역에 할당된 메모리의 공간해제


=> malloc함수는 성공 시 할당된 메모리의 주소값을 가지고, 실패시 NULL을 반환한다.

그래서 사람들은 흔히 힙 영역을 "프로그래머가 관리하는 메모리 공간"이라고 명명한다.


예시: 

int main(void)

{

void * pointer1 = malloc(sizeof(int));  // int형 크기로 힙영역에 할당된다.

//사실 위 코드의 의미는

// void * pointer1 = malloc(4); 와 같다. 이는 어떻게 사용할지는 사용자에게 달려있기 때문이다.  

// 따라서, 반환디는 주소값을 적절히 형 변환해서 할당된 메모리에 접근해야만 에러가 없다.


      free(pointer1);  //pointer1이 가리키는 4bytes의 메모리 공간이 해제 되었다.


}




이제 앞서 실패한 예제를 malloc 함수와 free를 사용해서 다시 해결해보자.


[해결된 예제]


#include <stdio.h>

#include <stdlib.h>


char * inputName(void)

{

char * name = (char *)malloc(sizeof(char)*20);  //힙영역에 메모리 할당

printf("what is your name?");

gets(name);

return name;

}


int main(void)

{

char * name1;

char * name2;

name1 = inputName();

printf("name1 = %s \n", name1);

name2 = inputName();

printf("name2 = %s \n", name2);

free(name1);

return 0;

}


따라서, malloc함수를 사용하게되면 지연변수나 전역변수로는 감당할수 없는 포인터관련 함수와 변수들로 일어나는 문제점들을 해결 할수가 있다. 



'Engineering > C Language' 카테고리의 다른 글

[C언어] 비트연산자와 16진법  (0) 2014.02.27
[C++] 구조체와 클래스  (0) 2014.02.24
[본문스크랩] enum의 사용법  (1) 2014.01.28
[C언어] 구조체사용하기  (0) 2014.01.16
[C언어] 포인터 사용하기-2  (0) 2014.01.15

출처 :  네이버 지식in

http://kin.naver.com/qna/detail.nhn?d1id=1&dirId=1040101&docId=70600274&qb=ZW51bQ==&enc=utf8&section=kin&rank=3&search_sort=0&spq=1&pid=RXPJbc5Y7tGssckXvJNsssssstG-506114&sid=Uue7GHJvLB4AAHbuDW8


re: enum에 대해서..(내공有)

mydog21 
답변채택률73.2%
 
2008.01.09 14:43


쉽게 말해 enum 은 열거형 변수 입니다.

"어떻게" 생각하면 struct와 다르냐? 라고 물을 수 있지만.

"전혀" 다릅니다.

 

보통 enum은 상수를 정의하는데에 많이 쓰입니다.

소스의 가독성을 높인다거나 상수에 의미를 부여해서 쓸 수 있습니다.

 

전처리기인 #define 을 사용하거나, 전역 변수를 const로 선언해서 상수처럼 사용이 가능하지만

enum을 상수로 생각해서 처리할때의 이점이 있습니다. (뒷부분에 설명하겠습니다.)

 

우선 이넘(enum)을 선언하는 방법은 다음과 같습니다.

 

enum {mon, tue, wed, thr, fri, sat, sun } day;

 

여기서 day를 열거형 변수라 하고 그 하위의 항목(원소) 들 ( mon, tue, wed, thr, fri, sat, sun)로 구성됩니다.

 

특별하게 값을 지정하지 않는다면 enum의 각 원소들은 순차적인 "정수" 값을 갖게 됩니다.

이때 갖게되는 값은 mon부터 0, 1, 2 ... 6 까지 각 각 가지게 됩니다.

 

특별하게 값을 지정해서 enum을 사용한다면

 

enum {mon=100, tue, wed=200, thr, fri=300, sat, sun } day;

 

이렇게 선언을 하게 되면, mon은 당연히 100일거고,, tue는... 150일까요?

tue는 101을 갖게 됩니다.

 

enum의 값을 정하는 정의는 "이전 원소 값의 + 1" 입니다.

 

 

그럼. "왜 이넘을 사용해야 하는가.." 입니다.

 

사실 enum은 필요없을 지도 모릅니다.

#define 이 있고,

const int 가 있습니다.

 

하지만 C++ 이 enum에게 준 몇가지의 특별한 이유 때문에 enum을 사용합니다.

 

① 기억이 용이하다.

enum대신 정수형 변수를 대신 사용할 수 도 있습니다. 하지만, 이렇게 될때 각 정수의 의미가 무엇인지를 만드는 사람이 기억하고 있어야 하죠. 값이 많아질 수록 혼란스러울겁니다. 보통, 사람은 숫자보다 문자를 더 잘 기억합니다. 그리고, enum의 원소들을에게 의미를 부여한 이름을 준다면 더 기억하기가 쉽겠죠..

 

② 소스의 가독성(Readability)이 높아진다.

enum의 원소만 봄으로써, 그 상수의 의미를 보다 쉽게 파악 할 수 있고, 소스 전체의 가독성을 높이는데 도움이 됩니다. 예를 들어 whatdays = 0; 이라고 표현한다면, 이게 월요일인지,, 일요일인지, 도무지 알 길이 없습니다. 하지만 whatdays = mon; 이라고 한다면, 두말할나위없이 이해할 수 있겠죠. 두명이상의 Project나 팀단위 작업을 할때에는 알고리즘을 아무리 잘 짜도 소스가 X같으면, 프로젝트 진행이 어려워집니다. 따라서 이런 팀단위 작업에는 가독성이 아주 중요시 됩니다.

 

③ 안전하다.

앞서 예를든 days에 대입될수 있는 값은 7개중 하나로 제한이 되기때문에 사용자의 실수를 컴파일러가 미리 예방해줄 수 있습니다. 따라서 변수형을 사용하는것보다 안전하죠.

 

 

필수는 아니지만 있으면 좋다. 이게 이넘(emum)입니다.

 

 

enum을 보다 효율적으로 사용하는 방법은

 

typedef를 사용하는겁니다.

 

바로 예부터 보자면...

 

typedef enum CDays {enum {mon, tue, wed, thr, fri, sat, sun };

 

이제 CDays는 하나의 타입으로 생성된겁니다.

 

CDays WhatDay(); 와 같이 오늘이 몇요일인지 리턴하는 함수를 작성 할 수도 있구요.

 

CDays Day = WhatDay();

 

와 같이 Enum을 효과적으로 처리할 변수를 만들어 사용할 수 있습니다.

'Engineering > C Language' 카테고리의 다른 글

[C++] 구조체와 클래스  (0) 2014.02.24
[C언어] 메모리구조와 동적할당  (0) 2014.02.21
[C언어] 구조체사용하기  (0) 2014.01.16
[C언어] 포인터 사용하기-2  (0) 2014.01.15
[C언어] 포인터 사용하기-1  (0) 2013.11.30
구조체는 프로그램 개발을 위한 매우 중요한 요소로써, 앞으로 코드를 다루일이 있다면 반드시 익히 가야하는 개념입니다. 구조체는 하나이상의 변수를 묶어서 새로운 자료형을 정의하는 도구를 말합니다.

예를들어서, 학생정보시스템을 만든다고 가정할때, 학생들의 이름,키,몸무게,주소 등을 학생마다 선언하고 관리해야한다면 여간 까다롭고 귀찮은 일이 아니다. 이럴때, 구조체를 이용하면 한 학생의 정보를 표현하는 여러가지 변수를 하나로 묶어버릴수가 있다. 이렇게해서 등장한 개념이 구조체이다. 

ex)

struct student
{
char name[30];    //student구조체를 구성하는 멤버 name
int height;          //student구조체를 구성하는 멤버 height
int weight;       //student구조체를 구성하는 멤버 weight
int address;   //student구조체를 구성하는 멤버 address

}

▶ 위의 예시와 같이 구조체 변수의 선언과 접근은 아래와 같다.


struct    type_name   val_name  //구조체 변수선언의 기본 형태

         구조체 이름  /구조체 변수이름


즉, 위의 구조체 student에 구조체 변수를 선언하고자 한다면,

struct student Namja; 와 같이 선언 할수가 있다.

Namja구조체 변수 안에는 


char name[30];   
int height;        
int weight;    
int address;          

의 변수들이 존재한다. 구조체 변수를 선언함으로써 student의 멤버들을 그대로 상속받을 수가
는것이다.  

▶ 구체의 접근법은 아래와 같다.

구조체 변수의 이름.구조체 멤버의 이름

ex) Namja.height = 180;  // 구조체 변수 Namja의 멤버 height에 180을 저장

구조체를 정의함과 동시에 변수를 선언할 수도 있다.

struct student
{
int height;        
int weight;    
} boy1, girl1;

이는 student 구조체를 정의함과 동시에 student형 구조체 변수 boy1, girl1을 선언하는 문장이다.
그리고 구조체 변수의 선언과 동시에 초기화는 중괄호를 통해서 초기화할 값을 명시하면 된다.

struct student boy={"180", "75"};



▶  구조체와 배열 & 포인터

먼저, 구조체 배열이란, 구조체 변수들이 여러개 모인 배열을 의미한다. 예를들면 위의 student 구조체에서 남학생이면 10명이면 struct student boy[10]; 과 같은 형태로 구조체를 선언할수가 있다.

point형 구조체의 포인터 변수는 아래와 같이 선언되고 초기화된다.

struct student
{
int height;        
int weight;    
}

struct student boy= {178,70}; //  student형 구조체의 boy 변수를 선언과 동시에 초기화
struct student *ptr = &number;  // 구조체 point 변수 ptr이 구조체 변수 boy를 가리킨다

접근법은 
(*ptr).height=180;  // ptr이 가리키는 구조체 변수의 멤버 height에 180을 저장
(*ptr).weight=75;  // ptr이 가리키는 구조체 변수의 멤버 weight에 75를 저장


▶  typedef 선언과 구조체

typedef는 기존의 존재하는 자룡형의 이름을 사용자가 원하는 형태로 새이름을 부여하는 것을 말한다.

이것이 왜 구조체와 연관이 있는가 하면, 위의 예를 다시 가지고 설명한다면,

구조체 변수의 선언시
struct student boy; 로 선언해야 한다.

하지만 이를 typredef를 사용해서 선언후 사용한다면,


typedef struct student  Student;  // struct student 대신에 Student 이름을 부여함.


이제 struct student boy;의 명령이 간편하게 Student boy; 로 줄었다.


그래서 많은 프로그래머들이 변수의 이름의 첫글자를 대문자로 지정하여 구조체의 선언시 typedef가 적용된 형태의 축약형을 많이 사용한다. 생략가능함을 염두해 두고 코드를 유념해서 보기를 바란다.



▶ 구조체의 또다른 접근법 : 포인터 연산자 ->


이는 위의 구조체 멤버변수 접근방식 (직접방식)과는 다른 방법으로써, 간접접근 방식이라 할수가 있다.

(대부분 힙에 할당된 기억장소에 접근할 때)

= 포인터와 같은 참조변수를 이용하여 구조체 변수의 주소값에 접근하기 위해서 -> 연산자를 사용하여 접근한다.


예를 들어보자.


#include <stdio.h>

#include <stdlib.h>

#include <string.h>


typedef struct smartphone  //다음부터 smartphone만 입력하면 구조체 선언을 하도록 typedef 선언

{

char name[30];

int price;

}Apple;   // 구조체의 선언과 구조체 변수 apple의 선언.


int main(void)

{

Apple *applephone;  //구조체변수 Apple형의 참조변수 applephone선언


applephone = (Apple*) malloc(sizeof(30)); //힙에 메모리를 할당하라.

//(Apple*)는 형을 의미하고, 30 메모리 만큼의 동적할당값을 applephone 참조변수에게 할당하라. 


strcpy(Apple->name, "아이폰5");

Apple->price = 800;

printf("%s %d \n", Apple->name, Apple->price);

free(applephone);

return 0;


}



메모리 동적할당에 대한 자세한 설명은, 아래의 블로그 게시글을 참고하자.

http://rus1031.blog.me/80187120151





:: 다차원 배열 ::


다차원 배열이란? 2차원 이상의 배열을 의미한다.

즉,2,3차원을 주로 사용한다. (4차원은 이해하기 까다로워 다루지 않는다)


ex) 2차원 배열의 선언


int array1[2][5]; // 세로가2, 가로가5인 int형 2차원 배열을 의미한다,

위에서 선언된 배열의 인덱스값은 아래와 같다.


1행 1열 [0][0]

1행  2열 [0][1]

 1행 3열 [0][2]

 1행 4열 [0][3]

 1행 5열 [0][4]

2행 1열 [1][0]

2행  2열 [1][1]

 2행 3열 [1][2]

 2행 4열 [1][3]

 2행 5열 [1][4]



그리고 이들의 할당된 메모리 형태는 아래와 같다.


 0x1000

 0x1004

 0x1008

 0x100C

 0x1010

 0x1014

 0x1018 0x101C

 0x10020

 0x1024


0x1014에서 0x0014의 의미: 16^1*1 + 16^0*4 = 16+4=20


이를 통하여 알수있는 사실은, 2차원 배열도 메모리상에서는 1차원 배열의 형태로 존재한다는 사실이다.


2차원 배열의 선언과 동시에 초기화는 아래와 같다.

int arr[2][2] = { {1,2}, {3,4} };     =   int arr[2][2] = {1,2,3,4}; //동일하다. 

1,2

3,4

의 형태로 선언된 2x2 행렬의 형태로 선언과 동시에 초기화 되었다.


만약 행과 열에 정의하지않는 요소가 존재한다면, "0"으로 채워진다. 배열과 동일하다.


:: 포인터의 포인터 ::


의미 : 포인터변수를 가리키는 또다른 포인터 변수를 말하며 흔히 '이중 포인터' or '더블 포인터'라 한다.


정의 :  int ** ptr; //int 형 이중 포인터 변수 ptr 선언


앞서 설명한 포인터의 개념과 별반 다를게 없는 개념이다.

포인터 변수는 주소값만을 저장하는 변수이고, 포인터 변수또한 가리키는 포인터변수의 주소값을 저장하는 변수이다.

예를들면, 중요한 문서의 이중봉투라고 볼수가 있겠다.

int main(void)

{


int number =1;

int *ptr = &number;

int **doptr = &ptr;


}

이들 변수들이 가리키는 참조관계는 아래와 같다.

doptr -> ptr -> number =1;


*doptr은 포인터변수 ptr을 의미한다.

**doptr 또는 *(*doptr) 은 변수 number을 의미한다.


따라서, 포인터변수들의 값을 Swap하는 함수의 형태는 아래와 같아야 한다.


void Swapfunction(int **dptr1, int **dptr2)

{

int *temp = *dptr1;

*dptr1 = *dptr2;

*dptr2 = temp;


}

어떤 객체의 값을 바꿔야 되는가에 대해서, 명확하게 생각해야만 실수가 없을 것이다.

그리고 사실 삼중포인터도 존재한다. int ***tpointer; 



:: 다차원 배열과 포인터와의 관계 ::


앞장 포인터 사용하기-1에서 배열이름과 포인터와의 관계에서 가장 주요하게 언급한것이 바로

배열 이름이 해당되느 자료형 포인터이므로 


예시)

int arr[2]; //int형 포인터 arr을 선언 = 배열 arr을 선언

prinft("&p", arr+1); 


여기서  출력되는 값은 arr+sizeof(int)의 값이 출력된다. 왜냐하면, 포인터형은 포인터를 대상으로 하는 가감산연산의 결과


에서 크기를 결정하기 때문이다. 즉, 포인터형이 같다면 증가 감소의 크기값도 동일 할것이다.


하지만 다차원 배열에서는 이것만을 고려해서는 안된다.


2차원 배열이름의 포인터형은 가로의 길이에 따라서 포인터의 크기가 달라지기 때문이다.


즉, 몇개의 열을 가지고 있는가에 따라서 "다차원 배열이름"+1 = 가진 열의 수만큼 행이 하나더 생성 되기때문이다.


따라서 이 문제가 다차원 배열이름의 포인터 형 결정을 어렵게 만드는 요인이다. 


int * ExA[3];  //포인터 배열,  int형 포인터 변수로 이루어진 배열을 선언한 것.

int (*ExB)[3]; //배열 포인터, 가로길이가 3인 2차원배열을 가르키는 포인터 변수를 선언한 것.


:: 함수 포인터와 void 포인터 ::


함수 포인터 : 메모리상에 저장된 함수의 주소 값을 저장하는 포인터 변수를 의미한다.  함수도 변수처럼 메모리 공간에 저장되어서 호출시 실행되기 때문이다.


형태 :  반환형 - 포인터변수이름 - 매개변수 (즉, 반환형과 매개변수가 포함된것이 포인터 형이다)


ex) int (*simplePtr) (int);


위의 예제는 함수포인터를 선언한 형태이다. 각각의 해체해보면,

int : 반환형이 int인 함수 포인터

*simplePtr : simplePtr 은 포인터

(int) : 매개변수 선언이 int 하나인 함수 포인터


그럼 예제를 확장시켜서, 실제 함수가 존재하고 이 함수의 주소값을 저장할수있는 함수포인터를 선언해보자.


int functionA(int num1, int num2)

{

생략..

}


int (*FPtr) (int, int);


FPtr=functionA; //함수포인터에 함수의 주소값이 저장되었다.


위의 functionA라는 함수의 주소값을 저장할수있는 함수포인터의 예제이다.

그래서 실제 사용은 아래와 같이 하면 같은 결과값이 나타난다.


FPtr(1, 2); // 이는 functionA(1, 2); 와 동일한 결과를 보인다.



만약 형타입도 없고, 매개변수도 없는 함수, 즉 Void타입을 함수 포인터로 선언할 경우는 아래와 같다.

void functionA(void)

{...}


void * ptr;  //void타입의 함수 포인터 선언.



C와 C++에서 포인터의 개념은 매우 중요하다.

따라서, 코딩을 하는 사람이라면 한번쯤 개념을 정리해놓고 두고두고 보는것도 매우 좋은 방법이다.


하지만, 포인터만 안다고 포인터와 관련된 모든 코드를 해석할 수있는 것은 아니다.

그러므로 먼저, 배열과 포인터의 관련성과 나아가 구조체와의 포인터와의 관련성 그리고 포인터함수에 대해서 고민해보고 이해하여야 한다.


아래의 글은 아주 간단한 예제와 기본적인 개념에 대해서만 언급하고 다룬다. 

따라서 빠른 이해와 사용을 목적으로 참조하기만 바란다. 




먼저 배열에 대해서 이해하자.


배열이란 무엇일까?

배열은 많은 데이터를 저장하고 처리하기에 매우 유용하다.

  배열선언의 형태 : 배열이름, 자료형, 길이정보

   ex)   int Num[10];   //길이가 10인 int자료형 1차원 배열 Num 을 의미한다.

      

            int Num[10]={1,2,3,4}; //이런경우는 선언과 동시에 초기화를 하는 방법이다.

                                           //공간은(길이) 10을 주었지만 4개만 초기화되었기에 나머지는 0으로 채워진다.

                                           //즉 1,2,3,4,0,0,0,0,0,0 의 값들이 저장된다.


1차원은 행1인 행렬을 의미한다. 그냥 수의 나열이라고 생각해도 된다.

하지만 2차원, 고차원으로 갈수록 개념을 잘 잡아야 한다. 후에 다차원 배열때 다시언급.


★배열의 위치정보(인덱스)는 0부터 시작한다.


배열을 이용해서 문자열 변수도 표현가능하다.


char str[10]="Hi hello";

//내부적인 저장은 : H i  h e l l o \0  으로 된다.

널문자는 문자의 끝이라는 의미로 들어간다. 그래서 문자열의 시작과 끝을 알수가 있다.




이제 포인터에 대해서 이야기 해보자.


포인터도 하나의 변수이다. 포인터변수는 메모리의 주소값을 저장하기 위한 변수이다.

이전에 써오던 다른 변수들은 상수값을 저장하기 위한 공간이었다면 포인터는 주소값(메모리)을 저장하기 위한 공간이다.


포인터는 2가지  Type이 존재한다.

1. 변수형태의 포인터 : 초기화 후 수정이 가능

2. 상수형태의 포인터 : 초기화 후 수정이 불가능


:: 변수포인터 ::


간단한 예)


int main(void)

{

int num=1;

int *ponum;

ponum = &num; // num변수의 주소 값을 포인터 변수에 저장함.


}


포인터변수의 선언형태.


자료형타입 * 변수이름;

ex) int * number;

     char * moonja;


선언시, * 연산자를 사용하는데, 해석시 이는 int형 *number 포인터를 선언한다고 보면 된다.


포인터변수의 선언형태는 보면 자료형 타입을 꼭 붙여준다. 이는 포인터변수에 저장되는 주소값은 저장되는 변수의 주소값의 처음 시작값을 의미하기 때문이다. 따라서 자료형의 크기에 따라서 포인터변수가 할당받는 공간크기가 다르며, 이는 포인터주소값의 증가폭이 자료형의 메모리크기와 동일하다는 뜻이다.


위의 int *number 포인터의 경우 참조하는 변수의 주소시작값으로 부터 4byte(int형크기)의 크기를 할당한단 의미이다.

그리고 포인터변수의 사용은 연산자*의 유무로 나눠진다. 예시는 아래와 같다.


int main(void)

{

int num=3;  //int 형 변수 num의 선언

int *ponum=&num; // int형 포인터변수 *ponum에 변수num의 주소시작값을 넣는다. 

                          // 혹은 *ponum변수는 변수num의 시작주소값을 가리킨다.

                         // 이는 int *ponum=nulll;

                         // ponum = &num; 과 동일한 의미이다. 

(*ponum)+=10;    // *pomum이 가리키는 주소값의 데이터에 +10을 한다는 의미이다.

                       // *을 붙이면 가리키는 주소값에 대한 접근, *을 안붙이면 포인터변수자체의 공간에 접근.

printf("%d", *ponum); //*ponum 이 가리키는 주소값의 데이터를 출력하라. 


}


포인터 변수는 선언시 초기화 하지 않으면, 쓰레기값으로 초기화 된다. 따라서 0; null; 로 초기화 해주면 된다.





배열의 이름은 포인터를 의미한다. 즉, 배열변수가 메모리에 할당받은 주소의 시작값을 의미한다.

따라서 배열이름을 가지고서 포인터처럼 사용이 가능하다.

단지, 포인터와 다른점이 있다면. 주소값의 변경이 불가능하다는 것이다. 배열은 데이터를 담고 포인터는 주소값을 담기때문이다.


int main(void)

{

int arr[2]={1,2};

int * ponum=arr;  // int *ponum=&arr[0]; 과 동일한 문장이다.

      ....

      printf("%d" ...)  //생략.


}


변수형태의 포인터변수는 증가 및 감소가 가능하다.

int *ptr=&num;

ptr++;  


와 같은 형태가 사용가능하다. 의미는 *ptr 포인터변수가 int형 자료형 데이터크기만큼 주소값이 후증가된다는 의미이다.

아래의 예제를 생각해보자


*(++ptr) =1; //ptr포인터에 저장된 값(즉, 주소값) 자체를 선증감시키고 가리키는 주소값의 공간에 1을 대입하라

*(ptr+1)=1;  //ptr포인터가 가리키는 주소값의 공간의 주소를 자료형 공간단위만큼 하나증가 하고 그 공간에 1을 대입하라.


위 두문장의 차이점은, 아래의 문장의 ptr이 처음 가리키는 주소값은 변하지 않는다는 것이다.

즉, 첫번째 문장의 ptr은 ptr이 가지고 있는 메모리주소값이 선증감한것이다.

(포인터변수란 주소값을 저장하고있는 변수이다)


그럼 이제 배열과 포인터를 연관지어서 중요한 공식을 얻어낼수가 있다.


array[i] == *(array+i); 


i번째 배열의 주소값은 포인터의 주소값이 i만큼 증가한 뒤의 주소값과 동일하므로 이 문장이 가리키는 데이터는 같다.


:: 문자열의 표현법과 상수포인터 ::


char num[] = "hello"; //배열의 길이를 자동으로 계산한다.


char *num = "hello2"; // hello2 문자열이 선언된 어느 메모리공간의 시작주소값을 *num포인터가 가리킨다. 

따라서 위의 경우는 상수 포인터가 되서, 포인터변수가 가리키는 주소값의 변경이 불가능하다.


:: 포인터 배열 ::


포인터 배열이란? 포인터 변수로 이루어진, 주소 값의 저장이 가능한 포인터들로 이루어진 배열을 의미한다. 


즉, int * num[10]=null; // 길이가 10인 int형 포인터 배열 num


char * stream[2]={"hi","bye"}; //길이가 2인 char형 포인터 배열을 의미하며, 

                                           //내부적인 동작은  hi와 bye가 어느 메모리공간에 선언되고 그 주소값이

char * stream[2]={0x1001, 0x1005}; //와 같은 형태로 포인터 배열에 저장되는 것이다.


여기서 포인터변수는 분명 char을 참조하기에 1바이트만 소비할텐데, 왜 4바이씩 저장되었을까? 

이는, 컴퓨터의 메모리구조에 관련이 있다. 32비트 시스템의 OS라면 (32비트=4바이트) 4바이트로 포인터변수가 메모리값을 사용할 것이, 요즘 사용중인 64비트 컴퓨터라면 8바이트의 공간을 소비할 것이다.





이제 함수와 포인터를 서로 엮어보자.


포인터를 배우기 전까지는 우리는 함수 인자의 전달은 값을 중심으로 한다고 체득했다.

히자만 이제 주소값을 보낼수 있음을 알게 될것이다.


간단한 예)


int simple_example(int *prr)

{

...

}                       

int main(void)

{

int arr[2]={0};

int *prr=arr;

.

.

simple_example(prr); // simple_example(arr); 동일한 의미이다.

}


:: const 선언의 활용 ::


const는 const가 가리키는 변수의 저장된 값에 대해서 변경을 허락하지 않겠다는 의미이다.


int num=0;

const int* ptr = &num; // 포인터 ptr이 가리키는 변수 num값의 변경을 허용하지 않겠다. 


"or"


int * const ptr = &num; // 포인터 ptr자체의 값변경을 허용하지 않겠다. 


이렇게 생각해보자, const와 가까운쪽을 고정시키겠다는 의미로 보자.

즉, const int* ptr = &num; 의 경우는  int* ptr 전체를 고정하기에 이가 가리키는 num값이 고정되는 것이고

int * const ptr = &num;의 경우는 ptr 포인터 변수를 고정시키기에 변수가 저장하는 메모리값을 고정하는 것이다.





[관계연산자]------------------------------------------


<

>

==  둘이 같은가?  :  같으면 참

!=   둘이 서로 다른가?  : 다르면 참

<=

>=


[논리연산자]------------------------------------------


&&  A,B모두 참이면 연산결과는 참으로 반환한다 (논리 AND)

||   A.B둘중 하나라도 참이면 연산결과로는 참을 반환 (논리 OR)

!      예를들어서 !C 이면 C가 참이면 거짓을 반환 거짓이면 참을 반환하는 형태 즉, 논리 NOT


[비트연산자]


대상으로 하는 자료를 비트단위로 생각하고 연산을 실시한다.


&          비트단위로 AND연산

|           비트단위로 OR 연산

^           비트단위로 XOR연산

~          단항 연산자로써, 모든 비트를 반전시킴

<<         비트열을 왼쪽으로 이동시킴

>>         비트열을 오른쪽으로 이동시킴



[음수의 표현법]------------------------------------------


1의 보수를 취한다 -> 1을 더한다.



[기본적인 반복문의 형태: While문]------------------------------------------


While(조건)

{

내용;  //내용실행후 조건으로 다시 이동후, 조건이 부정되지 않으면 중괄호 내용 다시 실행

}



while(조건1)

{


   내용1;


while(조건2)

{

  내용2;   //일단 내용1을 1번 실행후 조건2가 부정될때까지 내용2를 실행후 다시 조건1로 회귀.

}

}


do

{

 내용; // 일단 do 실행. 그 후 while 조건을 검토후 조건이 부정되지 않으면 다시 do실행.

}while(조건);  // 세미콜론 주의.


do~while 과 while문은 실제로 서로 대체가 가능한 문법이다. 

하지만 눈에 보이는 전달력의 차이로 꼭 1번 실행이 필요하다면 do~while로 표현하는것이 가독성을 위해서 추천한다.




[For 문의 형태]------------------------------------------


for( 초기식 ; 조건식 ; 증감식)

{

 내용;

}


으로 이루어져 있으면, 초기식은 변수의 선언 및 초기화, 조건식은 선언된 초기식조건을 이용하여 검사 (부정이면 반복중단), 증감식은 내용을 실행후 다시 조건식을 검사하였을 때, 조건식이 부정되지 않으면 조건의 변수를 증가시킴. 


예를들면,


for( int i=1; i<5; i++)

{

  printf("good"); //4번의 good이 결과 출력된다.

}



[조건문의 형태]------------------------------------------


if(조건)

{
내용; // 조건이 참이면 아래 중괄호의 내용을 실행한다.

}



if(조건)

{

  내용1;  //조건이 참이면 내용1을 실행, 아니라면 내용2를 실행

}

else

{

  내용2;

}




if(조건1)

{

  내용1;  //조건1을 검사하여 참이면 내용1 아니라면, 다시 조건2를 검사 맞으면 내용2, 그밖에 조건이면 else문 실행

}          //만약 조건1이 만족하면 그밖의 else if, else는 건너뛴다.

else if(조건2)

{

 내용2;

}

else

{

내용3;

}



if(조건1)

{

내용1; //조건1,2,3중에서 만족하는 조건만 실행, 그밖의 조건과 실행이라는건 없음.

}       // 만약 조건1이 만족하면 내용1을 실행시키지만, 조건2,3도 if문이라서 다시 조건검사를 한다. 건너뛰지 않는다.

if(조건2)

{

내용2;

}

if(조건3)

{
내용3;

}



*삼항 연산자의 경우*

이는 if~else문을 대체 할수가 있다.


(조건) ? (A) : (B);


해석) 조건이 참이면 A를 반환, 거짓이면 B를 반환하라.



[반복문의 탈출]------------------------------------------


break문은 가장 가까운 중괄호의 반복문 한개를 빠져나오게 하는 명령이다. (단 하나만!)

continue문은 반복문의 조건검사로 즉시 이동하라는 명령이다. 즉 continue 뒤의 문장은 실행하지 않는다.


[선택문]------------------------------------------

switch(입력받은수)

{

 case 1:

         내용1;

         break;

 case 2:

         내용2;

         break;

 case 3:

         내용3;

         break;

 default:

         내용4;

}


입력받은수가 1~4까지라고 할때, 해당하는 수에 맞는 case문을 실행한다.

만약 break; 가 없다면 다음 case문도 실행된다.


If~ else 문과 switch 문은 서로 유사하지만 분기가 많고 복잡하다면 주로 switch문을 이용하는것이 좋다.




[함수의 형태]------------------------------------------



반환형태 함수이름 (입력형태)

{


함수의 몸체;


}


*이름이 같을때, 우선순위

지역변수 > 전역변수



Void 재귀(void)

{


printf("재호출=n");

재귀();

}

// 재귀함수는 함수가 호출되면 복사본을 만들어서 실행되는 구조라고 이해하자. 즉, 계속 호출될때마다 원본은 나두고 계속 복사본을 만들어서 실행하는 것이다. 


재귀 함수의 디자인사례)


#include <stdio.h>


int Factorial(int n)

{

if(n==0)

return 1;

else

return n * Factorial(n-1);

}


int main(void)

{

printf("1! = %d \n", Factorial(1));

printf("1! = %d \n", Factorial(2));

printf("1! = %d \n", Factorial(3));

printf("1! = %d \n", Factorial(4));

return 0;

}

//결과 1! = 1   2! = 2  3! = 6  4! = 24 




원문주소 : http://blog.naver.com/unijun/37834300


1. Hexa파일과 BIN파일의 비교

1) 일반적으로 Hexa파일이 BIN파일보다 용량이 크다.

  - Hexa파일은 ASCII로 되어 있으며 BIN파일은 Binary로 되어 있다.

  - Hexa파일은 Address영역이 있으며 Bin파일은 Address영역이 없다.

  - 하지만 경우에 따라선 BIN파일 용량이 더 커질 수 있다. 특히 시작번지가 0번지에서

    멀어질수록 BIN파일이 더 불리하다.

 

2) Hexa파일은 시작Address를 자유롭게 바꿀수 있으나 BIN파일은 그러지 못한다.

  - BIN파일은 0번지부터 시작Adress까지 '00'이나 'FF'로 채워두어야 한다.

  - 중간에 빈 영역이 있을경우에도 BIN파일은 그 영역을 '00'이나 'FF'로 채워두어야

    한다.

 

3) Hexa파일은 자체적으로 Check Sum을 가지고 있어서 통신오류에 대응할 수 있다.

  - BIN파일은 프로그램에서 통신오류에 대응할 수 있도록 제작해 주어야 한다.

 

4) Hexa파일은 다운로드 프로그램을 대부분 공용으로 쓸 수 있다.

  - BIN파일은 0번지부터 시작Adress까지를 없애려면 강제로 시작 Address를 지정해야

    한다.

 

2. Hexa파일의 기본적인 구조



 

1) 시작표시 : ':'로 나타내며 레코드의 시작을 나타낸다.

2) 데이터길이 : 데이터항목의 크기 n을 나타낸다.

3) 주소옵셋 : 시작주소로부터 얼마나 떨어진 부분부터 쓸 것인지를 나타낸다.

4) 데이터타입

  - 00 : Data Record (16/20/32bit Address형식에 모두 사용)

  - 01 : End of File Record (16/20/32bit Address형식에 모두 사용)

  - 02 : Extended Segmented Address Record (20bit Address형식에 사용)

  - 03 : Start Segment Address Record (20bit Address형식에 사용)

  - 02 : Extended Linear Address Record (32bit Address형식에 사용)

  - 03 : Start Linear Address Record (32bit Address형식에 사용)

5) 테이터

  -  데이터타입이 00일때 : 기록할 데이터

  -  데이터타입이 01일때 : 무의미

  -  데이터타입이 02 또는 04일때 : 시작 Address

  -  데이터타입이 03 또는 05일때 : 실행 Address

6) 체크섬 : 모든 레코드 영역을 모두 더한 후에 2의 보수를 취한값

    byte i, 체크섬 = 0 ;
    for ( i = 0 ; i < 레코드길이 ; i ++ ) 체크섬 += 레코드[i] ;
    체크섬 = ~체크섬 + 1 ;

 

3. Hexa파일의 예 (20bit Address형식)

:020000023000CC
:10000000B8FF9F8EC026813E0A004F4D7402EB0957
:1000100026813E08006F527405EA4900A7FFC70613
:10002000BA00FE9FC706B8000000C7060C80FF1983
:10003000C7060C800000C70608800000C7066C8059
:040000032FFE0000CC
:00000001FF

 

각 문장을 해석해 보자.

 

1) : 02 0000 02 3000 CC

  - 02 : 데이터항목이 2바이트이므로 02이다.

  - 0000 : 이 문장은 시작 Address를 설정하는 문장이므로 옵셋이 필요없다.

  - 02 : 데이터가 20비트 형식의 시작주소를 나타낸다는 의미이다.

  - 3000 : 데이터인데 여기서는 데이타타입이 02이므로 시작주소이다.

    단 20비트형식이므로 실제 주소는 4비트 쉬프트되어 0x030000이 된다.

  - CC : 체크섬이다. 02+00+00+02+30+00 = 34, 34의 2의 보수는 CC이다.

 

2) : 10 0000 00 B8FF9F8EC026813E0A004F4D7402EB09 57

  - 10 : 데이터항목이 16바이트이므로 10이다.

  - 0000 : 실제주소는 시작주소+주소옵셋=0x030000+0x0000=0x030000이다.
  - 00 : 데이터가 저장될 코드라는 것을 나타낸다는 의미이다.

  - 뒤의 16바이트 : 실제로 저장될 코드이다.

  - 57 : 체크섬이다.

 

3) 세번째 줄부터 다섯번째 줄까지는 2)번항목과 동일하며 주소만 16씩 증가한다.

 

4) : 04 0000 03 2FFE0000 CC

  - 04 : 데이터항목이 4바이트이다.

  - 0000 : 이문장은 실행 Address를 설정하는 문장이므로 옵셋이 필요없다.

  - 03 : 데이터가 20비트 형식의 실행주소를 나타낸다는 의미이다.

  - 2FFE0000 : 실행주소이며 20비트형식이므로 실제 주소는 4비트 쉬프트되어 0x02FFE0

    이다. 뒤의 0000은 IP(Interupt Priority) 레지스터의 값이다.

  - CC : 체크섬이다.

 

5) : 00 0000 01 FF
  - 00 : 데이터항목이 없다.

  - 0000 : 이문장은 종료를 나타내는 문장이므로 옵셋이 필요없다.

  - 01 : 이 문장이 종료를 나타낸다는 의미이다.

  - FF : 체크섬이다.

'Engineering > C Language' 카테고리의 다른 글

[C언어] 구조체사용하기  (0) 2014.01.16
[C언어] 포인터 사용하기-2  (0) 2014.01.15
[C언어] 포인터 사용하기-1  (0) 2013.11.30
[C언어] 기본적인 개념들  (0) 2013.11.28
[C언어] 평균값필터, moving average  (2) 2012.11.01

+ Recent posts