사용자 삽입 이미지
네이버의 오픈 API를 사용해서 만든 네이버의 실시간 인기 검색어를 가져 오는 위젯입니다. 
 
사용하실려면 네이버로 부터 발급 받은 오픈 API 키가 필요합니다. 제 것으로 하면 편한데 쿼리를 하루에 5,000건 밖에 할 수가 없어, 사용자 별로 입력해서 사용하도록 했습니다.

먼저 네이버 OpenAPI사이트에서 이용키를 발급 받은 후 사용하셔야 합니다. 이용키는 아래와 같은 형태 입니다. 아래의 키는 유효하지 않습니다.

a49c1684d414922aac522ea812e02a0b

처음 실행하면 키를 입력하는 창이 나오는데, 이 곳에서 발급 받은 키를 복사해서 넣으시면 됩니다. 5초마다 번갈아 가며, 인물/영화/도서/게임/TV 드라마/TV 오락프로/공연의 인기 검색어를 보여 줍니다.

급조해서 만든거라 허술한데, 시간이나면 기능이나 디자인을 신경써서 다시 한번 작업해 봐야 겠습니다.  혹시 오류가 있으면  댓글로 알려 주시면, 수정해서 올리겠습니다.
AND

10. struct, union, enum, typedef

struct, union, enum, typedef 등은 새로운 자료형을 만들고, 문자화 된 상수 리스트들을 만들고, 변수 타입명을 대치하는 등, C에서 자료를 다루고 접근하는데 있어서 많은 편리한 기능들을 제공합니다. 이번 장에서는 이에 대해서 간단하게 알아 보겠습니다.

10.1 struct (구조체)
C에는 구조체(structure)라고 불리우는 사용자가 정의할 수 있는 자료형이 있습니다. 구조체는 하나 이상의 변수를 포함할 수 있는 집합체라고 보시면 됩니다.

1) 구조체의 필요성

어떤 프로그램이 직원들의 사원번호, 이름, 나이, 성별들을 관리한다고 가정합니다. 아래와 같이 직원 개인의 정보에 관한 변수들이 있습니다.

int employee_id;                    /* 사원번호 */
char employee_name[12];    /* 이름 */
int employee_age;                  /* 나이 */
int employee_sex;                  /* 성별 */

직원에 관련된 부분을 처리할 때, 대부분 이 변수들은 같이 사용될 것 입니다. 아래와 같이 직원정보를 저장하는 함수는 관련된 모든 변수들을 인자로 넘겨야 합니다.

int set_employee(int id, char* name, int age, int set);

또한 정보를 얻을 때도 함수는 반환값이 하나밖에 없으니, 아래와 같이 함수를 만들어 주어야 합니다.

int get_emploeyee_age(int id);
char* get_emploeyee_name(int id);
int get_emploeyee_sex(int id);

같이 사용되는 변수들이기 때문에, 하나로 그룹화를 해 두면 사용이 편할 것 같습니다.


2) 구조체의 선언

이제 구조체에 대해서 알아 보겠습니다. 구조체는 아래와 같이 선언합니다.

struct [구조체 명]
{
    [변수]
    [변수]
       .
       .
       .
};

[구조체명] 역시 변수명이나 함수명 사용 규칙과 동일하며, [변수]는 C에서 사용 가능한 모든 변수들이 올 수 있습니다.

1) 장에서 사용한 employee_ 로 시작하는 직원 변수들을 하나의 직원 구조체로 묶어 보겠습니다. 아래와 같이 구조체를 선언합니다.

struct employee
{
    int id;   
    char name[12];
    int age;
    int sex;
};

구조체의 멤버변수들은 employee라는 구조체 안에 있으므로, 직원에 관련된 변수임을 알리는 employee_를 생략하였습니다. 이제 위의 employee 정보를 저장하고 불러오는 함수들을 구조체를 사용하여 아래와 같이 단일 인자나 반환값으로 사용할 수 있습니다.

int set_employee(struct employee emp);
struct employee get_employee(int id);

구조체는 이와같이 변수의 사용을 명확히 하고, 함수로 전달이나 반환을 용이하게 해줍니다.


3) 구조체의 사용

이제 구조체를 사용하는 간단한 소스를 보겠습니다.

#include <stdio.h>
#include <string.h>

struct employee
{
    int id;
    char name[12];
    int age;
    int sex;
};

void print_employee(struct employee emp);

int main(int argc, char* argv[])
{
    struct employee emp1;
    struct employee emp2;

    emp1.id = 1;
    strcpy(emp1.name, "def");
    emp1.age = 29;
    emp1.sex = 1;

    emp2.id = 2;
    strcpy(emp2.name, "abc");
    emp2.age = 34;
    emp2.sex = 2;

    print_employee(emp1);
    print_employee(emp2);
}

void print_employee(struct employee emp)
{
    char c;
    
    if(emp.sex == 1)
        c = 'M';
    else    
        c = 'W';
        
    printf("%d: name %s, age %d, sex %c\n", emp.id, emp.name, emp.age, emp.sex);
}

struct employee emp1;
구조체는 다음과 같이 사용합니다.

struct [구조체 이름] [변수명];

emp1.id = 1;
구조체의 사용은 아래와 같이 .을 구분자로 하여 사용합니다. (-> 도 있지만, 포인터 설명시 하겠습니다.)

[구조체 명].[구조체 멤버 변수 명]

print_employee(struct employee emp);
위에서 예를 든 것과 같이 emp하나만 인자로 넘겨주면, 직원의 id, age, sex를 프린트 할 수 있습니다.

여기서는 포인터의 사용으로 구조체의 이해를 어렵게 하지 않기위해 포인터를 사용하지 않았지만, 일반적으로 구조체를 함수의 인자로 사용할 때는 포인터를 사용합니다. 포인터로 주소만 넘기면 스텍 메모리를 4바이트를 사용하지만, 구조체를 사용하면 구조체 크기만큼 스텍을 사용하기 때문입니다.

구조체의 크기가 적을 경우에는 문제가 되지 않지만, 클 경우에는 문제의 소지가 있고 값을 변경할 경우에 문제가 있을 수 있습니다. (이는 다시 설명하겠습니다.)

그러므로 구조체를 함수의 인자로 사용할 때에는 포인터를 사용하는 것이 좋습니다. 아래는 포인터로 변경한 소스입니다.

#include <stdio.h>
#include <string.h>

struct employee
{
    int id;
    char name[12];
    int age;
    int sex;
};

void print_employee(struct employee* emp);

int main(int argc, char* argv[])
{
    struct employee emp1;
    struct employee emp2;

    emp1.id = 1;
    strcpy(emp1.name, "abc");
    emp1.age = 29;
    emp1.sex = 1;

    emp2.id = 2;
    strcpy(emp2.name, "def");
    emp2.age = 34;
    emp2.sex = 2;

    print_employee(&emp1);
    print_employee(&emp2);
}

void print_employee(struct employee* emp)
{  
    char c;

    if(emp->sex == 1)
        c = 'M';
    else   
        c = 'W';
       
    printf("%d: name %s, age %d, sex %c\n", emp->id, emp->name, emp->age, emp->sex);
}

void print_employee(struct employee* emp);
함수 선언 시 emp 앞에 *)를 넣어 포인터로 넘기도록 변경합니다.

print_employee(&emp1);
print_employee(&emp2);
구조체 대신에 구조체의 포인터를 넘기도록, 주소연산자(&)를 사용합니다.
 
emp->id, emp->name, emp->age, emp->sex
구조체가 포인터일 경우에는 멤버변수를 가르킬 때, "." 대신, "->"를 사용하여야 합니다.


10.2 union (공용체)
공용체는 struct를 union으로 선언한다는 것 이외에는 구조체와 똑 같이 선언 합니다.

union [공용체 명]
{
    [변수]
    [변수]
       .
       .
       .
};

한 가지 다른 점은 멤버 변수들간에 메모리 영역을 공유한다는 것입니다. 그러므로 공용체의 크기는 멤버 변수 중 가장 큰 변수의 크기를 갖습니다. 아래의 소스로 같은 메모리 영역을 공유 한다는 의미에 대해서 알아 보겠습니다.

#include <stdio.h>

int main()
{
    union my_int 
    {
        unsigned int  i;
        unsigned char c[4];
    };

    int i; 
    union my_int mi;

    mi.i = 100;

    for(i = 0; i < 4; i++)
    {
        printf("%d\n", mi.c[i]);
    }

    printf("%x, %x, %x\n", &mi, mi.c, &(mi.i));

    return 0;
}

1) 메모리 공유의 의미

union my_int 
{
    unsigned int  i;    
    unsigned char c[4];
};
위와 같은 선언은 메모리에서 다음과 같이 자리 잡습니다.
사용자 삽입 이미지
mi는 위와같이 메모리 어디인가에 4바이트의 영역을 차지 하고 있습니다. i와 c의 메모리 시작 주소는 위와 같이 동일합니다.

이는 하단의 다음 코드에서
printf("%x, %x, %x\n", &mi, mi.c, &(mi.i));
세 변수의 메모리 주소를 출력해 보면,
> bffffc7c, bffffc7c, bffffc7c
위와 같이 동일하게 출력되는 것을 확인할 수 있습니다.

위에 mi가 공용체가 아닌 구조체로 선언되었다면, 아래와 같이 메모리에 위치할 것입니다.
사용자 삽입 이미지
보시는 바와 같이 mi.i와 mi.c가 각각의 메모리 블럭에 자리 잡고 있습니다.
 

2) 빅 엔디언, 리틀 엔디언

mi.i = 100;
for(i = 0; i < 4; i++)
{
    printf("%d\n", mi.c[i]);
}

mi.i에 100을 대입하면, 같이 메모리를 사용하는 mi.c 배열의 값에도 변경이 있습니다. 위는 제 맥에서 아래와 같이 출력 됩니다.
> 0
> 0
> 0
> 100
보시는 바와 같이 c[3]에 100이 들어 가 있습니다. 제 테스트 환경은 모토롤라 CPU를 사용하는 PPC 아이맥이기 때문에 c[3]에 먼저 값이 들어 가 있습니다. 인텔 CPU를 사용하는 환경에서는 c[0]에 100이 들어 갑니다.

이 차이는 cpu에 따라 바이트를 저장하는 순서가 다르기 때문입니다. 이 순서의 차이는 빅 엔디안과 리틀 엔디안으로 불리어지며, 빅 엔디안은 큰 쪽이 먼저 저장되고 리틀 엔디안은 반대로 작은 쪽이 먼저 저장됩니다.

4바이트를 가진 mi.i는 16진수로 표현하면, 0x00000064입니다. 0x는 16진수라는 것을 의미하며 2자리 당 1바이트 입니다. 빅 엔디언은 이 값이 [00][00][00][64]와 같이 그대로 메모리에 들어 가기 때문에 mi.c[3]에서 100(16진수로는 0x64)이 출력됩니다.

하지만 리틀 엔디언은 값이 위와 반대로 [64][00][00][00]와 같이 저장되기 때문에 mi.c[0]에서 100이 출력 됩니다.

전통적으로 모토롤라 계열의 CPU들은 빅 엔디언, 인텔 계열의 CPU들은 리틀 엔디언을 사용합니다. 넣고 빼는 방법은 동일하기 때문에 값에는 변화가 없습니다. 그렇기 때문에 빅 엔디언/리틀 엔디언에 관해서 신경 쓸 필요는 거의 없습니다.

하지만 위와 같이 union의 사용과 같이 바이트 열의 제어 시에는 유의해야 합니다.


10.3 enum (열거형 상수)

enum은 열거형 상수입니다. 비슷한 속성을 가진 값들이나 내용을 일련된 정수(int)로된 상수로 나타내어 줍니다. enum은 다음과 같이 선언합니다.

enum [열거형 상수명] { [상수명] = [값], ... } [열거형 변수];

[열거형 상수명]은 구조체나 유니온 처럼 변수 선언시 필요한 이름이며 생략할 수 있습니다.
[상수명]은 정수대신 사용할 상수의 이름 입니다. 이 값은 = [값]으로 초기화 할 수 있으며, 값을 생략하면 첫번째 상수부터 0, 1, 2... 차례대로 설정됩니다.
[열거형 변수]
는 enum 값을 사용할 변수 이며, 선언시에는 생략 가능합니다. 열거형 상수 값을 가지는 변수들은 정수형입니다.

아래의 소스를 보시면 쉽게 열거형 변수에 대해서 이해 할 수 있습니다.

#include <stdio.h>

int main()
{
    enum Usergrade { MASTER=0, LEVEL1, LEVEL2, LEVEL3 } u3;
   
    enum Usergrade u1 = MASTER;
    int u2 = LEVEL1;

    u3 = LEVEL2;

    printf("%d, %d, %d\n", u1, u2, u3);
    printf("%d, %d, %d, %d\n", MASTER, LEVEL1, LEVEL2, LEVEL3);

    return 0;
}

enum Usergrade { MASTER=0, LEVEL1, LEVEL2, LEVEL3 } u3;
Usergrade라는 enum 상수들을 선언 합니다. MASTER=0이며 기본 시작값이 0이기 때문에, =0을 생략하여도 의미는 같습니다. MASTER 뒤로 LEVEL1, LEVEL2, LEVEL3은 차례대로 1, 2, 3의 값이 들어 갑니다. 만약 MASTER=10으로 시작하면 그뒤로 차례대로 11, 12, 13의 값이 들어 갑니다.

마지막에 이 상수를 사용하기 위해 u3을 선언되어 있습니다. 변수는 꼭 enum 선언시 선언될 필요는 없습니다.

enum Usergrade u1 = MASTER;
int u2 = LEVEL1;
u3 = LEVEL2;
enum 변수를 선언(구조체와 선언이 유사합니다.)하고, 정수형에 대입하고, 위에서 enum 선언시 , 선언된 변수에 enum 상수값들을 넣어 봅니다.

enum을 사용하는 이유는 소스 작성시 의미를 명확하게 하고, 상수들을 일괄되고 안정되게 사용하기 위해서 입니다.

예를 들어 아래와 같은 소스에서 전자 보다는 후자가 더욱 명확하고 이해하기 쉽습니다.
if(user_grade == 0)
    printf("You are Master");
---------------------------------------------------------------
if(user_grade == MASTER)
   printf("You are Master");


10.4 typedef

typdef는 변수형을 사용자가 지정한 이름으로 사용할 수 있게 해줍니다. typedef는 적절하게 사용하여야 하며, 의미에 맞게 이름을 지어 주어야 합니다. 그렇지 않고 지나치게 많이 변수형들을 대치하거나, 작성자만 알수 있는 이름을 사용하면 제 3자가 소스를 볼 경우에 이해를 어렵게 할 수 있습니다.

1) typedef 선언

typedef  [기존 변수 형] [새로운 변수 형];

[기존 변수 형]은 int, struct [구조체명], enum [enum 명] 등과 같이 C 변수형 또는 사용자가 지정한 변수 형이 올 수 있습니다.

[새로운 변수형]은 위의 기존 변수형을 대신해서 사용할 사용자 정의 변수타입 이름입니다.

예를 들면 unsigned int는
typedef unsigned int uint;
위와 같이 선언해 놓으면 uint a;와 같이 편한게 unsigned int형의 변수를 선언할 수 있습니다.

2) 구조체와의 사용

typdef는 구조체에서 특히 편리하게 사용할 수 있습니다. 위에서 살펴본 employee 구조체를 다시 한번 보겠습니다.

struct employee
{
    int id;
    char name[12];
    int age;
    int sex;
};

typedef struct employee emp;

struct employee e1;
emp e2;

위에 e1과 e2는 똑같이 employee 구조체 변수를 선언하고 있습니다. e2는 typedef된 구조체를 사용하므로 e1보다 간단하게 선언할 수 있습니다. 구조체에서 typedef는 아래와 같이 선언할 때 같이 사용하면 더욱 편리합니다.

typedef struct
{
    int id;
    char name[12];
    int age;
    int sex;
} employee;

employee e1;

'프로그래밍 강좌 > C 언어 기초' 카테고리의 다른 글

11. define과 디버깅  (1) 2007.07.08
9. 배열 (array)  (0) 2007.06.17
8. 포인터 (pointer)  (4) 2007.06.16
7. C 함수 (function)  (4) 2007.06.15
6. 제어문  (0) 2007.06.14
AND

사용자 삽입 이미지
저에게는 초등학교 3학년인 아들이 하나 있습니다. 어려서부터 제가 컴퓨터에서 작업하던 모습을 보며 자란 이 녀석은 커가면서 제가 하는 작업을 따라 하려고 노력하였습니다.

어려운 프로그래밍을 배우려는 이유는 단 하나입니다. 그토록 좋아하는 게임을 직접 만들 수 있다는 것입니다.


어린 아들에게 프로그래밍을 권유하고 싶은 생각은 없지만, 컴퓨터로 게임만 하는 것보다 프로그래밍 공부를 해보겠다는데 딱히 말릴 이유도 없었습니다.

그래서 관련된 책도 사주었지만, 영어가 기반인 프로그래밍 언어와 VS, Xcode 같은 복잡한 툴을 사용하기에는 아직 어렸습니다.

관심이 없어진 줄 알았는데, 어느날 이 녀석이 티스토리에서 블로그를 만들어 아래와 같은 플래쉬들을 올리고 있다는 것을 알았습니다.
사용자 삽입 이미지 사용자 삽입 이미지 사용자 삽입 이미지

다른 건 어려워서 못하고, 플래쉬는 조금씩 적응을 해나가고 있었나 봅니다. 플래쉬의 액션 스크립트도 약간 쓸 줄 알게 되고, 이제 배우기 쉬운 프로그래밍 언어를 조금씩 가르쳐 볼까하는 생각이 들었습니다.

그래서 구글에서 아이들을 위한 쉽고 재미있는 개발 환경을 찾다가, 우연히 squeakland라는 곳을 방문하게 되었습니다. squeak을 아이들을 위한 교육적인 목적으로 활용는 것에 관해 나와 있는 사이트였습니다. 아~ 바로 이거다 라는 생각이 들었습니다.

사용자 삽입 이미지
그러다가 거기서 본 한장의 사진에서 어디선가 본 듯한 얼굴을 보았습니다.

바로 현대적 PC를 제안하고, OOP의 개념을 확립하고, 스몰토크를 만든  엘런 케이...

(이 사진이 있는 홈페이지에도 엘런 케이라고 언급이 없으니 확실하지는 않지만 콧수염이랑 윤곽이랑 비슷한 것 같습니다.)
사진출처:squeakland

프로그래밍이나 컴퓨터 관련 책에 전설처럼 등장 하는 그가, 이 세상 사람이 아닌 줄 알았습니다. 하지만 그는 지금도 활발하게 활동하고 있었습니다.

사이트를 둘러 보니 squeak은 엘런 케이가 애플에서 그의 오랜 동료들과 시작한 프로젝트였습니다. 엘런 케이는 아이들을 위해 현대 PC 환경에 많은 영향을 준 그 유명한 다이나북을 제안하고, 그 뒤에도 어린이의 컴퓨터 사용에 관해 여러 연구를 진행하였습니다. 그런데 아직까지도 이 분야에서 계속 활동을 하고 있었던 것이었습니다.. 정말 아이사랑이 남다른 분 같습니다.
 
아무튼 저는 squeak을 다운 받아 실행시켜 보았습니다. 찾아보니 한국스퀵 사이트가 있길래, 이 곳에서 한국어 버젼을 다운 받았습니다. (세상엔 참 고마운 분들이 많습니다.)

사용자 삽입 이미지
위는 squeak 맥버젼을 실행시켜 본 모습입니다. 개인마다 차이가 있겠지만 개인적으로는 UI가 화려하다거나, 이쁘다는 느낌은 받지 못했습니다.

그 뒤 squeakland에 있는 drive a car 예제를 따라 해 보았습니다. 차와 핸들을 그리는 시간을 제외하고 자동차를 핸들로 조종하며 움직이게 만드는데 걸린 시간은 단 1분도 안되었습니다. 시키는대로 따라해 보고, 결과물을 본 저는 경악할 수 밖에 없었습니다.

뭐 이런게 다 있지...

아래는 제가 그린 차가 움직이고, 소리를 내고, 원하는 방향으로 움직이게 만드는 스크립트입니다. 모든 작업은 마우스로 하였기 때문에, 스크립트라고 하기도 그렇습니다. 가장 감명 받은 부분은 [붕붕카 돌기][핸들의  머리방향]을 결합 시킬 때였습니다.
사용자 삽입 이미지

제가 마우스로 작업을 하는 동안 squeak은 아래와 같이 스몰토크 코드를 생성해 놓았습니다. 스몰토크의 아래와 같은 문법때문에 위의 액션 블록이 쉽게 스몰토크 코드로 바뀔 수 있는 것 같습니다.
사용자 삽입 이미지

퇴근 후, 집에 가서 아들에게 사용하게 해 보았습니다. 결과는 역시 대성공. 아들 녀석은 자기가 그린 오브젝트들을 마음대로 다룰 수 있는 걸 보고 무척이나 좋아하였습니다. 나 같은 아빠들을 위해 조만간 이 블로그에도 squeak의 사용법에 대해서 간단하게 소개해 볼려고 합니다.

사용자 삽입 이미지
squeak은 아이들 놀이/교육의 용도로만 제작된 것은 아닙니다. SmallTalk-80과 개발환경, 기본 라이브러리를 가지고 있어, 전문적인 개발자도 원하는 용도로 사용할 수 있습니다.

좌측은 Squeak에서 간단한 SmallTalk 소스를 작성하고 실행해 본 이미지입니다. 위의 모습과 달리 개발툴의 냄새가 납니다.

실제 한국 스퀵 사이트는 스퀵으로 만든 웹서버와 웹 어플리케이션으로 운영되고 있다고 합니다.


사용자 삽입 이미지
또한 Squeak VM이 있으면 웹브라우져 상에서도 java 처럼 실행이 가능합니다. 아래는 파이어폭스에서 squeak으로 만든 프리쉘을 실행시켜 본 모습입니다.

이 게임 외에도 테트리스 등의 게임을 해당 사이트에서 확인하고 실행해 볼 수 있습니다.
 



제게 있어 엘런 케이는 오래전 책에서나 존재하던 사람이었습니다. 이제 세상이 좋아져 웹상에서  최근에 올린 그의 글들을 볼 수 있고, 이런 프로그램 까지 쓸 수 있게 되다니... 엘런 케이 아저씨 존경합니다.

"The best way to predict the future is to invent it."
(미래를 예측하는 가장 좋은 방법은 미래를 만드는 것이다.)
- Alan Kay -
AND