출처 :  네이버 지식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타입의 함수 포인터 선언.



+ Recent posts