제 7강) 입력문과 출력문 |
우리는 6차례에 걸쳐서 printf문을 써왔습니다.
#include <stdio.h>
int main(void)
{
printf("Hello World! \n");
return 0;
}
위와 같이 써왔죠.
여기서 printf문은 "출력문"이라고 합니다.
그럼 출력문은 어떤것이 있을까요?
#include <stdio.h>
int main(void)
{
int num;
printf("Please Enter the Number : ");
scanf("%d", &num);
printf("Number is %d \n", num);
return 0;
}
C언어에서 아주 일반적인 입력문으로는 "scanf"라는 것이 있습니다.
오늘 이 2가지를 집중적으로 배워보도록 하겠습니다.
※put, get 관련 함수들도 있긴하지만 이것들은 나중에 따로 배우도록 하겠습니다.
출력문(printf) |
"printf"는 출력문이라는 것을 어느 정도 느낌으로 아셨을겁니다.
#include <stdio.h>
#include <limits.h>
// limits.h에는 C언어에서 기본으로 제공하는 자료형의 최소, 최대크기를 가지고 있습니다.
// CHAR_MIN, CHAR_MAX, UCHAR_MAX 등...
int main(void)
{
printf("char : %d ~ %d \n", CHAR_MIN, CHAR_MAX);
printf("unsigned char : %d ~ %d \n", 0, UCHAR_MAX);
printf("short : %d ~ %d \n", SHRT_MIN, SHRT_MAX);
printf("unsigned short : %d ~ %d \n", 0, USHRT_MAX);
printf("int : %d ~ %d \n", INT_MIN, INT_MAX);
printf("unsigned int : %d ~ %u \n", 0, UINT_MAX);
// %u는 "unsigned int"를 출력
printf("long : %ld ~ %ld \n", LONG_MIN, LONG_MAX);
// %ld는 "long"을 출력
printf("unsigned long : %d ~ %lu \n", 0, ULONG_MAX);
// %lu는 "unsigned long"을 출력
printf("long long : %lld ~ %lld \n", LLONG_MIN, LLONG_MAX);
// %lld는 "long long"을 출력
printf("unsigned long long : %d ~ %llu \n", 0, ULLONG_MAX);
// %llu는 "unsigned long long"을
return 0;
}
바로 지난 시간의 예제처럼 여러 값들을 출력하는데 쓰이죠.
그럼... 서식문자를 잠시 봅시다.
서식문자 | 출력대상 | 출력형태 |
%d | char, short, int | 부호가 있는10진수 정수 |
%ld(엘디) | long | 부호가 있는10진수 정수 |
%lld(엘엘디) | long long | 부호가 있는10진수 정수 |
%o(소문자 오) | unsigned int | 부호가 없는 8진수 정수 |
%u | unsigned int | 부호가 없는 10진수 정수 |
%x, %X | unsigned int | 부호가 없는 16진수 정수 |
%f | float, double | 10진수 방식의 부동소수점 실수 |
%Lf(엘에프) | long double | 10진수 방식의 부동소수점 실수 |
%e, %E | float, double | e방식의 부동소수점 실수(대문자 E로 하면 대문자로 나옴) |
%g, %G | float, double | 값에 따라 %f와 %e를 선택함(소수점 이하 필요없는 0을 없앰) |
%c | char, short, int | 값에 대응하는 문자 하나 |
%s | char * | 문자열 |
%p | void * | 포인터의 주소값 |
이건 꼭 알아두셔야 합니다.
(수학 문제를 풀때 +, -, /, *를 알아야 문제를 푸는 것 처럼)
그럼 하나하나 사용하여봅시다.
#include <stdio.h>
int main(void)
{
char ch = 65;
short sh = 66;
int it = 67;
long lg = 68L;
long long llg = 69LL;
unsigned int num_8 = 012; // 8진수(0으로 시작)
unsigned int num_10 = 10; // 10진수
unsigned int num_16 = 0xA; // 16진수(0x로 시작)
float ft = 123.456789F;
double db = 123.456789123;
long double ldb = 123.456789123;
char ch_ch = 'A';
short sh_ch = 'B';
int it_ch = 'C';
char* str = "Hello World?!";
char str2[20] = "Hello World?!2";
printf("<부호가 있는 10진 정수> \n");
printf("char : %3d, short : %3d, int : %3d \n", ch, sh, it);
printf("long : %3ld, long long : %3lld \n\n", lg, llg);
printf("<부호가 없는 진수들(10)> \n");
printf("8진수 : %3o, 10진수 : %3u, 16진수 : %3x \n", num_8, num_10, num_16);
printf("8진수 : %3o, 10진수 : %3u, 16진수 : %3x \n\n", num_10, num_10, num_10);
// 두번째 printf(21번줄)문장은 출력되는 수가 각각의 진수로 표현하지 않았을 지라도
// 출력 형식에 맞게 알아서 출력하여 준다는 것을 보여줍니다.
printf("<소수 출력> \n");
printf("float : %f, double : %.9f, long double : %.9Lf \n\n", ft, db, ldb);
printf("<소수 출력(e방식)> \n");
printf("float : %e, double : %e \n\n", ft, db);
printf("<소수 출력(g사용)> \n");
printf("float : %g, double : %g \n\n", ft, db);
printf("<문자 출력> \n");
printf("char : %3c, short : %3c, int : %3c \n", ch, sh, it);
printf("char : %3c, short : %3c, int : %3c \n\n", ch_ch, sh_ch, it_ch);
printf("<문자열 출력> \n");
printf("char* : %s \n", str);
printf("char 배열 : %s \n\n", str2);
printf("<주소값 출력> \n");
printf("ch 주소 : %p, sh 주소 : %p, it 주소 : %p \n", &ch, &sh, &it);
return 0;
}
아주아주 긴 예제이긴 합니다만 정말 간단한 예제입니다.
출력 결과는 다음과 같습니다.
%3d, %.2f |
printf문을 자주 쓰다보면 저런 것을 필요로 할 때가 있게 될겁니다. (여기서 부터 사용될 N은 숫자를 뜻합니다.) "%Nd"는 N칸만큼 확보해서 오른쪽 정렬하여 출력하라 라는 뜻입니다. "%3d"는 3칸만큼 확보해서 오른쪽 부터 정렬하여 출력하게 됩니다. int num1 = 1982, num2 = 94562; printf("%3d %2d", num1, num2); 그런데 위와 같이 자신의 수(1982, 94562)가 정렬할 공간의 크기(3, 2)보다 클 경우에는 정렬이 무시됩니다. #include <stdio.h> int main(void) { printf("%8d\n", 1); printf("%8d\n", 11); printf("%8d\n", 111); printf("%8d\n", 1111); printf("%8d\n", 11111); printf("%8d\n", 111111); printf("%8d\n", 1111111); printf("%8d\n", 11111111); printf("%8d\n", 1231231213); return 0; } 위의 예제를 한번 실행해 보시면 이해가 가실겁니다. 그리고 반대로 "%-Nd"는 N칸만큼 확보해서 왼쪽으로 정렬하여 출력하라는 뜻이지요. 이제 소수점을 보도록 합시다. "%.Nf"는 N칸만큼 소수점을 출력한다 라는 뜻입니다. 그럼 "%.6f"는 소수점 6자리까지 출력하라 라는 뜻이 됩니다. |
소수출력 방식 %g |
사실 저 위에 나와있는 길고 긴 예제를 통해서는 %g를 잘 알수 없습니다. 다음의 예제를 한번 실행하여봅시다. #include <stdio.h> int main(void) { double d1 = 123.123456789; double d1_s = 123.12; double d2 = 123456.123456789; double d2_s = 123456.12; double d3 = 123456789.123456789; double d3_s = 123456789.123456789; printf("<%%f를 사용하여 출력> \n"); printf("d1 : %f, d1_s : %f \n", d1, d1_s); printf("d2 : %f, d2_s : %f \n", d2, d2_s); printf("d3 : %f, d3_s : %f \n\n", d3, d3_s); printf("<%%g를 사용하여 출력> \n"); printf("d1 : %g, d1_s : %g \n", d1, d1_s); printf("d2 : %g, d2_s : %g \n", d2, d2_s); printf("d3 : %g, d3_s : %g \n\n", d3, d3_s); return 0; } 이 예제를 실행하여 보면 이렇게 나오게 됩니다. %g의 경우에는 다음과 같습니다. 1. 기본적으로 "정수부분 + 소수부분"의 자릿수를 기본적으로 6자리 출력을 합니다. 2. 소수점 아래의 0은 제거합니다. 3. 6자리를 넘어갈 경우 e표현식을 사용하게 됩니다. |
위의 예제에는 문자열이라던지 주소라던지 아직 배우지 않은 것들이 나왔습니다.
이것들은 나중에 배우도록 합시다.
입력문(scanf) |
"scanf"는 입력문입니다.
어떤 값을 입력받는 기능을 합니다.
scanf 사용법 |
scanf("형식", &변수); 예) scanf("%d", &num); // 입력받은 값을 num에 정수(%d) 형식으로 저장 scanf("%lf", &num); // 입력받은 값을 num에 double형 실수(%lf) 형식으로 저장 scanf("%s", str); // 입력받은 값을 str에 문자열(%s) 형식으로 저장 |
여기서 "&"이라는 것이 등장합니다.
이것은 포인터 단원으로 가면 이해가 쉬우시겠지만 지금은 "주소"라고 생각하세요.
(변수와 주소는 이 다음시간에 추가적으로 알아봅시다.)
문자열 입력시에는 &을 붙이지 않는다. |
다른 모든 변수들은 입력값을 받을 때 &을 붙이지만, 문자열을 입력받을 때는 붙이지 않습니다. (이것또한 "문자열"을 배울때 알려드릴테니 지금은 그냥 "그렇구나" 하고 알아두세요.) |
이제 아까전에 본 예제를 떠올려봅시다.
#include <stdio.h>
int main(void)
{
int num;
printf("Please Enter the Number : ");
scanf("%d", &num);
printf("Number is %d \n", num);
return 0;
}
바로 이 예제였죠.
※만약에 scanf에서 에러가 난다면 아래로 내려서 "입력문(scanf_s)"를 봐주세요.
실행시켜 봅시다.
이 화면이 뜨고 커서가 깜빡 깜빡 거릴겁니다.
바로 scanf가 값 입력을 기다리는 것인데요.
여기에 아무 숫자나 입력하여봅시다.
그리고 엔터를 누르면
이렇게 나오는 것을 볼 수 있습니다.
이제 사용법은 간단히 알았으니 이것을 쓰기위해서 printf문 처럼 서식문자를 알아봅시다.
서식문자 | 출력대상 | 출력형태 |
%c, %C | char | 문자 |
%d | int | 부호 있는 10진수 정수 |
%i | int | 부호 있는 정수 |
%o(소문자 o) | int | 부호 있는 8진수 정수 |
%p | void * | 16진수 포인터(32비트와 64비트 다름) |
%u | unsigned int | 부호가 없는 10진수 정수 |
%x | int | 부호 있는 16진수 정수 |
%e, %E, %f, %g, %G | float | 부동소수점 실수 |
%lf | double | 부동소수점 실수 |
%Lf | long double | 부동소수점 실수 |
%s, %S | char * | 문자열 |
(표 출처 : MSDN)
이것 역시 외우셔야 합니다.
이제 이것을 이용한 예제를 봅시다.
#include <stdio.h>
int main(void)
{
int num_d, num_i, num_o, num_x;
char ch_c;
unsigned int num_u;
float num_e, num_f, num_g;
double num_lf;
long double num_Lf;
char str[20] = { 0, }; // 문자열은 나중에 배울테니 지금은 그냥 봅시다.
printf("정수 입력(%%d) : ");
scanf("%d", &num_d);
while (getchar() != '\n'); // 이 문장은 엔터 버퍼를 없애는 겁니다.
printf("정수 입력(%%i) : ");
scanf("%i", &num_i);
while (getchar() != '\n');
printf("8진수 입력(%%o) : ");
scanf("%o", &num_o);
while (getchar() != '\n');
printf("16진수 입력(%%x) : ");
scanf("%x", &num_x);
while (getchar() != '\n');
printf("부호 없는 정수 입력(%%u) : ");
scanf("%u", &num_u);
while (getchar() != '\n');
printf("문자 입력(%%c) : ");
scanf("%c", &ch_c);
while (getchar() != '\n');
printf("실수 입력(%%e) : ");
scanf("%e", &num_e);
while (getchar() != '\n');
printf("실수 입력(%%f) : ");
scanf("%f", &num_f);
while (getchar() != '\n');
printf("실수 입력(%%g) : ");
scanf("%g", &num_g);
while (getchar() != '\n');
printf("실수 입력(%%lf) : ");
scanf("%lf", &num_lf);
while (getchar() != '\n');
printf("실수 입력(%%Lf) : ");
scanf("%Lf", &num_Lf);
while (getchar() != '\n');
printf("문장 입력(%%s 한:10글자, 영:20글자) : ");
scanf("%s", str);
printf("\nnum_d : %8d, num_i : %8i \n", num_d, num_i);
printf("num_o : %8o, num_x : %8x \n", num_o, num_x);
printf("num_u : %8u, ch_c : %8c \n", num_u, ch_c);
printf("num_e : %4.4e, num_f : %4.4f \n", num_e, num_f);
printf("num_g : %g, num_lf : %f \n", num_g, num_lf);
printf("num_Lf : %f \n", num_Lf);
printf("str : %s \n", str);
return 0;
}
이것또한 길다란 예제입니다.
입력하고 나서 실행하여 원하는 값들을 넣어보세요.
while (getchar() != '\n'); |
엔터를 눌렀을 때 다음 입력문이 씹히는 것을 방지합니다. 이렇게 씹히는 이유는 입력버퍼가 비워지지 않았기 때문입니다. (이 입력버퍼도 나중에 뒤에서 배우도록 하겠습니다.) 그래서 while(getchar() != '\n');를 입력하여 입력버퍼를 비우도록 합니다. (모든 운영체제에서 동일하게 작동) |
문자열 입력시 띄어쓰기 |
방금 위의 예제의 결과값에서 "Hello World?" 라고 입력을 했으나 "Hello"에서 끊겼음을 볼 수 있습니다. scanf에서는 기본적으로는 띄어쓰기 전까지만 입력된 값을 받습니다. |
입력문(scanf) 활용(띄어쓰기 인식) |
scanf에 선택적 인수(scanset character)를 넣어서 사용할 수 있습니다.
특히 띄어쓰기를 인식하여 넣을 수 있게 됩니다.
#include <stdio.h>
int main(void)
{
char str[20] = { 0, }; // 문자열은 나중에 배울테니 지금은 그냥 봅시다.
char str2[20] = { 0, };
printf("문장 입력(%%s 한:10글자, 영:20글자) : ");
scanf("%s", str);
while (getchar() != '\n');
printf("문장 입력(%%s 한:10글자, 영:20글자) : ");
scanf("%[^\n]s", str2);
printf("str : %s \n", str);
printf("str2 : %s \n", str2);
return 0;
}
위의 예제를 보면 색다른 scanf가 보일겁니다.
scanf("%[^\n]s", str2); |
라는 문장인데요.
"\n(엔터) 가 나올때까지 나오는 것을 입력받는다."라는 뜻입니다.
결론은 띄어쓰기가 나와도 계속 입력받는다는 소리죠.
입력문(scanf_s) |
Visual Studio 2015 이상의 버전을 쓰신다면 다음과 같은 것을 보실 수 있습니다.
바로 SDL(Security Development Lifecycle)인데요.
이걸 만약에 체크했다면
빌드하실 수 없습니다.
그 이유는 SDL검사로 인해서 "취약한 함수"를 사용하게 되면 자체적으로 빌드를 못하도록 하기 때문입니다.
이럴때는 2가지 방법이 있습니다.
1. SDL 검사를 해제하고 scanf를 사용 2. scanf_s함수를 사용 |
만약에 1번째 방법을 사용하면
이러한 경고문을 보게 됩니다.
이 함수는 "unsafe" 합니다. scanf_s를 대신 사용하세요. |
scanf함수를 사용하게 되면 "오버플로우 공격"에 취약해집니다.
(SDL 해제는 "프로젝트 - 속성 - C/C++ - 일반 - SDL 검사" 에서 할 수 있습니다.)
하지만 scanf를 주로 쓰도록 합시다.
(Visual Studio에서만 코딩하실거라면 scanf_s를 써도 무관합니다.)
GCC와 같은 경우에는 "scanf_s"함수를 지원하지 않습니다.
(왜냐하면 scanf_s는 표준함수가 아닌 MSVC에서 제공하는 함수이기 때문입니다.)
scanf_s 사용법 |
scanf_s("형식", &변수, 크기); 예) scanf_s("%d", &num); // 입력받은 값을 num에 정수(%d) 형식으로 저장 scanf_s("%lf", &num); // 입력받은 값을 num에 double형 실수(%lf) 형식으로 저장 scanf_s("%s", str, sizeof(str)); // 입력받은 값을 str에 문자열(%s) 형식으로 저장 |
기존의 scanf에서 "크기"가 들어갔는데요.
일반적으로 수(정수, 실수)를 사용하는데는 scanf와는 같으나 문자열을 사용하는데는 문자열의 길이만큼 크기를 정해줘야합니다.
#include <stdio.h>
int main(void)
{
char str[20] = { 0, }; // 문자열은 나중에 배울테니 지금은 그냥 봅시다.
char str2[20] = { 0, };
printf("문장 입력(%%s 한:10글자, 영:20글자) : ");
scanf_s("%s", str, sizeof(str));
while (getchar() != '\n');
printf("문장 입력(%%s 한:10글자, 영:20글자) : ");
scanf_s("%[^\n]s", str2, sizeof(str2));
printf("str : %s \n", str);
printf("str2 : %s \n", str2);
return 0;
}
아주 쉽죠?
(문자열 길이는 문자열을 배울때 따로 설명하겠습니다.)
sizeof(자료형) |
sizeof는 괄호안에 들어오는 인자의 크기를 반환하는 함수입니다. (크기는 바이트 기준입니다.) int main(void) { printf("sizeof(int) : %d \n", sizeof(int)); printf("sizeof(char) : %d \n", sizeof(char)); printf("sizeof(double) : %d \n", sizeof(double)); return 0; } 이렇게 되면 각각의 바이트 크기가 도출되죠. 변수로도 가능합니다. int main(void) { int num_int = 123; char num_char = 64; double num_double = 123.45678; printf("sizeof(int) : %d \n", sizeof(num_int)); printf("sizeof(char) : %d \n", sizeof(num_char)); printf("sizeof(double) : %d \n", sizeof(num_double)); return 0; } 그리고 값은 해당 변수의 자료형을 따라갑니다. |
다음시간에는 |
앞에서 이야기 하지 못했던 "변수의 추가적인 이야기"와 "변수 선언의 추가적인 이야기"를 하겠습니다.
'Study > C언어' 카테고리의 다른 글
처음하시는 분들을 위한 C언어 기초강의 시즌2 - 10 [연산자 이야기] (0) | 2018.10.17 |
---|---|
처음하시는 분들을 위한 C언어 기초강의 시즌2 - 08 [변수에 대한 추가적인 이야기] (0) | 2018.10.17 |
처음하시는 분들을 위한 C언어 기초강의 시즌2 - 06 [자료형2] (0) | 2018.07.08 |
처음하시는 분들을 위한 C언어 기초강의 시즌2 - 05 [자료형1] (0) | 2018.07.08 |
처음하시는 분들을 위한 C언어 기초강의 시즌2 - 04 [변수] (0) | 2018.07.08 |