2009년 4월 22일 수요일

씹어먹는 C 언어 - <3. 변수가 뭐지? >

이번 강좌에서 배우게 될 것은
  • 변수란 무엇인가?
  • 정수형, 실수형 변수 
  • 16 진법, 메모리 주소
  •  변수 이름 짓기


  안녕하세요? 여러분. 잘 지내셨나요. 지난 강의는 잘 이해가 되셨나요? 아마, 이해가 잘 안 되었을 것 입니다. 왜냐하면 우리는 C 언어의 기본적인 이해도 없이 프로그램을 무작정 분석했기 때문이죠. 따라서, 지난번의 강의가 C 언어의 맛보기 였다면, 이제 본격적으로 C 언어의 세계로 풍덩 빠져 보도록 합시다.


  변수란 무엇인가?

  컴퓨터는 많은 내용을 '기억' 합니다. 정확히 말하면, 컴퓨터의 '메모리' 라는 부분에 전기적인 신호를 써 놓는 것이죠. 컴퓨터가 무엇을 기억해야 되냐고 생각할 수 있지만, 우리가 많이 하는 게임인 스타크레프트만 보아도 일단, 각 유닛의 HP 와 마나, 그리고 실시간으로 바뀌는 미네랄과 가스, 뿐만 아니라 유닛의 위치, 유닛의 데미지 등 모든 것을 기억해야지 우리가 게임을 제대로 즐길 수 있게 되죠. 만약 컴퓨터가 미네랄의 양을 제대로 기억 못한다면 미네랄이 갑자기 100 에서 0 이 되거나 10 에서 9999 로 바뀔 수 있기 때문이죠.

  그렇다면 컴퓨터는 이러한 데이터들을 어떻게 기억할까요? 바로 컴퓨터의 메모리, 즉 RAM 이라는 특별한 기억공간에 이를 기록합니다. 보통 우리는 흔히 RAM 을 설명할 때 아래 처럼 표시합니다.
  마치, 감옥에 온 것처럼 각 '방' 에 데이터들이 저장될 수 있습니다. 이 때, 컴퓨터는 각 방에 이름을 붙이는데 단순하게 숫자로 이름을 붙입니다. 0 번, 1 번, 2 번 ,...  이 때, 우리 대부분이 사용하는 32 비트 CPU 에서는 최대 2³² 개(4GB). 즉, 42 억개 달하는 방을 가질 수 있는데 이러한 방들을 모두 숫자로 구분하게 된다. 보통, 컴퓨터의 메모리 번지는 16 진수로 나타내는데, 여기서 간단하게 16 진수에 대해 알아 보도록 합시다.


  수를 표현하는 방법

  ○○○○○○○○○○○○○○

  위에 흰 공이 있습니다. 위의 흰 공은 몇 개 인가요? 우리는 아마 지금 머리속에 '14개 아니야' 라는 생각이 들었을 것입니다. 물론, 경우에 따라 잘못세면 '13개... 아 , 14개군' 이라고 생각할 수 도 있겠지요. 그런데 컴퓨터는 이를 14 로 보지 않습니다. 컴퓨터는 이를 1110 으로 생각합니다. '어? 컴퓨터는 특별히 많이 세나..'

  아닙니다. 단지, 컴퓨터는 수를 표현하는 방법이 다를 뿐입니다. 우리가 14 라는 숫자를 좀 더 고상하게 표현해 보면 아래와 같습니다.
 

반면에 컴퓨터가 14 를 1110 이라 한 것은


이기 때문에 1110 이라 한 것입니다. (참고로, 2³ = 2 × 2 × 2를 편리하게 나타낸 것입니다.)

  우리와 같이 10을 기준으로 한 것을 10 진법이라 하고,위 컴퓨터와 같이 2를 기준으로한 것을 2진법이라 합니다. 그렇다면 16 진법은 16을 기준으로 표현한 것이죠.

  또한 우리가 한 가지 알 수 있는 사실은 임의의 진법 N 진법은, N 개의 숫자를 필요로 한 다는 것입니다. 예를들어, 이진법에서는 0 과 1 밖에 숫자가 필요하지 않았으나, 10 진법의 경우 0,1,2,3,4,5,6,7,8,9 와 같이 10 개의 숫자가 필요로 한 것입니다.

그렇다면 16 진법은 0,1,2,3,4,5,6,7,8,9 외에도 6 개의 숫자가 더 필요로 하는데, 수학자들은 이를 A,B,C,D,E,F 로 표현하였습니다. 따라서, 14는 16 진법으로 나타내면 E 가 되는 군요.


  그런데, 컴퓨터가 왜, 계산하기 힘든 16 진법을 사용하느냐면 2 진법에서 16 진법으로 바꾸기 쉽기 때문이죠. 아래의 예제를 본다면, 237 은 아래와 같이 이진법으로 바꿀 수 있습니다.
 

 이 때, 이진법을 16 진법으로 바꾸려면 뒤에서 부터 4 개씩 차근차근 바꾸면 됩니다. 위의 경우, 뒤의 4 개인 1101 을 16 진법으로 바꾸면 (13 이므로 D ) D 이고, 그 다음 4개인 1110 을 16 진법으로 바꾸면 (14 이므로 E) E 입니다. 결과적으로 237 = ED 가 되죠. 마찬가지로 111100111 을 한 번 바꿔보자면, 0111 은 7 이므로 그냥 7, 그 다음 1110 은 E, 마지막 1 은 그냥 1. 따라서 이 수는 1E7 이 됩니다. 간단하죠?

  이제 다시 본론으로 돌아와서 봅시다. 아까 메모리가 최대 2³² 까지 가능하다고 했습니다. 이 때, 번지를 0 번부터 메기므로 2³² 번째 메모리의 주소는 11111111111111111111111111111111b 이겠죠. 이 때, 끝에 b 는 binary 의 약자로, 2 진법으로 썼다는 것을 표시해 주는 것 입니다. 위 2 진법으로 표현된 숫자는 1 이 32 개 연속되어 있는 것이므로 16 진법으로 나타내면 FFFFFFFF 가 되겠죠. 따라서, 번지는 0x00000000 부터 0xFFFFFFFF 까지 가능합니다. 이 때, 앞에 0x 는 16 진법이라는 것을 표시해 주는 것 입니다.

  만약, 우리가 0x12345678 부터 0x1234567B 부분에 내가 캔 미네랄의 양에 관한 정보를 저장했다고 합시다. (이 한칸에는 1 바이트, 즉 00000000b 부터 11111111b 만큼의 정보를 저장할 수 있습니다. ) 만약 우리가 건물을 지을 때, 내가 가진 미네랄의 양이 건물이 필요로 하는 미네랄의 양과 비교하기 위해, 내가 캔 미네랄의 양에 관한 정보가 필요합니다. 그런데, 이렇게 미네랄에 관한 정보가 필요로 할 때 마다, 이 길고 알아보기 힘든 복잡한 주소를 일일이 써서 알아야 하나요? 그렇게 된다면 힘들어서 프로그래밍 어떻게 하나요.

  하지만 다행이도 C 언어에는 '변수' 라는 것이 있어서, 이 모든 작업을 쉽게 할 수 있는 것이죠. 예를들어, 내가 캔 미네랄의 양을 mineral 이라는 변수에 저장했다고 합시다. 그렇다면 컴퓨터는 '알아서' 메모리의 어딘가에 mineral 의 방을 주고 그 내용을 저장합니다. 예를들어서, 컴퓨터가 이 mineral 이라는 변수에게 4 칸의 자리를 할당해 주었다고 합시다. 이는 아래 그림처럼 메모리 상에 표시됩니다.
  이 때, 우리가 미네랄을 더 캐서 8 을 추가해야한다고 봅시다. 만약 이전에 8 을 추가한다면 0x12345678 부터 0x1234567B 까지의 모든 내용을 불러와서 8 을 더한 후, 다시 집어넣는 작업을 일일이 손으로 써 주어야 되었을 것입니다. 하지만, 이제는 단순히 mineral = mineral + 8 과 같이 써 주기만 한다면 mineral 에 8 이 더해지는 것이죠. (만약 mineral = mineral + 8 이라는 식이 이해가 안되도 그냥 넘어가세요. 이 처럼 간단해 진다는 것을 말해주고 싶었을 뿐입니다)

  자, 이제. 변수가 무엇인지 알겠죠?

변수 선언하기

/* 변수 알아보기 */
#include <stdio.h>
int main()
{
    int a;
    a = 10;
    printf("a 의 값은 : %d \n", a);
    return 0;
}

  프로젝트를 만들어 위의 내용을 적은 후, 컴파일 해봅시다. 까먹었다면 1 강을 참조하세요. 만약 성공적으로 하였다면 아래와 같은 화면을 볼 수 있겠죠.


  일단, 이번에도 역시 생소한 것들이 나왔기 때문에 한 문장씩 차근차근 살펴 봅시다.

int a;

  음, 이게 무엇일까요? 이전에 int main() 에서 보았던 int 가 다시 나타났군요. 사실 이 문장에 뜻은 a 라는 변수를 우리가 쓰겠다고 컴파일러에게 알리는 것입니다. 만약 이러한 문장이 없다면 우리가 x 가 뭐고 y 가 뭔지 알려주지도 않은 채, 친구에게 x + y 가 얼마냐? 하고 물어보는 것과 똑같은 격이 되는 것이지요.

  이 때, a 앞에 붙은 int 라는 것은 'int' 형의 데이터를 보관한다는 뜻으로, int 형의 변수는 -
2147483648 부터 2147483647 까지의 숫자를 보관 할 수 있게 됩니다. 따라서, 만약 중간의 문장을

a = 10000000000000;

와 같이 한다면 아마 a 의 값을 출력하였을 때, 이상한 결과가 나오게 되죠. 왜냐하면 보관할 수 있는 범위를 초과하는 숫자를 보관했기 때문이죠.

  그럼 이제, 걱정이 생깁니다. a 에 고작 10 밖에 안 넣을 거 면서, 굳이
2147483647 까지 표현할 수 있는 int 형의 변수를 왜 사용했냐고 물어 볼 수 있을 것 입니다. 컴퓨터 자원의 낭비 아닌가? 라는 생각도 들지요. 물론, int 형 보다 작은 범위의 숫자 데이터 만을 가지는 형식이 있기는 하지만(char), 32 비트 CPU 는 int 형의 변수를 가장 빨리 처리합니다.

  또한, 2147483647 보다 큰 수를 사용하려면 어떻게 해야되냐는 궁금증도 생기지요. 물론 이 보다도 훨씬 큰 숫자를 처리하는 데이터 형식이 있습니다. 아래의 표를 참조하세요.


  세번째 열인 Range 를 보시면, unsigned 와 signed 라고 나뉜 것이 있는데, 보통 int 라 하면 signed int 를 뜻합니다. 이는 음수/양수 모두 표시할 수 있는 대신에 양수의 표현할 수 있는 범위가 줄어듭니다. 반면에 unsigned int 는 양수만을 표현할 수 있는 대신에, 양수의 표현할 수 있는 범위가 늘어나죠. 또한 마지막에 보면 float ,double, long double 이 있는데 이들은 '실수형' 자료형으로 소수(0.1, 1.4123 등) 을 표현 할 수 있습니다. 뿐만 아니라 double 의 경우,
eq=10^{308}
까지표현 할 수 있습니다. (물론 정확도가 떨어집니다. 앞 15 자리 까지가 정확)

a = 10;

  음, 이 문장은 쉽게 알 수 있군요. 변수 a 에 10 이라는 데이터를 집어넣는 다는 것입니다. 즉, 나중에 a 의 값을 출력시 10 이 나오겠군요. 나중에 이 문장은 C 언어의 산술 연산에 대해 공부해 볼 때, 자세히 알아 봅시다.

printf("a 의 값은 : %d \n", a);

  마지막으로, 어제도 보았던 printf 문이군요. 그런데, 약간 다른 것이 있습니다. %d 가 말이죠. 사실, %d 는 컴퓨터에서 출력되지 않았습니다. 그 대신, %d 가 출력될 자리에 무언가 다른 것이 출력되었는데, 그 것이 바로 a 의 값이죠. 즉, %d 는 a 의 값을 '10 진수' 로 출력하라 라는 뜻이지요.

또 다른 예제를 봅시다.

/* 변수 알아보기 2*/
#include <stdio.h>
int main()
{
    int a;
    a = 127;
    printf("a 의 값은 %d 진수로 %o 입니다. \n",8, a);
    printf("a 의 값은 %d 진수로 %d 입니다. \n",10, a);
    printf("a 의 값은 %d 진수로 %x 입니다. \n",16, a);
    return 0;
}

  프로그램을 제대로 짰다면 아래와 같은 결과를 볼 수 있을 것입니다.


 일단, 위 프로그램에서 생기는 궁금증은 2 가지 있습니다. % 달린게 2 개나 있는데, 이를 어떻게 해야되냐와, %d 말고도 %o 와 %x 는 무엇인가 입니다.
  먼저, printf 의 작동 원리에 대해 봅시다.

  printf 출력시에, 큰 따옴표로 묶인 부분 뒤에 나열된 인자들 (8, a) 가 순서대로 큰 따옴표 안의 % 부분으로 들어감을 알 수 있습니다. 따라서 , 예를들면 printf("%d %d %d %d", a,b,c,d); 와 같은 문장은 a , b, c, d 의 값이 순서대로 출력되겠죠.

  이제, %o  와 %x 는 무엇인가요? 이는 인자의 값(a) 를 출력하는 형식 입니다. 즉, %o 는 a 의 값을 8 진수로 출력하라라는 뜻이고, %x 는 a 의 값을 16진수로 출력하라는 뜻 이죠.

실수형 변수

  앞서 말했듯이, 실수형에는 float 와 double 이 있습니다. double 의 경우 int 형에 비해 덩치가 2 배나 크지만 그 만큼 엄청난 크기의 숫자를 다룰 수 있습니다. 그 대신, 처음 15 개의 숫자들만 정확하고 나머지는 10 의 지수 형태로 표현됩니다. 또한 float 과 double 의 장점은 소수를 표시할 수 있다는 점인데, 정수형 변수에서 소수를 넣는다면 (예를들어 int a; a = 1.234; ), 소수 부분은 다 잘린 채, 나중에 a 의 값을 표시해 보면 1 이 나올 것 입니다.

/* 변수 알아보기 3*/
#include <stdio.h>
int main()
{
    float a = 3.141592f;
    double b = 3.141592;
    printf("a : %f \n", a);
    printf("b : %f \n", b);
    return 0;
}

  실행해 본다면 아래와 같이 나오게 됩니다.
 

   일단, 위 코드를 보면서 궁금한 점이 생기지 않았나요?

    float a = 3.141592f;
    double b = 3.141592;


  왜, float 형 변수 a 를 선언할 때 에는 숫자 뒤에 f 를 붙였는데 double 형 에서는 f 를 안 붙였는 지요. 왜냐하면, 그냥 f 를 안 붙이고 float a = 3.141592 로 하면 이를 double 형으로 인식하여 문제가 생길 수 있기 때문이죠. 따라서, float 형이라는 것을 확실히 표시해 주기 위해 f 를 끝에 붙이는 것 입니다.

    printf("a : %f \n", a);
    printf("b : %f \n", b);

  이제, 마지막으로 %d, %o, %x 도 아닌 %f 가 등장하였습니다. 만약, 여기서 a 를 %d 형식으로 출력하면 어떻게 될까요? 한 번 해보세요. 아마 이상한 숫자가 나오게 될 것입니다. 왜냐하면 a 는 지금 정수형 변수가 아니기 때문이죠. 설사, 우리가 a = 3f; b = 3; 라고 해도, 이미 a 와 b 를 실수형 변수로 선언하였기 때문에 컴퓨터는 a ,b 를 절대 정수로 보지 않습니다.
  따라서, 우리는 실수형 변수를 출력하는 형식인 %f 를 사용해야 합니다.

printf 의 또 다른 형식

/* printf 형식 */
#include <stdio.h>
int main()
{
    float a = 3.141592f;
    double b = 3.141592;
    int c = 123;
    printf("a : %.2f \n", a);
    printf("c : %5d \n", c);
    printf("b : %6.3f \n", b);
    return 0;
}

만약 위 소스를 성공적으로 쳤다면 실행시 아래와 같이 나오게 됩니다.


printf("a : %.2f \n", a);

  이번에는 %f 가 아니라 %.2f 로 약간 다릅니다. 그렇다면 .2 가 뜻 하는 것은 무엇일까요? 대충 짐작했듯이, 무조건 소수점 이하 둘째 자리 까지만 표시하라 란 뜻입니다. 따라서, 위의 경우 3.141592 중 3.14 까지만 출력되고 나머지는 잘리게 되죠.
  여기서 '무조건' 이라는 것은 %.100f 로 할 경우에도, 3.141592000000....00 을 표시해서 무조건 100 개를 출력하게 합니다.

printf("c : %5d \n", c);

  이번에는 %d 가 아닌 %5d 입니다. 여기서 .5 가 아님을 주의합시다. 이 말은, 숫자의 자리수를 되도록 5 자리로 맞추라는 것 입니다. 따라서, 123을 표시할 때, 5 자리를 맞추어야 하므로 앞에 공백을 남기고 그 뒤에 123 을 표시했습니다. 그런데, 123456 을 표시할 때, %5d 조건을 준다면 어떻할까요? 이 때는 그냥 123456 을 다 표시합니다. 앞서 .?f 는 ? 의 수 만큼 무조건 소수점 자리수를 맞추어야 하지만 이 경우는 반드시 지켜야 되는 것은 아닙니다.

printf("b : %6.3f \n", b);

  마지막으로, 위에서 썼던 두 가지 형식을 모두 한꺼번에 적용한 모습입니다. 전체 자리수는 6 자리로 맞추되, 반드시 소수점 이하 3 째 자리 까지만 표시한다는 뜻이지요.

변수 작명하기

   앞서, 보았듯이 변수를 선언하는 것은 어려운 일이 아닙니다. 단지, 아래의 형태로 맞추어 주기만 하면 되죠.

(변수의 자료형) 변수1, 변수2, .....  ;
/* 예를 들어 */
int a , b, c, hi;
float d, e, f, bravo;
double g, programming;
long h;
short i;
char j,k, hello, mineral ;

  이 때, 변수 선언시 주의해야 할 점이 있습니다. 일단, 변수 선언은 소스의 최상단에 위치해야 합니다. 예를들어서 아래와 같이 프로그램을 짠다면...

/* 변수 선언시 주의해야 할 점 */
#include <stdio.h>
int main()
{
    int a;
    a = 1;
    printf("a 는 : %d", a);
    int b; // 오류발생!
    return 0;
}

  컴파일시 다음과 같은 오류를 발생하게 됩니다.

error C2143: 구문 오류 : ';'이(가) '형식' 앞에 없습니다.

  따라서, 이러한 오류를 방지하기 위해, 모든 변수는 최상단에 몰아서 선언을 해야 합니다.
  두번째로, 사람의 이름을 지을 때, 여러가지를 고려하듯이 변수의 이름에서도 여러가지 조건들이 있습니다. 아래 예제를 보세요.

/* 변수 선언시 주의해야 할 점 */
#include <stdio.h>
int main()
{
    int a, A; // a 와 A 는 각기 다른 변수 입니다.
    int 1hi;
    // (오류) 숫자가 앞에 위치할 수 없습니다.
    int hi123, h123i, h1234324; // 숫자가 뒤에 위치하면 괜찮습니다.
    int 한글이좋아;
    /*
    (오류)
    변수는 오직 알파벳, 숫자, 그리고 _ (underscore)로만으로 이루어져야 합니다. */
    int space bar;
    /*
    (오류)
    변수의 이름에는 띄어쓰기하면 안됩니다.  그 대신 _ 로 대체하는 것이 읽기 좋습니다.*/
    int space_bar; // 이것은 괜찮습니다.
    int enum, long, double, int, break
    /* (오류)
       지금 나열한 이름들은 모두 '예악어' 로 C 언어에서 이미 쓰이고 있는 것들입니다.
       따라서 이러한 것들은 쓰면 안됩니다. 이를 구분하는 방법은 예약어들을 모두
       외우거나 '파란색' 으로 표시된 것들은 모두 예약어라 볼 수 있습니다   */

    return 0;
}

  이 안에 모든 내용이 들어 있습니다. 변수의 이름은 반드시
  • 숫자가 앞에 위치하면 안됩니다. 그러나 중간이나 뒤는 괜찮습니다. 
  • 변수명은 오직 영어, 숫자, _ 로 만 구성되어 있어야 합니다.
  • 변수의 이름에 띄어쓰기가 있으면 안됩니다.
  • 변수의 이름이 C 언어 예약어 이면 안됩니다. 보통 예약어를 쓰면 파란색 글자체로 표시되어 예약어를 썼는지 안썼는지 알 수 있습니다.
 또한 C 언어는 대소문자를 구분합니다. (Case sensitive) 따라서, VARiable 과 Variable 은 다른 변수 입니다. 왠지, 조건이 많아 변수명을 지을 때, 까다로울 것 같지만 그냥 평범하게 짓다보면 예약어와 겹칠일 도 없고, 숫자가 앞에 오는 경우도 별로 없습니다.

  자, 이제 우리는 C 언어에서 중요한 부분인 변수에 대해서 알아보았습니다. 현재 우리는 수를 다루는 변수들만 다루었지만, 다음 강좌에서는 변수에 대한 산술 연산과, 문자를 다루는 변수에 대해 알아보도록 하겠습니다.
 



댓글 3개:

  1. 좋은정보 감사해요 ^^ 더욱 수고해주세요 ^^

    답글삭제
  2. @아리가또~ - 2009/05/02 09:58
    감사합니다 ^^

    답글삭제
  3. 여기서부터머리가아파오네요..

    답글삭제