Study/C언어

처음하시는 분들을 위한 C언어 기초강의 시즌2 - 07 [입력문과 출력문(printf, scanf, scanf_s)]

2018. 10. 17. 16:30

 제 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는 괄호안에 들어오는 인자의 크기를 반환하는 함수입니다.
(크기는 바이트 기준입니다.)

#include <stdio.h>
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;
}

이렇게 되면 각각의 바이트 크기가 도출되죠.
 
변수로도 가능합니다.

#include <stdio.h>
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
'Study/C언어' 카테고리의 다른 글
  • 처음하시는 분들을 위한 C언어 기초강의 시즌2 - 10 [연산자 이야기]
  • 처음하시는 분들을 위한 C언어 기초강의 시즌2 - 08 [변수에 대한 추가적인 이야기]
  • 처음하시는 분들을 위한 C언어 기초강의 시즌2 - 06 [자료형2]
  • 처음하시는 분들을 위한 C언어 기초강의 시즌2 - 05 [자료형1]
Eskeptor
Eskeptor
Eskeptor
Hello World
Eskeptor
전체
오늘
어제
  • 분류 전체보기 (138)
    • Computer (5)
      • Linux (1)
      • Hardware (2)
      • Software (0)
      • Tips (1)
      • Website (0)
    • Mobile (1)
      • Application (1)
    • Study (108)
      • Android (9)
      • C언어 (45)
      • C++ (17)
      • Unity 5(유니티5) (11)
      • Qt 프로그래밍 (2)
      • MFC (12)
      • C#, Winform (12)
    • My World (24)
      • OpenPad(Android) (12)
      • 한글 패치 (1)
      • C#으로 만든 귀요미들 (5)
      • MFC로 만든 귀요미들 (6)
    • Life Goes On (0)
      • Hip Hop (0)

블로그 메뉴

  • 홈
  • 태그
  • 미디어로그
  • 위치로그
  • 방명록

공지사항

인기 글

태그

  • 테트리스
  • 자료구조
  • 알고리즘
  • Tetris
  • 슈팅게임
  • 안드로이드
  • Android
  • 프로그래밍
  • 자바
  • 만들기
  • 비행기
  • Unity
  • c++11
  • C++
  • 기초
  • 포인터
  • 왕초보
  • 초보
  • 배열
  • C#
  • Java
  • 유니티
  • 메모장
  • 강좌
  • openpad
  • 기본
  • MFC
  • 강의
  • 오픈패드
  • C언어

최근 댓글

최근 글

hELLO · Designed By 정상우.
Eskeptor
처음하시는 분들을 위한 C언어 기초강의 시즌2 - 07 [입력문과 출력문(printf, scanf, scanf_s)]
상단으로

티스토리툴바

단축키

내 블로그

내 블로그 - 관리자 홈 전환
Q
Q
새 글 쓰기
W
W

블로그 게시글

글 수정 (권한 있는 경우)
E
E
댓글 영역으로 이동
C
C

모든 영역

이 페이지의 URL 복사
S
S
맨 위로 이동
T
T
티스토리 홈 이동
H
H
단축키 안내
Shift + /
⇧ + /

* 단축키는 한글/영문 대소문자로 이용 가능하며, 티스토리 기본 도메인에서만 동작합니다.