C 언어는 컴퓨터에 올려져서 운영체제의 여러 기능을 이용한다. C 언어 시스템은 라이브러리를 가지고 있는데, 예를 들면 기본적인 산술 함수들은 산술 라이브러리에서 제공되고, 입출력함수들은 표준 라이브러리에서 제공된다. 이 장에서는 printf()와 scanf()를 포함한 표준적인 입출력을 자세히 설명하기로 한다.
1. 출력함수
우리가 자주 사용하는 출력함수는 printf()이다. 이것의 장점은 크게 두 가지로 볼 수 있는데, 첫째는 인자 리스트를 구성할 때 길이에 제약이 없다는 것이고, 둘째는 출력의 제어가 간단한 변환형식(format)에 의해 이루어진다는 것이다. 즉, 사용자가 직접 필요한 변환을 하지 않아도 된다는 것이다. printf() 함수는 표준출력 파일 stdout에 문자 스트림을 전달한다. printf()의 매개변수 리스트는 제어문자열과 인자 리스트의 두 부분으로 이루어진다. 다음의 함수 호출
printf("she sells %d %s for $ %f", 99, "sea shells", 3.77);
의 제어문자열과 인자 리스트는 다음과 같다.
제어문자열 : "she sells %d %s for $ %f"
인자리스트 : 99, "sea shells", 3.77
인자 리스트는 쉼표로 분리되는 식의 리스트이다. 이 식은 제어문자열에 있는 변환형식에 따라 계산되고 변환된다. 이렇게 한 후 출력 스트림에 놓여진다. 변환형식이 아닌 제어문자열 내의 문자들은 본문 그대로 바로 출력 스트림에 놓여진다. % 기호는 변환형식의 시작을 나타낸다. 하나의 변환형식은 % 기호로 시작해서 변환문자로 끝나는 문자열이다.
변환문자 |
의미 |
예 |
d |
십진 정수 |
45 |
o |
8진 정수 |
55 |
x |
16진 정수 |
2d |
u |
무부호 십진 정수 |
45 |
e |
지수부가 있는 부동 소숫점 |
-7.001100e+03 |
f |
지수부가 없는 부동 소숫점 |
-7001.100000 |
g |
e 혹은 f 중 짧은 것이 선택됨 |
-7001.1 |
c |
단일 문자 |
k |
s |
문자열 |
Pacific |
다음의 예
printf("she sells %d %s for $ %f", 99, "sea shells", 3.77);
에서 제어문자열에 있는 변환형식을 각각의 인자들에 대응시키면 다음과 같다.
%d 99
%s "sea shells"
%f 3.77
입출력자료의 점유폭, 소수부의 길이, 정렬방식 등과 같은 상세한 형식정보가 변환형식에 명시될 수 있다. 만약 형식정보를 생략하면 시스템이 정의한 방식으로 처리된다. 예를 들어 앞의 인자 3.77에 대응하는 변환형식이 %f 이므로 3.770000이 출력된다. 소숫점 이하 여섯자리까지가 생략시의 값으로 출력된다. 형식정보는 % 기호와 변환문자 사이에 오며 나타날 수 있는 차례에 따라 살펴보면 다음과 같다.
ㅇ 음 부호(-) : 변환되는 인자가 해당영역에서 왼쪽으로 정렬되어야 함을 나타낸다. 음 부호가 없으면 오른쪽으로 정렬된다. 인자가 출력되는 장소를 영역(field)이라고 하고, 인자를 인쇄하는데 사용된 공간의 수를 영역폭이라고 한다.
ㅇ 숫자 : 영역폭을 정의한다. 변환되는 인자가 명시된 영역폭보다 적은 문자를 가질 때에는, 우정렬 혹은 좌정렬에 따라 왼쪽 혹은 오른쪽에 공백이 채워진다. 만약에 명시된 영역폭보다 변환인자의 문자수가 더 많을 때에는 필요한 만큼 영역이 확장된다. 영역폭이 0으로 시작하면 공백 대신 0 으로 채워진다.
ㅇ 마침표(.) : 영역폭과 정확도(precision)를 분리한다.
ㅇ 숫자 : 변환인수의 정확도를 정의한다. e 혹은 f 변환에서는 소숫점 우측의 자릿수를 나타낸다. s 변환에서는 인쇄될 최대 문자수를 나타낸다.
ㅇ l 혹은 L : 다음에 오는 변환문자 d, x, o, u, 가 long형 인자에 대한 것임을 나타낸다.
영역폭 혹은 정확도에 숫자 대신 *이 사용되면 바로 다음에 나타나는 인자로부터 그 크기가 결정됨을 의미한다. 영역폭은 필요한 최소한의 공간이다. 생략시에는 인자를 표시하는 데 필요한 적절한 공간이 확보된다. 그러므로 십진 정수 값 102는 10진 변환 d나 8진 변환 o의 경우에 세 자리의 공간이 필요하다. 그러나 16진 변환 x에서는 두 자리의 공간만 있으면 된다. 출력숫자는 음 부호가 없으면 우정렬된다. 영역폭이 너무 작아 해당인자를 적절히 표시할 수 없으면 영역폭은 생략시의 크기로 증가된다. 변환되는 인자를 표시하는 데 영역의 전체가 필요하지 않을 경우, 영역의 나머지 부분은 공백으로 채워진다. 공백이 채워지는 위치는 좌정렬, 우정렬에 따라 결정된다. 영역폭의 선두에 0이 명시되면 공백 대신 0이 채워진다.
정확도를 나타내는 숫자는 마침표의 오른쪽에 나타난다. 문자열변환의 경우 이것은 인쇄될 문자 개수의 최대치이다. e나 f변환자의 경우에는 소숫점 오른쪽에 인쇄될 자릿수를 나타낸다. 앞으로 주어질 세 표는 정수, 실수, 문자열에 대한 다양한 변환형식의 효과를 보여준다.
선언문과 치환문 | |||
char a; int i,j,k; c='w'; i=1; j=29; k=230551777; | |||
형식 |
식 |
인쇄 |
비고 |
/* 따옴표는 영역의 경계를 나타내기 */ /* 위한 것일 뿐 실제로 인쇄되지는 않음 */ | |||
%c |
c |
"w" |
생랷l의 영역폭은 1 |
$2c |
c |
" " |
우정렬 |
%-3c |
c |
"w " |
좌정렬 |
%d |
c |
"119" |
생략시의 영역폭 |
%5d |
c |
" 119" |
우정렬 |
%-5d |
c |
"119 " |
좌정렬 |
%d |
i |
"1" |
생략시의 영역폭 |
%d |
-j |
"-29" |
생략시의 영역폭 |
%10d |
i |
" 1" |
우정렬 |
%010d |
i |
"0000000001" |
0으로 채워짐 |
%010d |
-i |
"-0000000001" |
0으로 채워짐 |
%-12d |
j |
"29 " |
좌정렬 |
%12o |
j |
" 35" |
우정렬 |
%-12x |
j |
"1d " |
좌정렬 |
%d |
k |
“230551777” |
생략시의 영역폭 |
%5d |
k |
“230551777” |
9칸이 필요 |
%17d |
-k |
“ -230551777” |
우정렬 |
앞의 설명에서 정확도는 소숫점의 오른쪽에 인쇄될 자릿수를 의미하거나 문자열로부터 인쇄될 무자의 수를 의미한다. 보통 정확도는 컴퓨터 안에 부동소숫점수를 저장할 때의 정확도를 말한다. float 형의 변수는 유효 숫자로 대략 6자리만 저장한다. double 형 변수인 경우에는 유효숫자가 약 16자리이다. 다음의 표는 부동 소숫점 변환형식의 사용법을 예시하고 있다. 이 표는 32비트 기계를 기준으로 한 것이다.
선언문과 치환문 | |||
float x; double y,z; x=y=333.12345678901234567890; z=-555.11111111111111111111e-9; | |||
혁식 |
식 |
인쇄 |
비고 |
/* 따옴표는 실제로는 인쇄되지 않음 */ | |||
%f |
x |
"333.123444" |
생략시의 정확도 |
%.1f |
x |
"333.1" |
정확도 1 |
%.9f |
x |
"333.123443603" |
정확도 9 |
%20.3f |
x |
" 333.123" |
우정렬 |
%-20.3f |
x |
"333.123 " |
좌정렬 |
%.9f |
y |
"333.123456789" |
정확도 9 |
%.20f |
y |
"333.12345678901234000000" |
정확도 20 |
%20.3e |
y |
" 3.331e+02" |
우정렬 |
%-20.3e |
y |
"3.331e+02 " |
좌정렬 |
%f |
z |
"-0.000001" |
생략시의 정확도 |
%e |
z |
"-5.551111e-07" |
생략시의 정확도 |
%.1e |
z |
"-5.6e-07" |
정확도 1 |
%.3e |
z |
"-5.551e-07" |
정확도 3 |
%20.9e |
z |
" -5.5511111111e-07" |
우정렬 |
%-20.9e |
z |
"-5.5511111111e-07 " |
좌정렬 |
%2e |
z |
"-5.551111e-07" |
더 많은 공간 소요 |
다음의 표는 문자열 반환형식의 사용 예이다.
선언문과 초기값 | |||
char c='w''; static char s1[]="she sells sea shells"; static char s2[]="by the sea shore"; | |||
형식 |
식 |
인쇄 |
비고 |
/* 따옴표는 실제로는 인쇄되지 않음 */ | |||
%s |
c |
/* error */ |
char는 문자열이 아님 |
%s |
s1 |
"she sells sea shells" |
생략시의 영역폭 |
%7s |
s1 |
"she sells sea shells" |
더 많은 공간소요 |
%20s |
s2 |
" by the sea shore" |
우정렬 |
%-20s |
s2 |
"by the sea shore " |
좌정렬 |
%.5s |
s2 |
"by th" |
정확도 5 |
%.12s |
s2 |
"by the sea s" |
정확도 12 |
%15.12s |
s2 |
" by the sea s" |
정확도 12, 우정렬 |
%-15.12s |
s2 |
"by the sea s " |
정확도 12, 좌정렬 |
%3.12s |
s2 |
"by the sea s" |
더 많은 공간소요 |
% 문자의 인쇄에는 %% 변환형식을 사용한다. 물론 변환형식 %c에 대응하는 인자로 ‘%’를 주어도 마찬가지이다.
2. 입력함수 scanf()
scanf() 함수는 다음의 두 특성을 보유하고 있어 융통성이 매우 높다. 첫째는 인자의 길이에 제한이 없다는 것이고, 둘째는 변환에 필요한 세부사항을 사용자가 몰라도 간단한 변환형식에 의해 입력이 제어된다는 것이다. scanf()의 매개변수 리스트는 제어문자열과 인자 리스트의 두 부분으로 구성된다. 다음의
예
char c, s1[81], s2[81];
int n;
double x;
scanf("%s%d%s%c%1f", s1, &n, s2, &c, &x);
의 제어문자열과 인자 리스트는 다음과 같다.
제어문자열 : "%s%d%s%c%1f"
인자리스트 : s1, &n, s2, &c, &x
인자 리스트는 쉼표로 분리되는 식의 리스트로 구성된다. 표준입력파일 stdin은 scanf()가 입력을 받아들이는 문자 스트림을 제공한다. 제어 문자열에 올 수 있는 것은 다음과 같다.
(1) 공백 혹은 탭 : 무시된다.
(2) 일반문자(% 제외) : 입력문자열에서 해당문자를 찾아낸다.
(3) 변환형식 : %로 시작해서 변환문자로 끝난다. 이 사이에 *, 최대 영역폭, h,i,l 이 선택적으로 올 수 있다.
변환문자 |
의미 |
d |
십진 정수 |
o |
8진 정수 |
x |
16진 정수 |
u |
무부호 십진 정수 |
e |
부동 소숫점 실수 |
f |
부동 소수점 실수 |
c |
하나의 문자 |
s |
문자열 |
[문자열] |
특수문자열 |
입력 스트림의 문자는 제어 문자열의 변환형식에 따라 값으로 변환되어 인자 리스트의 해당 포인터 식이 지정하는 장소에 저장된다. 지정억제문자(assignment suppression character) *가 변환양식에 있으면 그 입력영역은 건너뛴다. 변환문자 d, o, x, e, f 앞에 l이 올 수 있는데, 이는 long int 혹은 long float로의 변환이 일어남을 의미한다. 비슷한 방법으로 변환문자 d, o, x 앞에 h가 붙을 수 있는데, 여기서 h는 short로의 변환을 나타낸다.
문자입력의 경우를 제외하면, 입력영역은 명시된 변환에 맞는 가시문자(non-white space character)들로 구성된다. 부적절한 문자를 만나거나 규정된 영역폭이 끝나면 입력영역도 끝난다. 문자열의 입력시에는 문자열과 문자열의 끝 표시자 ‘\0’를 지정하기에 충분한 메모리 공간이 할당되었다는 가정하에서 입력이 이루어진다. float 형의 값은 %e 혹은 %f 형식으로 입력된다. 변환문자 e와 f는 서로 동등하다. 입력 스트림의 부동 소수점 수치는 e 혹은 E 다음의 숫자로 표시되는 지수부를 가질 수 있다.
제어문자열에 % 외의 일반문자가 사용되면 입력 스트림에서 그 문자를 찾아낸다. 입력 스트림의 % 문자를 무시하려면 %%를 사용하면 된다. 함수 scanf()는 성공적인 변환의 개수를 반환한다. 파일끝(end-of-file) 표시를 만나면 EOF의 값이 반환된다. 보통 이 값은 -1이다. 성공적으로 이루어진 반환이 없을 경우 0의 값이 반환된다. 입력이 변환명세와 일치하지 않으면 입력은 즉시 중지된다.
3. 관련함수 : fprintf(), sprint(), fscanf(), sscanf()
함수 printf()는 stdout 파일에 출력한다. 함수 fprintf()는 파일 포인터를 첫 인자로 가져야 한다. 나머지 인수들은 printf()의 것들과 같다. 특히 다음의 두 문은 동등하다.
fprintf(stdout, …);
printf(…);
stdout는 기정의된 파일 포인터형의 식별자로서 다음 절에서 다시 설명한다. 이와 비슷하게 sprintf(s, …)는 파일 stdout 대신에 문자열 s에 출력한다. 이 문자열은 출력을 저장하기에 충분한 크기이어야 한다.
함수 fscanf()는 scanf()에 대응하는 것으로서 입력화일에 대한 포인터를 첫 인자로 갖는다. sscanf()는 scanf()의 문자열 형태이다.
4. 파일
파일은 구조체에 대한 포인터에 의해 접근된다. 이 구조체는 표준 헤더파일 stdio.h에서 FILE로 정의되었다. 추상적으로는 파일을 순차적으로 처리되는 문자들의 스티림으로 생각할 수 있다. 시스템에서 제공하는 표준 하일로는 다음의 셋을 들 수 있다.
stdin 표준 입력 파일 : 키보드에 연결됨
stdout 표준 출력 파일 : 스크린에 연결
stderr 표준 오류 파일 : 스크린에 연결
라이브러리 함수 fopen()은 파일을 열기 위해서 사용된다. 이것은 FILE에 대한 포인터를 반환한다. 다음은 my_file이라는 이름의 파일을 여는 예이다.
#include <stdio.h>
main()
{
int c;
FILE *fp, *fopen();
fp=fopen("my_file","r");
파일이 개방된 후 파일에 대한 모든 참조에 파일 포인터가 사용된다. 표준 라이브러리에 마련되어 있는 중요한 함수를 살펴보면 다음과 같다. 이 외에 다른 함수도 있으며 시스템마다 다소 차이가 있을 수 있다. 상세한 사항은 사용시스템으 지침서를 참조하기 바란다.
1) fopen(파일명, 파일모드)
버퍼가 있는 파일을 열기 위해 필요한 작업을 수행하고 FILE에 대한 포인터를 반환한다. “파일명”을 접근할 수 없으면 NULL값이 반환된다. “파일명”과 파일모드“모두 문자열이다. ”파일모드“는 ”r“, ”w", “a" 가 있는데, 각각 일기(read), 쓰기(write), 추가(append)에 해당한다. 파일 모드가 ”r"이나, “w" 이면 파일 포인터는 파일의 처음에 위치하고, ”a“ 모드인 경우에는 파일의 끝에 위치한다. 파일 모드가 "w" 나 "a" 인 경우에 파일이 존재하지 않으면 파일이 새로 생성된다.
2) fclose(파일 포인터)
“파일 포인터”는 FILE에 대한 포인터이다. 함수 fclose는 버퍼를 비우는 데 필요한 일을 수행하고 지정된 파일에 대한 연결을 모두 제거한다. “파일 포인터”가 파일과 관계가 없는 경우에는 EOF값이 반환된다. 개방되는 파일의 수는 제한적이다. 필요한 파일만을 열어 놓는 것이 시스템의 효율을 높이는 데 도움이 된다.
3) getc(파일 포인터)
“파일 포인터”가 지시하는 파일에서 다음 문자를 추출한다. 문자의 값은 정수로 반환된다. 하일의 끝을 만나거나 오류가 있는 경우에는 EOF값을 반환한다.
4) getchar()
이것은 getc(stdin)과 동일하다
5) fgetc(파일 포인터)
getc()와 유사하게 동작한다.
6) ungetc(c, 파일포인터)
c를 “파일 포인터”가 지시하는 파일에 푸쉬한다. 푸쉬한 다음 번 읽기에는 바로 이 값이 반환된다. ungetc의 반환치는 c이다. 오류인 경우에는 EOF가 반환된다.
7) putc(c, 파일 포인터)
c의 값을 파일 포인터가 지시하는 출력파일에 출력한다. 출력한 문자의 int 값을 반환한다.
8) putchar(c)
putc(c,stdout)와 동일하다
9) fput(c,파일 포인터)
putc(c,파일 포인터)와 유사하다
10) gets(s)
s는 문자열 변수이다. gets(s) stdin으로부터 s로 문자열을 읽어들인다. 개행(newline)문자가 나올 때까지 입력문자들을 s에 저장한다. 개행문자는 널(null)문자로 바꿔어 s에 저장된다. 문자형 포인터인 s의 값이 반환된다.
11) fgets(s, n, 파일 포인터)
파일 포인터가 지시하는 파일로부터 n-1개 문자가 읽혀질 때까지 혹은 첫 번째 개행문자가 읽혀질 때까지 문자를 읽어 문자열 s에 저장한다. gets(s)와는 달리 개행문자 자체도 s에 저장되며 이것 다음에 널이 추가된다. 문자형 포인터인 s의 값이 반환된다.
12) puts(s)
문자열 s와 개행문자가 stdout에 출력된다.
13) fputs(s, 파일 포인터)
문자열 s를 파일 포인터가 지시하는 파일에 출력한다. puts()와는 달리 개행문자가 추가로 출력되지 않는다.
14) fseek(파일 포인터, offset, place)
다음 입력 또는 출력의 시작 위치르 지정한다. offset은 long형의 변수이고 place는 int형 변수이다. place는 0, 1, 2, 중 하나의 값을 가진다. 0은 파일의 시작, 1은 현재 위치, 2는 파일의 끝에 해당한다. 파일 포인터는 place로부터 offset 바이트만큼 이동된다. ungetc()의 효과가 fseek()에 의해 소멸된다.
15) rewind(파일 포인터)
fseek(파일 포인터, 0L, 0)와 동일하다.
16) ftell(파일 포인터)
파일 포인터가 지시하는 파일의 처음으로부터 현재 위치까지의 차감거리를 long으로 반환한다. 유닉스에서 차감거리는 바이트 단위로 측정된다.
17) exit(status)
프로그램 실행을 종료시킨다. 모든 버퍼는 바워지고 파일은 닫힌다. status의 값을 호출 프로세스에 반환한다. status는 int형의 식이다. 보통 프로그램이 적절하게 수행되었으면 0이, 그렇지 않으면 0이 아닌 값이 된다.
18) system(command)
command는 유닉스의 명령어에 해당한는 문자열이다. 예를 들면
system("cal 1990");
은 쉘(shell)이 유닉스 명령어 “cal 1990"을 수행하도록 한다. 이 결고 1990년의 달력이 파일 stdout에 인쇄된다.
이러한 표준 라이브러리 함수들을 사용하는 예들을 살펴보자, 첫째 프로그램은 파일에 존재하는 문자들을 대문자로 바꾸어 stdout에 출력하는 것이다.
/*** 파일의 모든 문자를 대문자로 바꾸기 ***/
#include <stdio.h>
main(argc, argv)
int argc;
char *argv[];
{
int c;
FILE *fp, fopen();
fp=fopen(argv[1], "r");
while((c=getc(fp) !=EOF)
if('a' <= c && c <= 'z')
putchar(c+'A'-'a');
else
putchar(c);
}
다음의 문장
fp=fopen("argv[1], "r");
은 사용자가 입력한 코맨드의 첫 인수로 지정된 파일을 읽기용으로 한다. 이제 포인터 변수 fp가 이 파일을 지시하는 것으로 생각하면 된다. 다음 줄
while((c=getc(fp)) != EOF )
로부터 시작되는 while 루프는 파일의 끝까지 문자를 c로 읽어 이를 대문자로 변환하여 출력한다. UNIX의 경우 이 프로그램이 컴파일된 후 프로그램의 수행은 다음과 같은 코맨드에 의한다.
a.out 파일
여기서 “파일”은 입력자료가 저장되어 있는 파일의 이름이다. 출력은 stdout에 기록된다.
다음의 프로그램은 입력파일을 한 행씩 더 뛰워 stdout과 출력파일에 기록한다. 즉, 입력파일에 개행문자마다 이를 하나 더 추가한다.
/*** 파일의 행을 이중으로 뛰우기 ***/
#include <stdio.h>
main(argc, argv)
int argc;
char *argv[];
{
int c;
FILE *myfile_in, *myfile_out,*fopen();
if(argc != 3) {
fprintf(stderr, "\nusage: %s infile outfile\n", *argv);
exit(1);
}
if((myfile_in = fopen(*++argv, "r")) == NULL) {
fprintf(stderr, "\nmain:cannot open %s\n", *argv);
exit(1)
}
if(myfile_out = fopen(*++argv, "w")) == NULL) {
fprintf(stderr, "\nmain:cannot open %s\n", *argv);
exit(1);
}
while((c = getc(myfile_in) != EOF) {
putc(c,myfile_out);
putc(c,stdout); /* echo to stdout also */
if(c == '\n') { /* double space */
putc(c, myfile_out);
putc(c, stdout);
}
}
fclose(myfile_in);
fclose(myfile_out);
}
이 프로그램의 목적 코드가 double_space라는 파일에 저장되어 있다고 하자. 다음의 코맨드
double_space 파일1
이 입력되면 argc는 2의 값이 되어 오류 메시지를 stderr로 보내고 시스템 함수 exit(1)를 호출하여 탈출하게 된다. exit()의 인자가 0이 아니므로 비정상적인 탈출이다. 다음의 코맨드
double_space 파일1, 파일2
가 입력되면 argc가 3이 되므로 제어는 밑의 if 문으로 이동된다. 다음의 식
(myfile_in = fopen(*++argv, "r")) == NULL
은 코맨드의 첫째 인자가 제시하는 파일을 열기 위해 사용되었다. *++argv 대신에 argv[1] 을 써도 된다. =가 ==보다 우선순위가 낮으므로 괄호는 반드시 있어야 한다. 만을 fopen()이 파일을 열지 못하면 NULL값이 반환되는데, 이 경우 stderr에 적절한 메시지를 출력한후 프로그램을 빠져 나간다
'컴퓨터 > 언어,프로그래밍' 카테고리의 다른 글
정적 메모리 할당 vs 동적 메모리 할당 (0) | 2009.03.20 |
---|---|
C언어 :: 이중포인터 사용법 (0) | 2009.03.20 |
C언어 :: 매크로 (0) | 2009.03.17 |
[C언어] C언어란? (0) | 2009.03.17 |
[C언어] C언어 변수 (0) | 2009.03.17 |