ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Kernighan - C] #1-10 외부변수
    전기전자공학/프로젝트 2017. 6. 24. 19:18

    1. 10 외부 변수

    The C Programming Language - Kernighan, Ritchie


    프로그램을 작성하다 보면 여러 함수에서 공통으로 변수를 사용해야 될 때가 있는데, 이 변수를 '공통 변수'(global variable)이라 한다.

    공통 변수의 정의는 extern 이라는 명령어를 사용한다.

    이 변수는 함수 바깥에서도 정의하고 함수 각각에서 정의되어야 한다.


    예제) 가장 긴 행 출력 프로그램 <-- 1.9 에서 했던 내용과 동일하지만 longest, max, line을 공통변수로 바꿔서 코딩했다.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    #include <stdio.h>        // 가장 긴 행 출력 프로그램
     
    #define MAXLINE 1000    /* 최대 입력받을 수 있는 행 길이*/
     
    int max;                /* 최대 길이 */
    char line[MAXLINE];        /* 현재 입력받는 행을 담는 배열 */
    char longest[MAXLINE];    /* 가장 긴 행을 저장할 배열 */
     
    int getline(void);
    void copy(void);
     
    /* 가장 긴 행을 출력한다; specalized ver */
    main()
    {
        int len;
        extern int max;
        extern char longest[];
     
        max = 0;
        while ((len = getline()) > 0
            if (len > max) {
                max = len;
                copy();
            }
        if (max > 0)
            printf("%s", longest);
     
        return 0;
    }
     
    /* getline: 행을 입력받아서 행의 길이를 측정 */
    int getline()
    {
        int c, i;
        extern char line[];
     
        for (i = 0; i < MAXLINE - 1 && (c = getchar()) != EOF && c != '\n'++i)
            line[i] = c;
        
        if (c == '\n') {
            line[i] = c;
            ++i;
        }
     
        line[i] = '\0';
     
        return i;
     
    }
     
    void copy()
    {
        int i = 0;
        extern char line[], longest[];
     
        while ((longest[i] = line[i]) != '\0')
            ++i;
    }
     
    cs


    프로그램의 첫 부분에서 변수를 선언하고 각 함수에 필요한 공통변수를 extern과 함께 문장으로 선언하고 있다.

    공통변수를 사용하는 함수가 공통변수가 선언된 파일과 같은 데 들어있는 경우(위와 같이 display.c에 같이 들어있을 때) 

    'extern' 선언을 생략할 수 있다.

    그치만 다른 .c 파일에 있을 경우, copy함수 안에 extern 선언이 꼭 있어야 한다.  

    그러면 라이브러리에 있는 함수를 사용할 경우 그 함수에는 사용하는 모든 공통변수들을 프로그램의 처음에 선언해 주어야 한다는 게 말이된다.

    #include <stdio.h>

    이 선언을 통해 stdio에서 사용되는 공통변수는 다 선언된 거라 할 수 있다.


    프로그램 작성시 모든 변수를 공통으로 두면 매개변수 부분도 필요없고 간편할 거라 생각할 수 있지만

    사용하는 변수가 많아질 수록 혼돈하기 쉬워지고 일반성이 줄어든다.

    일반성이라는 말은 예를 들어, 다른 곳에서 copy 함수를 사용하려면 copy함수에서 어던 변수가 공통으로 잡힌지 알고 난뒤 선언해 주어야 한다.

    [예제 1-20] 

    파일 중에 있는 tab을 같은 길이의 빈칸으로 대치하는 프로그램을 작성하라.

    매 n번째 위치에 tab지점이 있다 가정한다. (프로그램 이름: dtab)

    내가 만든 파일: tt.h , main.c, dtab.c

    tt.h

    1
    2
    3
    4
    5
    6
    #include <stdio.h>
     
    #define MAXLINE 1000
     
    void dtab(char[], int);
    int getline(char[], int);
    cs

    main.c

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    #include "tt.h"
     
    main()
    {
        int len;
        char line[MAXLINE];
        int num = 4;
        while ((len = getline(line, MAXLINE)) > 0)
        {
            dtab(line, num);
            printf("%s\n", line);
        }
    }
    cs

    dtab.c

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    #include "tt.h"
     
    /* 왜 위치에 따라 tab의 공간이 달라지는가... */
    #define TABWIDTH 4
    void dtab(char s[], int n) {
     
        char cpArr[MAXLINE];
        int i, j;
        i = 0;
     
        while (s[i] != '\0')
            cpArr[i++= s[i];
        cpArr[i] = '\0';
     
        for (i = 0; i < TABWIDTH; i++)
            s[n + i] = ' ';
        j = n + 1;
        while (cpArr[j] != '\0')
        {
            s[n + i] = cpArr[j++];
            i++;
        }
     
        s[n + i] = '\0';
    }
    cs


    참고코드

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    #include <stdio.h>
    #include <stdlib.h>
     
    #define TABWIDTH 8
     
    int main(void)
    {
        int i;
        int c, col, spaces;
     
        col = 0;
        while((c = getchar()) != EOF) {
            if (c == '\t') {
                spaces = TABWIDTH - col % TABWIDTH;
                for (i = 0; i < spaces; ++i)
                    putchar(' ');
                col = col + spaces;
            } else {
                putchar(c);
                col = col + 1;
                if (c == '\n')
                    col = 0;
            }
        }
        return EXIT_SUCCESS;
    }
    cs

    참고 코드로 하면 tab의 위치가 달라져도 출력모양은 달라지지 않는다.

    spaces = TABWIDTH - col % TABWIDTH    부분을 익혀둬야 겠다.


    [예제 1-21] 

    빈칸이 연속으로 나올 때 그것을 tab 문자로 바꾸는 프로그램을 작성하라

    즉 예제 1-20의 역작용을 하는 entab 프로그램을 작성하되, 사용되는 tab문자의 갯수는 될 수 있는 데로 작도록 하라.

    entab.c (참고 코드 <- 출력 동작을 나중으로 미룸)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    #include<stdio.h>
    #define TABINC 8
     
    int main(void)
    {
        int nb, nt, pos, c;
     
        nb = 0;
        nt = 0;
     
        for (pos = 1; (c = getchar()) != EOF; ++pos)
            if (c == ' ') // 입력한 게 빈칸이면
            {
                if ((pos % TABINC) != 0)
                    ++nb;
                else // 빈칸갯수 = tab너비 이면
                {
                    nb = 0;
                    ++nt;
                    //printf("t");
                }
            }
            else // c가 빈칸이 아니면
            {
                for (; nt > 0--nt) // 이전에 빈칸갯수=tab너비 있으면
                    putchar('\t'); // '\t' 출력 (entab의 핵심!)
                if (c == '\t') // 빈칸입력x && tab이면
                    nb = 0; // nb = 0 -> 빈칸 출력을 안하도록..
                else // 빈칸x && tab x이면,
                    for (; nb > 0--nb) // 이전 입력에 빈칸(들)이 있지만, tab너비만큼이 아니면
                        putchar(' '); // 입력된 빈칸만큼 출력
     
                putchar(c); // c가 빈칸이 아닐 때는 입력된 c 값을 그대로 출력
      // 이 프로그램은 (빈칸=탭 너비)만을 확인하는 걸 중점으로 두므로 다른 건 상관x
                if (c == '\n')
                    pos = 0;
                else if (c == '\t')
                    pos = pos + (TABINC - (pos - 1) % TABINC) - 1;
            }
     
        return 0;
    }
    cs

    pos = pos + (TABINC - (pos - 1) % TABINC) -1;

    이 수식에서 TABINC - (pos - 1) & TABINC는 앞 detab.c의 TABWIDTH - col %TABWIDTH와 같다.

    여기서 pos 의 기준이 1인 관계로 pos -1 = col 이다.

    pos는 현재 위치를 의미하므로 원래 저장된 pos + (위치에 따른 빈칸너비) -1 해주면 된다.



    [예제 1-22]

    한 행의 n 번째 열 앞에 나타나는 빈칸 아닌 문자 중에서 마지막 문자의 뒷부분을 다음 행으로 보내어 한 행을 두 개 이상의 행으로 만드는 프로그램을 작성해보라

    ver1: 그냥 c = getchar()으로 입력받아서 특정 길이 이상이면 \n으로 한 행을 두행으로 나눠서 출력


    #include <stdio.h>
     
    main()
    {
        int i = 0;    // 열을 표시하는 인덱스
        int n = 5;    // n번째에서 행을 나누기 위한 변수
        int c;        // 입력받는 공간
     
        while ((c = getchar()) != EOF)
        {
            if (i %= n == 0)
            {
                putchar('\n');
                if (c != ' ' || c != '\t')
                    putchar(c);
                else
                    ;
            }
            else 
                putchar(c);
            i++;
        }
    }

    ver2. getline(line, MAX);

    line 부분 - 중간에 '\n'을 입력해서 line 배열 성분 자체를 바꾸는 것

    (문자열 배열 line을 copy해놓은 다음 반복변수를 각각 i,j로 써서 n번째 배열이 나올 때 마다 \n성분 넣기

    if( d== '\\')

    putchar(getchar()); 

     이 부분의 코드 해석이 아직 불완전하다.


    [예제 1-23] 

    Write a program to reomove all comments from a C program. Don't forget to handle quoted strings and character properly.

    C comments do not nest

    한글: c 프로그램에서 모든 설명문을 없애는 프로그램을 작성하라. 인용 부호 속에 있는 문자열에 주의

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    #include <stdio.h>
     
    void rmcomment(int c);
    void incomment(void);
    void in2comment(void);
    void echo(int c);
     
    main()
    {
        int c, d;
        printf(" To check /* Quoted String */ \n");
     
        while ((c = getchar()) != EOF)
            rmcomment(c);
    }
     
    void rmcomment(int c) {
        int d;
        if (c == '/')        // main에서 입력받은 문자가 '/'일 때
        {
            if ((d = getchar()) == '*'// 다음 입력받은 문자 d = '*' 인 경우
                incomment();            // 인용문 /* */이거나 // 일 때 
            else if (d == '/') {
                in2comment();
            }
            else{
                putchar(c);
                putchar(d);
            }
        }
        else if (c == '\'' || c == '"'// '이거나 "이게 입력되면,
            echo(c);
        else
            putchar(c);
    }
     
    void incomment()  // 인용문으로 슬래시 별(/*) 별 슬래시(*/)을 이용할 때
    {
        int c, d;
        c = getchar();
        d = getchar();
     
        while (c != '*'|| d != '/'// 처음에 바로 */이렇게 끝나는 게 아니면!
        {
            c = d; // 입력받은 d를 계속 c에 담아 놓기 때문에 *를 호출 받고 다음에 /를 받으면 
                   // while문을 빠져나감
            d = getchar(); // getchar() 함수 호출로 계속 입력받을 수 있고, 
        }
    }
     
    void in2comment() // 인용문으로 슬래시 두번을 이용할 때
    {
        int c;
        while ((c = getchar()) != '\n')
            ;
        printf("\n");
    }
     
    void echo(int c){ // '말'안에 있는 // 나 /* 는 인용문으로 인식하지 않고
                      // 그래로 출력한다.한다.
     
        int d;
        
        putchar(c); // 말(문학에서의 인용문)일 때는 '나 "를 출력
     
        while ((d = getchar()) != c) { // d = ' || d = "이 아니면
            putchar(d);                // '말'을 출력
            if (d == '\\')            // ?? 말을 입력 받는 도중 \가 있으면 
                putchar(getchar()); // ?? 입력 받은 걸 그대로 출력 (단, ' 나 "가 오기 전 까지)
                    // 영문 풀이: Within a quoted string, if we encouter a special character, 
                    //then we try to read them literally as two characters and print them.
        }
        putchar(d);
    }
    cs

    if( d== '\\')

    putchar(getchar()); 

     이 부분의 코드 해석이 아직 불완전하다.

    [예제 1-24]

    기본적인 C문법을 검사하는 프로그램을 작성하라.

    검사 대상은 짝이 맞지 않는 괄호, 중괄호, 그리고 인용부호, escape 문자, 설명문 표시 등이다.













    댓글

Designed by Tistory.