[Kernighan - C] #1-10 외부변수
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으로 한 행을 두행으로 나눠서 출력
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 문자, 설명문 표시 등이다.