IT Japan

제 8장 - 화일 조작 / 기타의 것들 본문

IT/Programming

제 8장 - 화일 조작 / 기타의 것들

swhwang 2016. 3. 22. 00:15
반응형

새내기 강좌란내 'C언어를 시작하자!'의 마지막회인 8장에서는 컴퓨터에 

서 빼놓을 수 없는 화일 처리에 대하여 알아보고, 7장에 걸쳐 강좌를 진행 

해 오면서 여러분들에게 언급하지 않은 C의 선행처리기, 명령행 인자, typ 

edef 등에 대하여 설명할 예정이다. 


사실, 이 강좌에서 언급하지 않는 기억장소 할당, 다차원 배열포인터, 원 

/근거리 포인터, 함수 포인터 등의 포인터의 깊은 곳과 어셈블리 접속 등 

도 C의 한 요소이지만, 이 강좌의 성격과 맞지 않아서 생략한다. 하지만, 

이 강좌에서 언급한 것들만 완전히 이해했다면, 초급자 수준을 벗어났다고 

할 수 있다. 



1. 입출력의 종류 


화일을 대상으로 하는 작업의 종류는 쓰기, 추가, 읽기의 3가지로 나눌 

수 있다. 


* 쓰기 - 화일을 새로 만드는 것인데, 이미 같은 화일이 존재하면 기존의 

자료는 모두 지워진다. 

* 추가 - 화일 뒷부분에 새로운 자료를 추가하기 위해 사용되는데, 선택 

한 화일이 발견되지 않으면 그 자체로 새로운 화일이 형성된다. 

* 읽기 - 이미 만들어진 화일에서 자료를 읽어들이는 것으로서, 해당 화 

일이 존재하지 않으면 에러를 발생시킨다. 


이 세가지 이외에도 '읽기/쓰기 겸용', '읽기/추가 겸용'의 형태를 지원 

한다. 



2. 입출력의 절차 



화일을 대상으로 작업하는 경우에는 반드시 다음의 절차를 거쳐야 한다. 


(1) 화일 open - 입출력 대상이 되는 화일명과 mode를 지정하는 절차 

(화일 입출력을 위한 버퍼를 할당하게 된다) 

(2) 입출력 수행 

(3) 화일 close - 입출력 과정을 끝낸 다음에는 반드시 이 과정을 실행 

시켜 (1)에서 할당된 버퍼를 해제해야 한다. 화일을 

닫지 않은채 프로그램의 실행을 종료시키면 화일이 깨 

지는 경우가 있으므로 주의 하여야 한다. 



3. 화일 처리에 관한 함수 


1) 화일의 개방 


.선언문 FILE *fp : 화일 구조체 포인터 변수를 선언한다. 이 함수는 변수 

의 선언 부분(보통 선언부의 맨 끝)에서 정의한다.(fp는 임의 

변수임) ===> 에 화일에 관한 자료들을 저장할 공간 

이 구조체 'FILE'로 선언되어 있다. 



.fp=fopen("화일명","mode") : 화일을 오픈하고 화일 포인터를 초기화한 

다. 화일 포인터는 자료가 화일 내의 어느 부분에 위치해 있는 

지를 기억하는 변수로서 자료를 읽거나 쓸 때마다 자료의 크기 

만큼 뒤로 이동한다. 화일 오픈에 실패하면 이 함수의 값은 nu 

ll pointer가 된다. mode는 아래의 모드가 있다. 


<화일포인터 위치> 

"r" : 화일을 읽기 전용으로 연다(read) 화일 처음 

"w" : 화일을 새로이 생성하고 쓰기 전용으로 연다(write) 화일 처음 

"a" : 화일을 추가 전용으로 연다(append) 화일 끝 

"r+" : 화일을 갱신용으로 연다(읽기,쓰기 모두 가능) 화일 처음 

"w+" : 화일을 새로이 생성하고 갱신용으로 연다 화일 처음 

"a+" : 화일이 없는 경우 새로이 생성하고 갱신용으로 화일 끝 

연다 


모드지정문자 뒤에 t나 b를 붙여 텍스트화일과 이진화일임을 지정할 수 

있다. 텍스트 화일은 ASCII코드를 이용하여 문자로써 저장된 화일을 말 

하고, 이진 화일은 기계어코드를 저장할 때 사용되는 화일 형식이다. 


.freopen("화일명","mode",fp) : fp가 가리키는 화일을 닫고 새로운 모드 

로 다시 연다. 이 함수는 이미 오픈된 함수의 모드를 바꾸는 

경우에 이용한다. 



2) 화일의 종결 


.fclose(fp) : fp로 지정한 화일을 닫는 함수이다. fclose()함수는 성공적 

인 패쇄이면 0을, 그렇지 못하면 -1을 반환한다. 



3) 화일 입출력 함수 


.fputs("문자열/변수",fp) : 문자열을 화일 포인터가 가리키는 위치에 수 

록한다. 문자열을 수록한 다음에는 화일 포인터 fp가 문자열의 

길이만큼 뒤로 이동한다. 


.fputc(fp,'문자') : 문자 1개를 화일에 출력한다. 


.fprintf(fp,"서식제어문자열",변수,..) : 변수의 내용을 서식제어 문자열 

의 포맷에 맞추어 화일에 출력한다. 출력 매체가 화일이라는 

것만 다를 뿐 printf()함수와 용법이 같다. 


.fgets(변수,길이,fp) : 화일 포인터가 가리키는 위치에서 지정된 (길이-1 

)만큼 자료를 읽어 변수에 할당한다. 변수의 마지막에는 '\0' 

이 추가되는데 화일의 자료 내부에 '\n'이 있거나 화일이 끝날 

경우에는 그 시점까지만 읽어들인다. 화일에서 지정된 길이만 

큼 읽어들일 때는 수록된 문자열의 길이보다 1문자 더 크게 지 

정해야 한다. 


.fgetc(fp) : 화일에서 한 문자를 읽어 들인다. (내장함수이다) 


.getc(fp) : 화일에서 한 문자를 읽어 들인다. (매크로함수이다) 


.fscanf(fp,"서식제어 문자열",변수,..) : 화일에서 서식제어문자열의 포 

맷대로 자료를 읽어들여 변수에 할당한다. 입력매체가 화일이 

라는 것만 다를 뿐 scanf()함수와 동일하다. 


.fread(변수,길이,갯수,fp) : 화일 포인터의 현재 위치에서 지정된 길이만 

큼 지정된 갯수를 읽어들인다. 화일 끝이 검출되면 그 시점에 

서 읽어들이는 것을 멈춘다. 함수의 값은 정상 수행인 경우에 

는 읽어들인 갯수가 되며, 화일의 끝을 검출했을 경우에는 0이 

된다. 


.fflush(fp) : 버퍼 내의 자료를 비운다. 디스크에 수록할 자료가 있으면 

디스크에 수록할 자료가 있으면 디스크에 수록한 후 버퍼를 비 

운다. 보통은 자료의 수록을 확실히 하기 위하여 사용된다. fc 

lose()함수가 수행되면 자동으로 fflush()함수가 수행된다. 


.feof(fp) : 화일의 끝인지를 판정한다. 화일의 끝이 아니면 0 이외의 값 

이 되고, 끝이면 0이 된다. 


.ftell(fp) : 화일 포인터의 위치를 구한다. 


.fseek(fp,상대위치,모드) : 화일 포인터의 위치를 변경한다. 상대 위치는 

수치 다음에 'L'을 붙이고, 수식인 경우에는 붙이지 않는다. 

모드는 아래 세 가지 중 하나가 된다. 


0: 화일의 시작부터 상대 위치를 계산한다. 

1: 현재의 화일 포인터부터 상대 위치를 계산한다. 

2: 화일의 끝부분부터 상대 위치를 계산한다. 


<예제1> fseek()의 사용 예 


fseek(fp,5L,0); : fp를 화일의 시작위치에서 5바이트번째로 이동 

fseek(fp,2L,1); : fp를 현재의 위치에서 2바이트 이동 

fseek(fp,-1L,2); : fp를 화일의 끝에서 1바이트 전의 위치로 이동 


이 때 함수의 수행이 성공적이면 변경된 화일 포인터 위치가, 실패하면 

-1이 구해진다. 



4. 화일 처리에 관한 예제들 



<예제2> 텍스트 화일을 읽어 화면에 출력하는 프로그램 작성 


<리스트1> 한 글자씩 읽어 한 글자씩 출력하는 프로그램 

( fopen()함수안의 화일명은 현재 디렉토리내에 있어야 한다 ) 

#include 

main() 

FILE *in; /* 화일에 대한 포인터를 선언 */ 

int ch; 


if ((in=fopen("c:hello.c","r")) != NULL) { 

/* in에 null pointer가 되돌려 지지 않는다면 */ 

/* 즉, 화일이 성공적으로 개방이 되면 */ 


while ((ch=getc(in)) != EOF) /* 화일에서 한 문자를 읽어 */ 

/* ch변수에 저장한다. 그리 */ 

/* 고 화일끝(EOF)이 아니면,*/ 

putc(ch,stdout); /* 표준 출력(화면)에 ch 출력 */ 

fclose(in); 

else printf("file not found..\n"); 


<리스트2> 한 라인씩 읽어 한 라인씩 출력하는 프로그램 

#include 

#define MAX 80 

main() 

FILE *fpt; 

char str[MAX]; 


fpt=fopen("c:hello.c","r"); 

while (fgets(str,MAX,fpt) != NULL) 

puts(str); 


<예제3> 다음 자료를 키보드에서 입력하여 화일로 작성하는 프로그램을 

작성하여라. 

======================================= 

이 름 주 소 전화번호 

======================================= 

홍 길동 서울시 중구 신당동 432-3453 

이 순신 경남 진해시 89-3455 

김 유신 전북 부안군 34-1123 

x <- (자료의 끝 표시) 


<리스트3> 1:#include 

2:main() 

3:{ 

4: char name[8],addr[21],tel[9]; 

5: int n=0; 

6: FILE *fp; 

7: fp=fopen("SAMPLE.DAT","a"); 

8: do { 

9: printf("%2d. ",++n); 

10: printf("성명 : "); gets(name); 

11: if (name[0] == 'X' || name[0] == 'x') break; 

12: printf(" 주소 : "); gets(addr); 

13: printf(" 전화 : "); gets(tel); 

14: fputs(name,fp); fputc('\n',fp); 

15: fputs(addr,fp); fputc('\n',fp); 

16: fputs(tel,fp); fputc('\n',fp); 

17: } while (1); 

18: fclose(fp); 

19:} 


#화일에 자료가 수록되는 형태: (화일내에서 '\n은 2바이트로 취급된다) 

+------------------------------------------------------- 

|홍 길동\n서울시 중구 신당동\n432-3453\n이 순신\n... 

+------------------------------------------------------- 


위 프로그램의 14행 - 16행에서 보면, fputs()함수에서는 문자를 화일에 

수록 할때 각 문자열의 끝에 아무런 표시도 해주지 않으므로 자료를 구분 

하기 위해서 fputc()를 이용해 자료구분 문자의 하나인 '\n'문자를 추가하 

였다. 


<예제4> 위에서 구축한 화일을 읽어 화면에 출력하는 프로그램 작성 


<리스트4> 

#include 

main() 

char name[8],addr[21],tel[9]; 

int n=0; 

FILE *fp; 

fp=fopen("SAMPLE.DAT","r"); 

printf("============================================\n"); 

printf("번호 이 름 주 소 전화번호\n"); 

printf("============================================\n"); 

do { 

fgets(name,25,fp); 

if (feof(fp)) break; /* 화일 끝이 검출되면 중지 */ 

fgets(addr,25,fp); 

fgets(tel,25,fp); 

printf("%2d %-11s%-19s%8s\n",++n,name,addr,tel); 

} while (1); 

printf("============================================\n"); 

fclose(fp); 


위 프로그램을 실행시키면 데이터 화일내의 '\n' 때문에 출력결과가 이상 

하게 나온다. 위의 12, 14, 15행을 아래와 같이 각각 변경시키자. 


fgets(name,25,fp); name[strlen(name)-1]='\0'; 

fgets(addr,25,fp); addr[strlen(addr)-1]='\0'; 

fgets(tel,25,fp); tel[strlen(tel)-1]='\0'; 



5. 순차화일과 랜덤화일 



화일은 자료의 편성방법에 따라 순차화일(Sequential file)과 랜덤화일(R 

andom file)로 구분된다. 순차화일은 모든 자료가 발생한 순서대로 저장되 

고, 편성된 화일은 순차적으로 읽혀진다. 또한 화일 내의 특정한 데이터를 

갱신할 때에는 일단 화일을 모두 읽어들여 필요한 부분을 수정하고, 다시 

처음부터 차례대로 디스크에 수록하는 화일 형태이다. 이러한 화일은 자료 

의 갯수가 적은 경우나 문서 화일, 자료의 변동이 거의 없는 고정형 자료 

인 경우에 유리하다. 

랜덤화일은자료를 일정한 크기(레코드)에 맞추어 저장한 다음, 레코드 

단위로 입출력하는 형태의 화일이다. 이 것은 화일내의 특정 자료를 수정 

하는 경우 원하는 레코드만 읽어들여 수정한 다음 다시 그 자리에 기록하 

는 것이다.( 좀 더 개선된 경우는 인덱스 화일이라는 색인 화일을 만들어 

데이터를 검색하기 편하도록 한 것이 있다 ) 

C에서 랜덤 화일의 처리를 위한 함수로는 fprintf(),fseek(),fread(),fte 

ll() 등이 있다. 


<예제5> <리스트3>의 프로그램을 랜덤화일 형식으로 작성하라. 


<리스트5> #include 

main() 

char name[8],addr[21],tel[9]; 

int n=0; 

FILE *fp; 

fp=fopen("SAMPLE1.DAT","a+"); 

do { 

printf("%2d. 성명:",++n); gets(name); 

if (name[0]=='X' || name[0]=='x') break; 

printf(" 주소:"); gets(addr); 

printf(" 전화:"); gets(tel); 

fprintf(fp,"%-11s%-19s%-8s\n",name,addr,tel); 

} while (1); 

fclose(fp); 


#화일에 자료가 수록되는 형태: (1레코드당 40byte : \n은 2Byte로 처리) 

+------------------------------------------------------------ 

|홍 길동 서울시 중구 신당동 432-3453\n이 순신 .. .. 

+---------------------------------------+-------------------- 


fprintf()함수는 따로 변수에 대한 '구분을 해주지않고' 서식제어 문자열 

대로 화일에 써 주는 함수임을 명심하라. 


<예제6> 랜덤 화일을 화면에 출력하는 프로그램 

정수 n을 입력하여 n번째 레코드를 읽어서 화면에 표시하라. 


<리스트6> #include 

main() 

char name[8],addr[21],tel[9]; 

int n; 

FILE *fp; 

fp=fopen("SAMPLE1.DAT","a+"); 

do { 

printf("번호입력:"); 

scanf("%d",&n); 

if (n<1) break; 

fseek(fp,40*(n-1),0); 

fgets(name,12,fp); 

fgets(addr,20,fp); 

fgets(tel,9,fp); 

printf("성명:%s\n주소:%s\n전화번호:%s\n", 

name,addr,tel); 

} while (1); 

fclose(fp); 


C에서는 현재 레코드를 읽어들일 경우 현재의 화일 포인터 위치를 읽으며 

, 화일 포인터는 읽어들인 자료의 크기에 맞추어 자동으로 증가된다. 그러 

나 다른 위치에 있는 레코드를 읽어들일 경우에는 프로그래머가 직접 화일 

포인터를 이동시켜야 한다. fseek()함수로 읽어들일 화일 포인터의 위치를 

지정할 때는 '(읽을 레코드 번호-1)*레코드당 바이트수'로 지정하여야 한 

다. 

위 프로그램에서 fgets()함수의 길이를 쓸 때는 반드시 읽어 들일려는 문 

자보다 1 많게 설정하여야 한다. 자세한 사항은 위에 기술한 화일 입출력 

함수 부분을 확인하기 바란다. 

fscanf()함수를 이용하여 화일을 읽어들일 경우에는 fscanf()의 특성상 

자료내에 포함된 공백을 그 자료의 끝으로 판단하여 예기치 않은 결과가 

발생할 수도 있으니 주의해야 한다. 




이것으로 화일 처리에 관한 설명을 마치겠다. 화일 처리 중에서도 기본적 

인 것만 언급하였다. 사실 화일 처리는 자료 구조와 연계되어 알고리즘의 

한 분야를 차지하고 있다. 나중에 시간이 나면 자료구조를 공부해 보는 것 

도 좋을 성 싶다. 








##### 가변인수를 사용하는 함수 작성법 ##### 



가변인수를 사용하는 대표적인 함수로는 printf가 있습니다. 이러한 가변 

적인 갯수의 인수를 사용할 수 있는 방법은 다음과 같습니다. 


void va_start(va_list param, lastfix); 

type va_arg(va_list param, type); 

void va_end(va_list param); 


위 함수들은 가변적인 갯수의 인수를 사용할 수 있게 해주는 함수들이다. 

선언은 화일에 되어 있습니다.. 


va_list param; 


- va_arg()함수와 va_end()함수에서 사용할 정보를 포함하고 있는 배열 

호출된 함수가 가변적인 갯수의 인수를 사용하는 경우에는 이 인수의 

데이터형을 va_list형으로 선언해 주어야 한다. 


va_start(va_list param, lastfix); 


- 인수 param이 호출함수로 부터 인도된 첫번째 인수를 지정하게 한다. 

lastfix는 호출함수의 인수중 "고정적인 마지막 인수"이다. 

예를 들어 함수 func(arf1, argf2, argf3, argv1, argv2, ...);에서 

argf1..argf3는 반드시, 항상 사용되는 인수이고, argv1, argv2, ... 

는 사용할 수도 있고, 않을 수도 있는 인수라면 lastfix로 argf3을 

지정한다. 


va_arg(va_list param, type); 


- 호출함수로부터 인도된 인수를 구하고 param이 다음번 인수를 가르키 

게 한다. 즉, 첫실행때는 호출함수로부터 인도된 첫번째 가변적인 인 

수(lastfix다음번 인수)를 구하고, 두번째 실행시에는 두번째 가변적 

인 인수를 구한다. 


va_end(va_list param); 


- 호출함수가 정상적으로 호출한 함수로 복귀할 수 있도록 한다. 이 함 

수가 실행되면, 나머지 인수를 더이상 사용하지 않는다. va_arg()함 

수가 인도된 모든 인수를 다읽은 경우에는 반드시 이 함수를 실행시 

켜줘야 한다. 


자세한 내용은 레퍼런스 가이드를 참고하시기 바랍니다. 실제 사용예를 

보시면 이해할 수 있을 것입니다. 



<실제 사용예> 정수 임의개를 받아 16진수로 화면에 출력하는 프로그램=exam.c=#include /* 가변함수 지원함수 선언 헤더 */ 

#include /* printf()함수 선언 헤더 */ 

#include /* clrscr()함수 선언 헤더 */ 


void printxy(int fmt, ...); /* 가변인수 함수 선언 .의 갯수는 3개 */ 


void main(void) 

clrscr(); 

printxy(10, 10, 20, 30, 0); /* 인수의 갯수는 가변적이다 */ 


void printxy(int fmt, ...) /* 인자는 반드시 하나는 있어야 함 */ 

int i; 

va_list ap; /* 인수리스트를 저장할 배열(ap)을 va_list형으로 선언*/ 



va_start(ap, fmt); /* ap가 호출함수로 부터 인도된 첫번째 인수를 */ 

/* 지정하게 한다. fmt는 호출함수의 인수중 */ 

/* 고정적인 마지막 인수이다. 고로 이 함수는 */ 

/* 최소 한개이상의 인수를 가진다 */ 


while((i=va_arg(ap, int)) != 0) printf(" %d=%x\n", i, i); 


/* 각 인자를 하나씩 추출하여 i로 되돌리고, */ 

/* 인자가 0이 될때까지 16진수로 변환하여 출력 */ 


va_end(ap); /* 호출함수가 정상적으로 복귀할 수 있도록 한다 */ 


참고로 위의 main()함수내의 printxy(10,10,20,30,0);에서 처음의 10은 반 

드시 있어야 하는 값이고, 마지막의 0은 인수리스트가 끝났다는 것을 표시 

하기 위한 값입니다. 이러한 가변 인수를 사용하는 함수는 반드시 고정적 

인 인자 하나는 있어야 합니다. (printf()함수를 생각해 보십시요) 

그리고 while문에서 인자가 끝난다는 표식으로 마지막에 0을 사용하였습니 

다. va_arg()함수는 가변인자를 되돌림으로 가변인자가 끝났다는 표식이 반 

드시 있어야 합니다. 

위의 printxy(10, ................ , 0); 에서 ....... 부분에 원하는 인 

자를 넣으면(갯수상관없슴) 출력할수가 있습니다. 


6. C의 선행처리기 


선행처리기란 문자 그대로 프로그램이 컴파일 되기 전에 처리되는 명령어 

들을 말하는데, 이는 프로그래머의 프로그래밍 효율을 높혀 주므로 알아둘 

필요가 있다. 



(1) 매크로(macro) 


매크로란 일련의 문자, 상수, 수식을 하나의 식별자(문자열)로 처리하는 

것으로서, 이렇게 정의한 후에는 식별자만 기술하면 컴파일 과정에서 지정 

한 수나 수식으로 전개된다. 매크로를 이용하면 복잡한 수식을 간단하게 

기술할 수 있으며, 프로그램의 수정시에도 매크로만 새로 정의하면 되므로 

용이하다. 매크로는 변수와 구별하기 위해서 보통 대문자로 기술하며, 그 

형식은 다음과 같다. 


#define 매크로명 대체문자열 ex> #define PIE 3.14159265 


매크로 정의는 #define다음에 하고, 매크로명은 변수의 규정에 맞는 문자 

열이면 정의 횟수에 제한없이 사용할 수 있다. 매크로 정의 다음에는 ; 을 

기술하지 않는다. 


<리스트7> 

#define PRINT printf 

#define MAX 999 

#define AND && 

#define OR || 

#define PIE 3.14159 

#define AREA 4*r*r*PIE /*매크로를 매크로 문자열로 사용*/ 

#define VOL 4.0/3*pow(r,3.0)*PIE /*구의 부피를 구하는 공식*/ 

#define SWAP(a,b) {int t; t=a; a=b; b=t;} 

/*두 변수의 값을 바꿔주는 매크로 함수*/ 

#define LARGE(a,b,c) { if (a if (a if (b /*세개의 수치를 정렬하는 매크로 함수*/ 


<리스트7>에서 보듯이 매크로는 단순한 상수나 수식을 치환하는 것으로부 

터 복잡한 문장을 치환하는 것에 이르기까지 매우 다양하게 정의할 수 있 

다. 따라서 매크로를 단순한 치환의 경지에서 벗어나 하나의 함수로 정의 

해 활용하기도 하며 실제로 C에서는 매크로가 하나의 함수로 취급된다. 위 

에서 정의한 SWAP()나 LARGE()도 매크로 함수이다. 


C의 내장 함수에는 일반 함수와 매크로 함수의 두 가지가 있는데, 일반 

함수는 그 실체가 라이브러리(*.lib)에 있으며 링크시에 프로그램에 결합 

된다. 이들 함수는 프로그램의 여러 곳에 나타나더라도 그 본체는 링크시 

에 한 번만 결합되므로 프로그램의 크기가 거의 변하지 않는다. 그러나 함 

수가 호출될 때마다 함수 본체가 있는 부분을 찾아가기 때문에 실행시간이 

다소 떨어진다. 

매크로 함수는 실체가 헤더 화일(*.h)에 있으며, 그 실체는 매크로로 정 

의되어 있으므로 컴파일시에 그 함수가 호출된 부분에서 전개된다. 따라서 

매크로 함수는 호출될 때마다 각각 전개되어 프로그램의 크기가 그만큼 커 

지지만 실행속도는 좀 더 빨라진다. 하지만 실행속도의 차라는 것은 인간 

이 거의 느낄 수 없으므로 그렇게 남용하지는 않는다. 


매크로는 상당히 편한 기능이지만 매크로 정의를 잘못하면 부작용을 일으 

킬 수 있으므로 주의하여야 한다. 


<리스트8> #define MAX 256 

#define BUF 10+MAX 

........ 

main() 

... 

k=BUF * 10; 

... 


<리스트9> #define SQUAR(x) x*x 

......... 

main() 

... 

a=2; b=3; 

k=SQUAR(a+b); 

... 


<리스트8>에서 BUF는 '(10+256)'으로 전개되지 않고 '10+256'으로 전개된 

다. 그러므로 결국 이식은 'k=10 + 256 * 10'이 된다. 그러므로 본래 의 

도대로 'k=(10 + 256) * 10'으로 할려면 매크로를 아래와 같이 수정해야 

한다. 

#define BUF (10 + MAX) 


<리스트9>에서 SQUAR는 (a+b)*(a+b)로 전개되지 않고 'a+b*a+b'로 전개되 

어 의도와는 다른 결과가 출력된다. 그러므로 'k=(a+b)*(a+b)'의 의도라면 

다음과 같이 괄호까지 정의해야 한다. 

#define SQUAR(x) (x)*(x) 


매크로를 함수의 형태로 쓸 경우에는 지적한 바와 같이 인수와 체부를 괄 

호로 묶어주는 것이 안전하다. 매크로는 문자열이 그대로 전개되는 것이지 

그 값이 전개되는 것이 아니라는 사실을 명심해야 한다. 그러므로 앞에서 

예로 든 #define BUF (10+MAX)의 경우도 #define BUF (10+(MAX))와 같이 

전개되어야 할 것이다. 


사용이 끝난 매크로는 취소가 가능하다. 


<형식> #undef <매크로명> ex> #undef SQUAR 


그러나 매크로는 호출시에만 전개되며, 매크로의 갯수가 제한된 것도 아 

니므로 굳이 취소할 필요는 없다. (매크로의 취소는 이미 정의된 매크로의 

값을 바꾸어 다른 값을 할당하려는 경우에 이용된다. 



(2) 화일의 포함: #include 


선행처리기가 #include를 만나면 그 다음에 오는 화일을 포함시킨다. 


#include 

#include "myheader.h" 

#include "c:\tc\data\sample.h" 


< >부호는 표준 시스템 디렉토리에서 화일을 찾아 포함시키고, " "부호는 

현재 그 화일이 있는 디렉토리(또는 정해진 디렉토리)에서 화일을 찾아 포 

함시킨다. 

보통 헤더화일을 포함시키는데 보통 내장 함수들이 선언된 시스템에서 제 

공하는 헤더 화일들은 <>로 포함시키고, 사용자가 자체적으로 제작한 헤더 

화일은 ""로 포함을 시킨다. 


<예제7> 사용자 헤더 화일의 사용 예제 


== bool.h 화일 제작 == 

#define BOOL int 

#define TRUE 1 

#define FALSE 0 


== sample.c 화일 == 


#include 

#include "bool.h" 


main() 

int ch; 

int count=0; 

BOOL whitesp(); 


while ( (ch=getchar()) != EOF ) 

if (whitesp(ch)) count++; 

printf("There are %d whitespace characters.\n", count); 


BOOL whitesp(c) 

char c; 

if (c == ' ' || c == '\n' || c == '\t') 

return(TRUE); 

else return(FALSE); 



(3) 그 외의 선행 처리기 (#if, #ifdef, #ifndef, #else, #endif) 


이것들은 조건적인 컴파일을 가능하게 해 주는데, 대형 프로그램에서 주 

로 사용된다. 


#ifdef MAVIS 


#include "horse.h" 

#define STABLES 5 


#else 


#include "cow.h" 

#define STABLES 15 


#endif /* if 블럭이 끝남을 알리는 기능을 한다. */ 


#ifdef지정어는 그 다음에 오는 명칭(MAVIS)이 선행처리기로 정의되었으 

면, 그 지점에서 다음에 오는 #else나 #endif이전까지에 있는 명령들을 처 

리하게 한다. 즉, #ifdef은 다음의 명칭이 정의되었으면 참의 판단을 하고 

#ifndef은 다음의 명칭이 정의가 안 되었으면 참의 판단을 내린다. 


#if SYS == "IBM" /* #if는 if문과 기능이 비슷하다 */ 

#include "ibm.h" 

#endif 


이러한 조건 컴파일의 용도는 프로그램의 호환성을 높이기 위한 것이다. 

화일 처음에 있는 몇 개의 중요한 정의만 바꾸면 다른 시스템을 위한 다른 

화일이 포함되고 다른 값들이 설정된다. 




7. typedef 



typedef문은 프로그래머 자신만의 수형명을 만들어 준다. #define과 기능 

이 비슷하지만 아래와 같은 차이점이 있다. 


1) #define와는 달리, 단지 데이터 수형에만 기호적인 명칭을 사용할 수 

가 있다. 

2) typedef는 선행처리기에 의해 처리되는 것이 아니라 컴파일러에 의해 

처리되는 내장 명령어이다. 


<리스트10> 


typedef float REAL; /* float수형명을 REAL으로 사용할 수 있다 */ 

typedef char *STRING; 


main() 

REAL a=12.3243; 

STRING name="We are the world!"; 

... 


위에서 STRING name;은 char *name;과 같다. 

또한 구조체에 대해서도 사용할 수가 있다. 


typedef struct CompLex { 

float real; 

float imag; 

} COMPLEX; 


main() 

COMPLEX son; 

... 




(1) 자주 사용되는 수형에 대해서 인식하기 쉬운 명칭을 제공하기 위함 

이다. 

(2) 복잡한 수형명을 간단하게 하기 위해서이다. 

(3) 프로그램의 이식을 용이하게 하기 위해서이다. 



(2)의 경우의 예를 들면, typedef char *FRPTC () [5];는 FRPTC가 char 

5개로 구성된 배열을 가리키는 포인터를 반환하는 함수의 수형임을 나 

타낸다. ( FRPTC flump;는 char *flump()[5];와 같다. ) 

포인터의 깊은 곳으로 들어가면 아주 복잡하고 헷갈리는 선언들이 많 

이있다. 이 글의 성격과는 맞지 않으므로 자세한 설명은 피하겠다. 


(3)의 경우의 예를 들면, 프로그램이 16비트 숫자를 사용할 필요가 있 

을때, 어떤 시스템(OS에 따라)에서는 short가 어떤 시스템에서는 int 

가 16비트이다. 그 때 프로그램의 첫머리에 typedef int TWOBYTE;로 

선언해 두고 int를 short로만 바꾸어 주면 프로그램 이식을 간편히 

할 수가 있다. 




8. 명령행 인자(Command-line arguments) 



아래 한글 사용시 도스에서 hwp c:\data\letter.hwp와 같이 명령을 내리 

면 프로그램을 시작과 동시에 letter.hwp화일이 뜨는 것을 알고 있을 것이 

다. 이러한 것을 어떻게 구현할까? 


명령행이란 프로그램을 실행시키기 위해 명령어를 입력해 넣는 행을 말한 

다. 

우리가 보통 프로그램을 작성할 때 main()이라고만 기술하였는데, main() 

함수도 인자가 존재한다. 아래 예제를 보자. 


<예제8> 명령행 인자를 사용한 프로그램 - I 


<리스트11> 화일명을 copystr.c라고 하자. 

#include 

main(argc, argv) 

int argc; 

char *argv[]; 

int c; 

for (c=1;c < argc;c++) 

printf("%s",argv[c]); 

printf("\n"); 


위 프로그램을 컴파일 시킨 후, 명령행에서 copystr My name is robocap. 

이라고 입력하면, My name is robocap.이라고 출력한 후 프로그램을 종료 

한다. 

main()함수는 두 가지 인수를 가진다. 첫번 째 인자(argc)는 명령어에 따 

르는 문자열의 갯수를 나타낸다. argc는 argument count의 약자로써, 다른 

인자명도 사용할 수 있으나 관례적으로 arg> 


--------------------------------------------------------------------------------


Transfer interrupted!


?하는데, 위의 예제는 5개의 문자열을 가진다. 

두번째 인자(argv : argument value)는 문자열 포인터 배열이다. 명령행 

의 각 문자열은 그 자신에 대한 포인터로 배열에 저장된다. 위의 경우, 


argv[0]은 프로그램명 copystr을 가리킨다. 

argv[1]="My" argv[4]="robocap."을 각각 가리킨다. 

argv[2]="name" 

argv[3]="is" 


<예제9> <#1편>의 <예제2>를 명령행 인자를 사용하는 융통성 있는 프로그 

램으로 바꿔 보자. 


<리스트12> 프로그램명이 son.c라면, 

#include 

main(int argc,char *argv[]) 

FILE *in; 

int ch; 


if (argc==1) printf("Usage: son filename.ext\n"); 

else { 

if ((in=fopen(argv[1],"r")) != NULL) { 

while ((ch=getc(in)) != EOF) 

putc(ch,stdout); 

fclose(in); 

else printf("file not found..\n"); 




9. 강좌를 끝내며.. 



축하합니다! 당신은 이제 C의 초보자 딱지를 떼고 중급자로 도약할 수 있 

는 발판을 마련하셨습니다. 끝까지 강좌를 쫓아와 주신 것에 대해서 정말 

감사드립니다. 그리고 그 끈기에 박수를 보냅니다. 짝짝짝짝짝! 


반응형

'IT > Programming' 카테고리의 다른 글

Python(자료형)  (0) 2018.01.24
[Python]Python으로 OpenCV를 사용  (0) 2017.06.07
제 7장 - 구조체와 공용체  (0) 2016.03.22
제 6장 - 배열과 포인터  (0) 2016.03.22
제 5장 - 함수 / 기억부류  (0) 2016.03.22
Comments