전기전자공학/프로젝트

[Kernighan - C] #1-10 외부변수

LinZBe4hia 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 문자, 설명문 표시 등이다.