IT 과학/C언어

C언어 | 데이터형과 기억 클래스

곰뚱 2020. 1. 11.

 

 

 

C 언어에는 변수나 함수의 성질을 결정하는 데에 형(type)과 기억 클래스라는 개념을 이용한다. 형은 데이터 크기를 규정하는데 대해서, 기억 클래스는 그 데이터가 메모리상의 어디에 위치하는가를 규정하는 것이다. 이미 설명한 기본적인 형과 새로운 형인 enum형을 설명하고 다시 기억 클래스에 의해 유도되는 자동 변수, 정적 변수, 외부 변수 등의 사항에 대해 설명한다.

 

 

1. 기본 데이터형

데이터형의 분류는 이미 앞에서 설명하였다. 여기서는 기본 데이터형에 관한 보조적인 사항에 대해 설명한다.

 

(1) 문자형

K&R C에서 char형은 부호가 없이 사용하지만 ANSI C에서 char형은 부호가 있으며, unsigned char형은 부호 없이 사용되므로 주의할 필요가 있다.

예를 들어, K&R C는

 

            char c;
            while((c=getchar( ))!= EOF) {
                 .
                 .
                 .
             }

 

와 같이 표시하며 c는 부호를 갖지 않기 때문에 -1은 255로 취급되어 EOF를 검사할 수 없다. 위의 예는 무한 루프가 되어 루프를 벗어날 수 없지만, ANSI C에서 위의 프로그램은 정상적으로 동작된다.

 

(2) 정수형

정수형은 short, int, long으로 분류된다. C의 명세서에서는 short는 2바이트, long은 4바이트로 정해져 있는데 int는 컴퓨터나 컴파일러에 따라 정해진다. Turbo C에서 2바이트이나 Visual C++에서는 4바이트이다. short와 int의 구분은 컴퓨터에 따라서 short와 int의 크기가 달라질 수 있다. 또 short, int, long에 대하여 각각 unsigned형이 인정되고 있다.

 

(3) 실수형

실수형은 float와 double이 있다. 복합 연산이나 함수간에 데이터를 주고 받을 때에는 float는 double형으로 변환된다. 따라서 특별한 경우를 제외하고는 double형을 사용한다. Visual C++에서는 가급적 double을 쓸 것을 권장한다.

 

(4) 데이터의 크기

데이터의 크기를 조사하는데는 sizeof 연산자를 사용하며

 

sizeof(형)

 

라고 표시하는 것은 지정된 형의 크기를 바이트 단위로 나타내는 것이다. 아래의 프로그램은 Turbo C에서 기본형의 데이터 크기를 sizeof 연산자를 사용해서 조사한 프로그램의 결과를 표시한 것이다.

 

예제 1. Turbo C의 기본형의 데이터 크기를 조사하라.

프로그램

#include <stdio.h>

main( )
{
             printf("char    = %d\n", sizeof(char));
             printf("short   = %d\n", sizeof(short));
             printf("int     = %d\n", sizeof(int));
             printf("long    = %d\n", sizeof(long));
             printf("float   = %d\n", sizeof(float));
             printf("double  = %d\n", sizeof(double));
}

 

결과
      char    =1
      short   =2
      int     =2
      long    =4
      float    =4
      double  =8

 

확인문제 1

/* sizeof 연산자를 사용한 자료형의 크기 확인 프로그램 */
#include <stdio.h>
main( )
{
      char ch[ ] = "Lee Hyolee";
      char *p = "이효리";
      struct tag{
        char name[10];
        int  age;
      }x;
      union abc{
        char ch;
        int  number;
        long length;
      }y;

      printf("       char: %3d\n", sizeof(char));
      printf("      short: %3d byte(s)\n", sizeof(short));
      printf("        int: %3d byte(s)\n", sizeof(int));
      printf("   unsigned: %3d byte(s)\n", sizeof(unsigned));
      printf("       long: %3d byte(s)\n", sizeof(long));
      printf("      float: %3d byte(s)\n", sizeof(float));
      printf("     double: %3d byte(s)\n", sizeof(double));
      printf("long double: %3d byte(s)\n", sizeof(long double));
      printf("array ch[ ]: %3d byte(s)\n", sizeof ch);
      printf("  pointer p: %3d byte(s)\n", sizeof p);
      printf("   struct x: %3d byte(s)\n", sizeof x);
      printf("   union  y: %3d byte(s)\n", sizeof y);
}

 

(5) 데이터의 배정도

데이터 크기가 작은 데이터는 메모리 용량이 작지만 표현 능력도 역시 작다. 반대로 데이터가 큰 데이터는 메모리 용량을 많이 차지하지만 표현 능력도 역시 크다. 이와 같이 데이터의 크기와 배정도(유효 자릿수)는 그 형에 따라서 어떤 것을 사용할 것인가를 사용자가 필요에 따라서 결정해야 한다.

 

예제 2. 10의 0 제곱부터 17제곱까지 int형, long형 double형으로 각각 구하고 그 정밀도를 비교하여라.

프로그램

#include <stdio.h>

main()
{  
        int i, a=1;
    long b=1;
    double c=1.0;
    printf("       int       long       double\n");
    for (i=0; i<18; i++) {
          printf("10^%2d=%12d%12d%25.1f\n",i, a, b, c);
          a = a * 10;         
          b = b * 10;
          c = c * 10;
    }
}

 

결과
                        int             long                double       
      10 ^ 0=             1                1                    1.0
      10 ^ 1=            10               10                   10.0
      10 ^ 2=           100              100                  100.0
      10 ^ 3=          1000             1000                 1000.0
      10 ^ 4=         10000            10000                10000.0
      10 ^ 5=        -31072           100000               100000.0
      10 ^ 6=         16960          1000000              1000000.0
      10 ^ 7=        -27008         10000000             10000000.0
      10 ^ 8=         -7936        100000000            100000000.0
      10 ^ 9=        -13824       1000000000           1000000000.0
      10 ^ 10=        -7168       1410065408          10000000000.0
      10 ^ 11=        -6144       1215752192         100000000000.0
      10 ^ 12=         4096       -727379968        1000000000000.0
      10 ^ 13=       -24576       1316134912       10000000000000.0
      10 ^ 14=        16384        276447232      100000000000000.0
      10 ^ 15=       -32768      -1530494976     1000000000000000.0
      10 ^ 16=            0       1874919424    10000000000000000.0
      10 ^ 17=            0       1569325056     100000000000000000

728x90

 

 

2. enum 형

Pascal에서도 포함되어 있는 이 형을 ANSI C에서도 포함한다. enum형을 다른 말로 열거형이라고 하기도 한다. enum 형의 정의와 enum 형 변수의 선언은 다음과 같다.
    

enum 태그(tag)명 {이름, 이름, ...}; <--- 태그의 정의

enum 태그(tag)명 변수명 [, 변수명, ...]; <--- 변수의 선언

 

예를 들어 요일을 표현하는 데이터 sun, mon, ...sat를 사용하고 싶을 경우 우선,

 

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

 

라고 enum day 형을 정의한다. 다음

 

enum day a;

 

라고 표시하면 enum day 형의 변수 a 가 선언된다. 이 변수를 이용하여 다음과 같이 표현한다.

 

            if(a==sun){

                일요일의 처리

              }   

 

또, enum 형의 데이터로 나열되는 이름들(sun, mon, ...)은 int 형의 값을 갖는 상수로, 선언되는 순서로 0, 1, 2, ... 라고 차례대로 지정되어 있으므로 다음과 같이 표현한다.

 

            for(a=sun; a<=sat; a++){
                 .
                 .
                 .
            }

 

확인문제 2

/* 열거형 상수의 사용 예제 1 */
#include <stdio.h>
void main(void)
{
      enum hardware{IBM, VAX, TriGem, DEC};
      enum hardware computer1, computer2, computer3, computer4;
      int data1, data2;

      data1 = VAX;
      data2 = DEC + TriGem;
      computer1 = IBM;
      computer2 = VAX;
      computer3 = TriGem;
      computer4 = DEC;

      printf("computer1= %d,computer2= %d,computer3 = %d,computer4 = %d\n",
            computer1, computer2, computer3, computer4);
      printf("data1 = %d, data2 = %d\n", VAX, DEC + TriGem);
      computer1 = DEC + 10;
      computer2 = computer1 + VAX;
      printf("computer1 = %d, computer2 = %d\n", computer1, computer2);
}

 

확인문제 3

/* 열거형 상수의 사용 예제 2 */
#include <stdio.h>
enum {FALSE, TRUE} flag;
enum car_type {tico, pride, accent, ciero, sepia, prince, sonata,
                granger, acadia, galloper, musso};
int main(void)
{
      char *cars[ ] = {"tico", "pride", "accent", "cielo", "sepia", "prince",
                       "sonata", "granger", "acadia", "galloper", "musso"};
      char car_name[10];
      enum car_type car;

      flag = FALSE;
      printf("Enter a car type: ");
      gets(car_name);
      for (car = tico; car <= granger; car++)
      {
          if (strcmp(car_name, cars[car]) == 0)
          {
               flag = TRUE;
               break;
          }
      }
      if (flag == TRUE)
          printf("%s is my car.\n", car_name);
      else
          printf("I don't know about the car %s.\n", car_name);
      return 0;
}

 

 

3. 형 변환과 캐스트

(1) 혼합 연산

C는 데이터형의 변환 규격이 까다롭지 않다. 지금까지의 예제에서도 char형과 int형을 혼용해서 사용하였다. 예로,

 

int c;

 

라고 작성하고,

 

c = 'a',

 

라고 작성하는 등이다.

또, C에서는 식 안에 다른 형이 있을 때는 char와 short는 int로 변환되고, float는 double로 변환된다. 또 식 중에서 가장 높은 형으로 변환되며 식의 값도 역시 가장 높은 형으로 된다.

 

double c;

c = 10.5 + 5 + 'a';

 

위에서 보면, 'a'는 int로 5와 'a'는 다시 double로 변환되어 최종적으로 double 형으로 연산된다. 이것은 함수의 인수로 이용될 경우에도 같다. 함수의 인수가 스택에 저장되는 경우에도 위와 같은 규칙에 따라서 int(2바이트), long(4바이트), double(8바이트) 중 어느 한 형으로 변환되어 전달된다.

 

확인문제 4

/* 혼합 산술 연산에서 자동형 변환 */
#include <stdio.h>
main( )
{
      char ch = 'A';  short sh = 10;  int i = 20;  unsigned u = 30;
      long l = 40;  unsigned long ul = 50;
      float f = 3.3, f2 = 3.4;  double d = 4.4;  long double ld = 5.5;

      printf("%d\n", sizeof( ch - sh / i ));  /* 최종 값은 int 형 */
      printf("%d\n", sizeof( i + d /ld ));    /* 최종 값은 long double형 */
      printf("%d\n", sizeof( ch + 'a' ));     /* 최종 값은 int 형 */
      printf("%d\n", sizeof( sh + l * 5 ));   /* 최종 값은 long 형 */
      printf("%d\n", sizeof( f + i / sh ));   /* 최종 값은 double 형 */
      printf("%d\n", sizeof( i / sh * f));    /* 최종 값은 double 형 */
      printf("%d\n", sizeof( u * i - sh));    /* 최종 값은 unsigned 형 */
      printf("%d\n", sizeof( ul + u / d ));   /* 최종 값은 double 형 */
      printf("%d\n", sizeof( (23 / f) + i));  /* 최종 값은 double 형 */
      printf("%d\n", sizeof( f + d + ld ));   /* 최종 값은 long double 형 */
      printf("%d\n", sizeof( f + f2 ));
}

 

확인문제 5

/* 대입문에서 자동형 변환(올림 변환과 내림 변환) */
#include <stdio.h>
main()
{
     char    ch;
     long    data;
     float   x_data;
     double  f_data;

     x_data = 123.456;
     f_data = data = ch = 'C';
     printf("ch = %c, data = %ld, f_data = %lf, x_data = %.3f\n",
             ch, data, f_data, x_data);
     ch = ch + 1;
     data = x_data + 2 * ch;
     f_data = 3.0 * ch + x_data;
     printf("ch = %c, data = %ld, f_data = %.3lf\n", ch, data, f_data);
     ch = 32237345;
     printf("ch = %c\n", ch);
}

 

(2) 캐스트 (cast)

C에 있어서 형 변환 규칙은 엄격하지 않기 때문에 애매함이 생길 위험이 있다. 그래서 캐스트라는 연산자를 사용해서 일시적으로 변환하여 형 변환에 따르는 엄격함을 유지하려고 한다. 예를 들어, 다음과 같이 인수가 double 형인 함수 sqrt( )가 있다고 가정하자.

 

         sqrt(double x)
         {
            .
            .
            .
         }

 

이것을 sqrt(10); 이라고 호출하면 10은 정수형이기 때문에 형이 불일치하여 결과를 전혀 예측할 수 없다. 따라서 이것은 sqrt(10.0)으로 호출해야 한다. 그러나 i가 정수형이었을 때 sqrt(i); 로 호출하고 싶을 경우에는 위와 같이 0을 쓸 필요가 없다. 그럼 다음과 같이 캐스트를 이용해 보자.

 

sqrt((double)i);

 

이렇게 하면 i의 형은 일시적으로 double 형이 된다. 캐스트의 일반형은  ( ) 내의 형(char, int…double, 포인터, 구조체, 공용체) 등을 사용한다. 예를 들어, int형의 배열 word[ ]이 있을 때 이 데이터를 바이트 단위로 참조하려면

 

char *byte;

 

라는 포인터를 준비하여

 

byte = (char*)word;

 

라고 하면, *byte 에서 1바이트 단위로 데이터를 취하게 된다. 그러나 word는 int 형의 포인터 상수이므로 (char*)로 캐스트하지 않으면 경고 에러가 발생한다.

 

확인문제 6

/* 캐스트 연산의 사용한 산술 연산의 예제 */
#include <stdio.h>
main( )
{
      char ch;
      int i1, i2;
      float f1, f2;

      i1 = 875;
      ch = (char) i1;
      printf("Integer assigned to character: %d -> %d :(%c)\n",
            i1, (char)i1, ch);

      f1 = 111.88;
      f2 = 222.99;
      i2 = (int)f1 + (int)f2;
      printf("(int)%.2f + (int)%.2f -> %d\n", f1, f2, i2);
}

 

 

4. 기억 클래스

C는 변수나 함수의 성질을 결정하는데, 형과 기억 클래스라는 개념을 사용한다. 형은 데이터의 크기를 규정하고, 기억 클래스는 그 데이터가 메모리의 어느 곳에 위치하는가를 규정하는 것이다.

(1) 기억 클래스와 영역

C 에서는 여러 가지 성질의 변수를 사용하는 경우가 있다. 예를 들면, 프로그램의 전체 영역에서 사용하는 변수와, 변수가 선언된 함수 중에서만 사용할 수 있는 것으로 구분된다. 이와 같이 어떤 변수가 프로그램의 어느 범위에서 사용될 수 있는가를 영역(scope) 혹은 통용 범위라고 한다. 또 변수를 스택에 위치시킬 것인지, 정적 영역에 위치시킬 것인지를 지정할 수 있다. 이와 같이 변수를 특정 메모리 영역 내에 할당하는 것을 기억 클래스라고 한다.

 

변수의 성격은 그것을 선언하는 기억 클래스 지정자와 선언 장소 (함수 중에서 또는 함수의 외부에서)에 의해 결정된다. C에 있어서 기억 클래스 지정자는 auto, static, extern, register와 같다.

 

기억 클래스 지정자를 포함한 변수의 선언은 다음과 같다.

 

[기억 클래스 지정자] 형 변수명[, 변수명 …];

 

기억 클래스 지정자를 생략하면 auto라고 해석된다. 

 

(2) 자동 변수

지금까지 특별히 설명하지 않았지만, 우리가 아무런 기억 클래스 지정도 하지 않고 사용했던 변수는 실제로는 자동 변수(auto) 이다. C 에서 사용되는 변수의 대부분은 auto 이므로,

 

auto int i;

 

라고 표시한 것을 생략해서

 

int i;

 

라고 쓴다.

 

이 자동 변수는 메모리 중 스택에 위치하게 되어 그 변수가 선언되어 있는 함수의 시작에서부터 생성되고 함수의 끝에서 소멸된다. 이와 같이 자동 변수는 프로그램의 실행 중에서 처음부터 존재하는 것이 아니고 그것이 선언되어져 있는 함수가 실행되고 있을 때만 스택에 위치할 수 있기 때문에 메모리의 낭비를 줄일 수 있다. 그러나 자동 변수는 함수의 시작 때에 생성되는 것으로 컴파일시에 초기화할 수는 없다.

 

확인문제 7

/* 블럭에서 자동 변수의 사용 범위 */
#include <stdio.h>
void sub(void);
void main(void)
{
      int a = 1, b = 2, c = 3;
      {
          int a = 100;
          printf("a = %3d, b = %3d, c = %3d\n", a, b , c);
          {
              int b = 200;
              printf("a = %3d, b = %3d, c = %3d\n", a, b , c);
              {
                 int c = 300;
                 printf("a = %3d, b = %3d, c = %3d\n", a, b , c);
              }
              printf("a = %3d, b = %3d, c = %3d\n", a, b , c);
          }
          printf("a = %3d, b = %3d, c = %3d\n", a, b , c);
      }
      printf("a = %3d, b = %3d, c = %3d\n", a, b , c);
      sub( );
}

void sub(void)
{
      int a = 10, b = 20, c = 30;
      printf("a = %3d, b = %3d, c = %3d\n", a, b , c);
}

 

(3) 정적 변수

정적 변수는 기억 클래스 지정자 static을 사용해서

 

static int a;

 

등과 같이 선언한다. 이렇게 변수 a는 정적인 영역에 할당되어 프로그램의 시작에서부터 끝까지 같은 메모리 위치에 할당된다. static 변수가 함수의 내부에서 선언되었다면 그 변수는 선언된 함수 중에서만 참조가 된다.  이런 과정은 자동 변수와 같지만 자동 변수는 스택에 위치되고 함수의 시작에서부터 생성되며 함수의 끝에서 소멸되기 때문에 변수의 값을 프로그램 실행 중에 보존할 수 없다. 반면에 static 형 변수는 프로그램 실행 중에도 변수값이 계속 보존된다.

 

static 변수는 선언시에 다음과 같이 초기화를 할 수 있고 이 값은 컴파일 때에 한번 초기화된다.

 

static int num = 0;

 

이것을 이용하여 어떤 처리가 일어난 횟수를 세는 함수 count( )를 작성한다면 다음과 같게 된다.

 

          count( )
          {
             static int num = 0;
             print("%d\n", ++num);
          }

 

즉, static 변수 num은 컴파일시에 한 번만 0으로 초기화된다. 그리고 count( )가 호출될 때마다 num의 값은 +1씩 증가된다.

 

만약 이것을 자동 변수를 사용해서 int num = 0; 이라고 하면 num의 값은 이전의 값을 가지고 있지 않기 때문에 정상적으로 횟수를 셀 수 없게 된다. 이러한 변수를 static 지역변수라고 한다. 이에 반해 static 변수를 함수의 외부에서 선언하게 되는 경우, 그 변수는 그 파일(file) 내에서 사용되며 static 전역변수라고 한다.

 

예제 3. 숫자 문자가 몇 번 입력되었는가를 세는 함수 count를 작성하여라.
  
 ●프로그램

#include <stdio.h>
void count();

main()
{
       int c;
       while((c=getchar()) != EOF)  
          if('0'<= c && c <='9')    
            count();   
}

void count()
{
       static int num=0;
       printf("이제까지 숫자 문자가 입력된 횟수는 %d\n",++num);
}

 

● 결과
 5
 이제까지 숫자 문자가 입력된 횟수는 1
 8
 이제까지 숫자 문자가 입력된 횟수는 2
 q
 2
 이제까지 숫자 문자가 입력된 횟수는 3
 ^ Z

 

※ 정적변수와 자동변수의 차이 : 아래의 프로그램을 ANSI C 형으로 변환하고 실행시켜 보라.

        #include <stdio.h>

        main()
        {
                printf("static var.     auto var.\n");
                printf("-----------------------\n");
                sub();
                sub();
                sub();
        }

        sub()
        {
                static int i = 1;
                auto   int j = 3;

                printf(" i = %d         j = %d\n", i, j);
                i++; j++;
        }

 

(4) 외부 변수

함수 밖에서 기억 클래스 지정자를 붙이지 않고 정의하는 것이 외부 변수이다. 외부 변수는 모든 함수에 공통으로 사용된다. 다음에서 a는 외부 변수이므로 main에서나 func에서도 공통으로 사용된다. main 실행시에 우선 a=0이 되나 func에서 a=100이 되므로 main으로 돌아왔을 때의 a의 값은 100이 된다.

 

     int a; /* 외부 변수 */
     main( )
     {
        a = 0;
        func( ); /* 이 때 a의 값은 100이 되어 있다. */
     }
     func( )
     {
       a = 100;
     }

 

외부 변수가 정의되기 전에 그 변수를 사용할 경우, 또는 다른 파일에서 그것을 사용하고 싶을 때에는 extern을 선언해야 한다. C 에서는 소스 코드를 몇 개로 분할하여 컴파일 시킨다.  이것을 분할 컴파일이라 하며 이 1개의 컴파일 단위를 파일 혹은 모듈이라고 한다.

 

모듈(파일) A에서 정의된 외부 변수 a를 모듈(파일) B에서 사용할 경우, 외부 변수 a는 모듈(파일) A에서 정의되어 있으므로 이것을 모듈(파일) B에서 사용하고 싶을 경우 extern 지정자를 사용하여 선언해야 한다. 외부 변수에 대한 정의(definition)와 선언(declaration)의 의미 차이는 중요하다. 정의는 그 변수의 메모리 할당에 관여하는 것인데 반하여 선언은 그 변수를 지정한 형으로 사용하는 것을 선언하는 것일 뿐이지 메모리 할당에 관여하지는 않는다.

 

확인문제 8

/* 외부 변수와 자동 변수의 사용 방법 */
#include <stdio.h>
double up_external = 55.55;
double global = 33.33;
void up_date(double);
void local(void);
void main(void)
{
      extern double down_external;

      printf("up_external = %4.2lf, global = %4.2lf\n", up_external, global);
      up_date(22.22);
      printf("down_external = %4.2lf\n", down_external);
      local( );
      printf("global = %4.2lf\n", global);
}

double down_external = 44.44;
void up_date(double down_external)
{
      printf("up_external = %4.2lf\n",up_external);
      printf("down_external = %4.2lf\n",down_external);
}

void local(void)
{
      double global = 11.11;

      printf("global = %4.2lf\n", global);
      printf("up_external = %4.2lf, down_external = %4.2lf\n",
              up_external,down_external);
}

 

(5) 레지스터 변수

기억 영역을 CPU의 레지스터에 할당하는 것 외에는 자동 변수와 동일한 특성을 가진다.  레지스터 변수는 기억 클래스 지정자 레지스터를 사용해서 다음과 같이 선언한다.

 

register a, b;

 

레지스터 변수는 내부적으로 무조건 int 형을 사용한다. 자주 사용되는 변수의 접근(access) 속도를 빠르게 할 때 사용되고 자동변수와 함수의 형식인자에 대해서만 허용된다. Turbo C 에서는 레지스터 변수로 SI와 DI 레지스터로 할당할 수 있기 때문에 동시에 두 개까지 레지스터 변수를 사용한다. 그 밖의 컴파일러의 경우에는 미리 정해진 개수만큼의 레지스러가 이용되거나 컴파일러가 알아서 지정하는 경우도 있다. 만약 레지스터가 모두 사용되고 있으면 사용되지 않은 레지스터 변수는 자동 변수로서 스택 위치를 확보하게 된다.

 

<응용> 난수 함수 : rand(), srand()

                static int randx = 1;   /* seed 초기화 */

                rand()  /* random 함수: <stdlib.h>에도 있음 */
                {
                        randx = ( randx * 25173 + 13849) % 65536U;
                        return (int) randx;
                }       /* 밑줄친 숫자는 prime number로 사용하는 것이 좋음 */

        ※ 위 rand() 함수의 문제점 --- 항상 동일한 순서대로 난수를 발생시킴

                srand(x)        /* seed 값을 변경하는 함수 */
                unsigned x;
                {
                        randx = x;
                }

                main()  /* 난수함수 test program */
                {
                        int seed, i;

                        printf("Input seed value for random number generation : ");
                        scanf("%d", &seed);
                        srand(seed);
                        for (i = 0; i < 10; i++) printf("%d\n", rand());
                }

 

<응용> 주사위 게임

기하학적으로 가능한 주사위: 4/6/8/12/18면 5가지만 가능 ( 컴퓨터로는 n면 주사위 가능 )

                #define SCALE   32768.0

                int rollem(sides)       /* sides면을 갖는 주사위 */
                int sides;
                {
                        float roll;

                /*      위의 rand() 함수 이용
                        -32768 <= rand() < 32768
                        ---> -1 <= rand()/SCALE < 1
                        --->  0 <= rand()/SCALE+1.0 < 2
                        --->  0 <= (rand()/SCALE+1.0)*sides < 2*sides
                        --->  0 <= (rand()/SCALE+1.0)*sides/2.0 < sides
                        --->  1 <= (rand()/SCALE+1.0)*sides/2.0+1.0 < sides+1
                */
                        roll = ( (float) rand() / SCALE + 1.0 ) * sides / 2.0 + 1.0;
                        return (int) roll;
                }

                main()  /* dice rolling program */
                {
                        int dices, sides;
                        int count, score, seed;

                        printf("Input seed value : "); scanf("%d", &seed);
                        srand(seed);
                        printf("Input sides of a dice : "); scanf("%d", &sides);
                        while ( sides > 0 ) {
                                printf("Input number of dices : "); scanf("%d", &dices);
                                for ( score = count = 0; count < dices; count++ )
                                        score += rollem(sides);
                                printf("Using %d-side %d dices, your score is %d.\n",
                                        sides, dices, score);
                                printf("Input sides of a dice (0 to quit) : ");
                                scanf("%d", &sides);
                        }
                        puts("Thank you!");
                }

 

<응용> 정수 입력 함수 : getint()

                #include <stdio.h>

                #define LENG            80
                #define STOP            -1
                #define NONUM            0
                #define YESNUM           1

                int getint(pint)        /* fetch integer string and */
                int *pint;              /* convert it to an integer */
                {
                        char intarr[LENG];
                        int ch;
                        int ind = 0;

                        ch = getchar();
                        while ( ch == '\n' || ch == ' ' || ch == '\t' )
                                ;       /* skip blanks, tabs, new-line characters */
                        while ( ch != EOF && ch != '\n' && ch != ' ' && ind < LENG ) {
                                intarr[ind++] = ch;
                                ch = getchar();
                        }
                        intarr[ind] = '\0';
                        if ( ch == EOF ) return STOP;
                        else return stoi(intarr, pint);
                }

                int stoi(string, intptr)        /* convert string to integer */
                char string[];                  /* string of digits */
                int *intptr;                    /* integer value for 'string' */
                {
                        int sign = 1;
                        int index = 0;

                        if ( string[index] == '-' || string[index] == '+' )
                                sign = (string[index++] == '-') ? -1: 1;

                        *intptr = 0;
                        while ( string[index] >= '0' && string[index] <= '9' )
                                *intptr = 10 * (*intptr) + string[index++] - '0';
                        if ( string[index] == '\0' ) {
                                *intptr = sign * (*intptr);
                                return YESNUM;
                        } else
                                return NONUM;
                }

                main()
                {
                        int num, status;

                        puts("Get integer from keyboard.");
                        puts("Input integers and press <Enter>.");
                        while ( (status = getint(&num)) != STOP ) {
                                if ( status == YESNUM )
                                        printf("Integer %d is fetched.\n", num);
                                else
                                        printf("Input string NOT an integer\n");
                        }
                }

※ 난수 함수 rand(), srand()를 별개의 file로 독립시켜 compile, link해 보자.
   정수 입력 함수 getint(), stoi()를 별개의 file로 독립시켜 compile, link해 보자.

 

<응용> sorting (정렬)

                #include <stdio.h>

                #define MAX     100

                main()
                {
                        int n, data[MAX];

                        n = getData(data, MAX);
                        sort(data, n);
                        putData(data, n);
                }

                getData(data, limit)    /* input n integers */
                int data[], limit;
                {
                        int i = 0;

                        while ( i < limit ) {
                                scanf("%d", &data[i++]);
                                if ( data[i++] == -999 ) return i-1;
                        }
                }

                sort(data, n)   /* interchange sort: ascending order */
                int data[], n;
                {
                        int i, j;

                        for ( i = 0; i < n-1; i++ )
                                for ( j = i+1; j < n; j++ )
                                        if ( data[i] > data[j] )
                                                interchange(&data[j], &data[i]);
                }       /* 위의 interchange는 아래와 같다 */

                putData(data, n)
                int data[], n;
                {
                        int i;

                        for ( i = 0; i < n; i++ )
                                printf("\t%d\n", data[i]);
                }

 

interchange 함수
                
                interchange(u, v)
                int *u, *v;
                {
                       int temp;

                       temp = *u;
                       *u = *v;
                       *v = temp;
                }

 

※ data file(예: sample.dat)에 sort하고자 하는 정수를 나열하여 입-출력 방향 전환으로 실행.
        ( 끝 표시를 위한 data는 -999 )

※ 함수 getData와 putData는 file 'getput.c', 함수 sort는 'sort.c', 함수 main은 'main.c'로   작성하여 TC에서 project를 이용하여 compile 하시오.

        1. 파일 <이름>.prj를 작성하고 <이름>.prj에는 C source 파일들의 이름만 나열한다.
                예) test.prj --- main.c getput.c sort.c
        2. TC에서 project name을 <이름>.prj로 하고 compile 한다.

※ TCC로 각 파일을 compile하여 TLINK로 link하여 실행 파일을 만드시오.

 

 

5. 초기화

컴파일시의 초기화와 실행시의 초기화는 의미가 다르다. 변수의 선언시 행하는 초기화가 컴파일시의 초기화이고, 선언시 이외의 장소에서 치환문(배정문, 대입문)을 사용하는 것이 실행시의 초기화이다. 가령

 

     count ( )
    {
        static int a = 0; ← 컴파일시의 초기화
        static int b;
        b = 0; ← 실행시의 초기화
        ː
    }

 

라고 표현되는 함수가 있을 때 a는 컴파일시에 한 번 초기화되는 것에 비해 b는 함수 count가 호출될 때마다 0으로 초기 설정된다. 컴파일시의 초기화와 실행시의 초기화가 의미하는 것은 각각 정적 변수와 외부 변수라는 것이다. 자동 변수는 컴파일시에 초기화될 수 없다.  자동 변수에 있어서,

 

int a = 0;

 

 

int a;

a = 0;

 

은 같은 표현이고 모두 실행시에 0으로 설정된다.

 

 

그리드형

댓글