C & C++/디딤돌 C언어

Part 8. 표준 입출력

언휴 2024. 1. 9. 08:54

 

C언어 - 표준 입출력

23. 표준 입출력 개요

2024년 4월 3일에 새롭게 제작하여 유튜브에 업로드할 예정입니다.

프로그래밍 언어의 문법을 익히고 프로그래밍 작성 능력을 키우려면 기본적인 입출력 기능은 사용할 수 있어야겠죠.
C언에서는 다양한 입출력 방법을 제공하는데 여기에서는 표준 입출력 함수 중에 자주 사용하는 함수를 살펴볼게요.

이미 앞에서 설명없이 사용했던 printf 함수는 대표적인 표준 입출력 함수예요.
표준 입출력 함수를 사용하려면 stdio.h 파일 포함문이 필요하죠.

#include <stdio.h>

여기에서는 stdio.h에서 제공하는 많은 함수 중에 세 가지 표준 출력 함수와 다섯 가지 표준 입력 함수를 소개할게요.

int printf(const char * format, … );
int putchar(int ch);
int puts(const char *str);

int scanf(const char * format, … );
int scanf_s(const char *format,…);
int getchar(void);
char * gets(char *buf);
char *gets_s(char *base,size_t size);

printf 함수와 scanf, scanf_s는 첫 번째 입력 인자로 전달한 포멧에 맞게 출력하거나 입력 받는 함수입니다.
하나의 문자를 출력하거나 입력 받을 때는 putchar와 getchar를 사용해요.
그리고 문자열을 출력하거나 입력 받을 때는 puts와 gets, gets_s를 사용할 수 있어요.

scanf_s와 get_s 처럼 뒤에 _s 함수는 안전한 버전의 함수를 의미해요.
scanf 함수와 gets 함수에서 문자열을 입력받을 때 버퍼 크기를 전달하지 않아 버그가 발생할 수 있어요.
만약 사용자가 버퍼 크기보다 더 많이 입력하면 버퍼 오버 플로우 버그가 발생하거든요.
이러한 문제를 해결하기 위해 제공하는 함수들은 _s가 뒤에 붙어요.

뒤에 _s가 붙는 함수가 모두 버퍼 오버 플로우 문제를 해결하기 위한 것은 아니지만 안전하다는 의미는 맞아요.
그리고 버퍼 오버플로우 버그를 해결하기 위해 제공하는 함수들은 모두 _s가 붙어요.
이러한 함수들은 초기 C언어 표준에서는 제공하지 않았던 함수들이예요.

24. printf 함수

int printf(const char * format, … );

pritnf 함수의 원형(함수를 사용할 때 전달하는 입력 매개 변수 리스트와 반환 형식)을 보면 반환 형식이 int이고 첫 번째 입력 매개 변수는 const char *이고 뒤에 다른 입력 매개 변수가 여러 개 올 수 있어요.

printf 함수의 첫 번째 인자에는 어떤 형태로 출력할 것인지 표현하죠.
그리고 두 번째 이후의 인자는 출력할 데이터들이 와요.
그리고 printf 함수를 호출했을 때 반환하는 값은 콘솔 화면에 출력한 문자 개수예요.

◈ printf 함수의 반환 값

#include <stdio.h>
int main()
{
    int re = 0;
 
    re = printf("Hello World %d\n",123);
    printf("re: %d \n",re);
    return 0;
}

◈  실행 결과

Hello World 123
re: 16

위 예제를 보면 Hello World 123[개행 문자]를 출력한 후에 반환값을 다시 출력하고 있어요.
printf 함수는 출력한 문자의 개수를 반환하는데 공백이나 탭, 개행 문자(엔터)도 하나의 문자로 취급해요.
따라서 실제 출력한 문자 개수는 16개이며 printf 함수의 결과값으로 16을 반환받음을 알 수 있어요.

printf 함수와 scanf 함수는 첫 번째 인자로 출력 및 입력 받고자 하는 포멧 문자열을 전달합니다.

다음은 포멧 문자열을 어떻게 사용하는지 설명한 것이예요.
전체를 기억할 필요는 없여요.
자주 사용하다 보면 자연스럽게 익혀질 거예요. 하지만 필요할 때 찾아서 볼 수 있게 표시는 해 두세요.

포멧 지정 문자열에 % 문자는 변환 사양 문자로 옵션으로 flags와 길이 수정자가 올 수 있으며 필수적으로 어떠한 형식으로 출력할 것인지 포멧 지정자 문자인 diouxXaAeEfFgG% 중에 하나를 사용합니다.
d,i
    int 형식 인자를 10진수로 출력합니다.
o, u, x, X
    unsigend int 형식 인자를 8진수(o), 10진수(u), 16진수(x 또는 X)로 출력합니다. x를 사용하면 16진수의 abcdef를 소문자로 출력하고 X를 사용하면 ABCDEF를 출력합니다.
f,F
    double 형식 인자를 [-]ddd.ddd 형태로 출력합니다. 디폴트로 소수점 이하 6자리를 출력하며 [d].[d] 형태로 폭과 소수점 이하 출력할 자리수를 결정할 수 있습니다. 이 때 출력하지 않는 자리의 값은 반올림하여 출력합니다.
※ scanf로 %f는 float 형식 포인터 인자, %l”는 double 형식 포인터 인자일 때 사용하는 것 때문에 많은 개발자는 printf에서도 %lf로 double 형식 인자일 때 사용해야 하는 것으로 생각하지만 printf에서는 %lf에서 l은 아무런 영향도 미치지 않습니다. (ISO/IEC 9899:TC3 표준 문서 276 페이지~277페이지)
※ printf에서 %lf 나 %f가 차이가 없지만 scanf 사용하는 것과 혼돈하고 기억할 필요 없이 언제나 %lf를 쓴다고 문제가 발생하지는 않습니다.
e,E
    double 형식 인자를 [-]d.ddde±dd 형태로 출력합니다.
g,G
    알아서 f나 e(F나 E) 형태로 출력합니다.
a,A 
    double 형식 인자를 [-]0xh.hhhhp±dd 형태로 출력합니다.
c
    문자를 출력합니다. int 형식 인자일 때 unsigned char로 변환 후 문자를 출력합니다.
s
    종료 문자를 만나기 전까지의 문자의 조합을 출력합니다. 문자열을 출력한다고 생각할 수 있습니다.
p
    void 포인터로 메모리 주소를 16진수로 출력합니다.
%
    %를 출력합니다. 따라서 %를 출력하려면 %%로 포멧을 지정합니다.
flags 
    -+ 공백 #O 가 있습니다.

   출력 필드를 왼쪽으로 배치합니다. 디폴트는 오른쪽입니다.
+
    언제나 부호 필드를 출력합니다.
공백
    기호가 없을 때 수 앞에 공백을 출력합니다.
#
    8진수나 16진수로 출력할 때 수 앞에 o(또는 O)나 0x(또는 0X)를 출력합니다.
O
   소수점 이하 자리가 값이 있는 부분까지만 출력합니다.
길이 수정자는 hh, h, l, ll,j, z, t, L이 있습니다.
hh
    diouxX가 뒤에 오면 인자를 char 혹은 unsigned char로 변환하여 출력합니다.
h
   diouxX가 뒤에 오면 인자를 short 혹은 unsigned short로 변환하여 출력합니다.
l
    뒤에 diouxX가 뒤에 오면 인자를 long 혹은 unsigned long으로 변환하여 출력합니다.
ll
    뒤에 diouxX가 뒤에 오면 long long 혹은 unsigned long long으로 변환하여 출력합니다.
j
    뒤에 diouxX가 오면 intmax_t 로 변환하여 출력합니다.
z
    뒤에 diouxX가 오면 size_t 로 변환하여 출력합니다.
t
    뒤에 diouxX가 오면 ptrdiff_t 로 변환하여 출력합니다.
L
    뒤에 aAeEfFgG가 오면 long double 로 변환하여 출력합니다.

◈  다양한 포멧을 지정하여 출력

// 다양한 포멧을 지정하여 출력
#include <stdio.h>
#include <limits.h>
#include <math.h>
int main (void)
{
    printf("================type====================\n");
    printf("1. 십진수로 출력: % -d \n", 123);
    printf("2. 부호있는 십진수로 출력: %i \n", 123);
    printf("3. 부호없는 8진수로 출력: %o \n", 123);
    printf("4. 부호없는 십진수로 출력: %u \n", 123);
    printf("5. 부호없는 16진수로 출력(소문자): %x \n", 123);
    printf("6. 부호없는 16진수로 출력(대문자): %X \n", 123);    
    printf("7. 부동 소수점 표기로 출력: %f \n",123.45);
    printf("8. 지수형 표기로 출력(소문자): %e  \n",123.45);
    printf("9. 지수형 표기로 출력(대문자):  %E \n",123.45);
    printf("10. 간단한 표기로 출력(소문자): %g \n",123.45);
    printf("11. 간단한 표기로 출력(대문자): %G \n",123.45);
    printf("12. 문자 출력: %c \n",'a');
    printf("13. 문자열 출력: %s \n", "Hello");
    printf("14. 퍼센트 문자 출력: %%\n");
    printf("15. 메모리 주소 출력: %p \n","Hello");
    printf("16. long long 형식 출력: %lld\n", 0x123456789012345);    
 
    printf("================flag====================\n");
    printf("1. flag 지정하지 않고 10진수로 출력: %8d \n",123);
    printf("2. flag에 -를 지정하여 10진수로 출력: %-8d \n", 123);
    printf("3. flag에 +를 지정하여 10진수로 출력: %+d\n",123);
    printf("4. flag 지정하지 않고 8진수로 출력: %o\n",123);
    printf("5. flag에 #을 지정하여 8진수로 출력: %#o\n",123);
    printf("6. flag 지정하지 않고 16진수로 출력:%X\n",123);
    printf("7. flag에 #을 지정하여 16진수로 출력:%#X\n",123);
 
    printf("===========width와 .prec, modifier ======\n");
    printf("1.flag #을 지정하여 16진수로 출력(대문자): %#X \n",0x123456);
    printf("2.flag #, modifier h를 지정하여 16진수로 출력(대문자): %#hX \n",0x123456);
    printf("3.flag #, width 12 지정하여 16진수로 출력(대문자): %#12X \n",0x123456);
    printf("4.flag #, width 012 지정하여 16진수로 출력(대문자): %#012X \n",0x123456);
    printf("5. .prec를 .3을 지정하여 부동 소수점 표기로 출력: %.3f \n",123.456789);
    
 
    printf("===========long long 타입 출력 ===========\n");
    printf("lld 사용: %lld\n", 1234567890123);
 
    printf("========pow(10.0,-50)출력======\n");
    printf("Lf 사용:%.50Lf\n", pow(10.0,-50));    
    return 0;
}

◈  출력

================type====================
1. 십진수로 출력: 123
2. 부호있는 십진수로 출력: 123
3. 부호없는 8진수로 출력: 173
4. 부호없는 십진수로 출력: 123
5. 부호없는 16진수로 출력(소문자): 7b
6. 부호없는 16진수로 출력(대문자): 7B
7. 부동 소수점 표기로 출력: 123.450000
8. 지수형 표기로 출력(소문자): 1.234500e+02
9. 지수형 표기로 출력(대문자): 1.234500E+02
10. 간단한 표기로 출력(소문자): 123.45
11. 간단한 표기로 출력(대문자): 123.45
12. 문자 출력: a
13. 문자열 출력: Hello
14. 퍼센트 문자 출력: %
15. 메모리 주소 출력: 01366D4C
16. long long 형식 출력: 81985529205302085
================flag====================
1. flag 지정하지 않고 10진수로 출력: 123
2. flag에 -를 지정하여 10진수로 출력: 123
3. flag에 +를 지정하여 10진수로 출력: +123
4. flag 지정하지 않고 8진수로 출력: 173
5. flag에 #을 지정하여 8진수로 출력: 0173
6. flag 지정하지 않고 16진수로 출력:7B
7. flag에 #을 지정하여 16진수로 출력:0X7B
===========width와 .prec, modifier ======
1.flag #을 지정하여 16진수로 출력(대문자): 0X123456
2.flag #, modifier h를 지정하여 16진수로 출력(대문자): 0X3456
3.flag #, width 12 지정하여 16진수로 출력(대문자): 0X123456
4.flag #, width 012 지정하여 16진수로 출력(대문자): 0X0000123456
5. .prec를 .3을 지정하여 부동 소수점 표기로 출력: 123.457
===========long long 타입 출력 ===========
lld 사용:1234567890123
========pow(10.0,-50)출력======
Lf 사용:0.00000000000000000000000000000000000000000000000001

1. ‘a’ 문자의 아스키 코드 값을 출력하시오.
2. 10진수 255를 16진수로 출력하시오
3. 185.39485702를 소수점 이하 4자리까지 출력하시오.

//1. ‘a’ 문자의 아스키 코드 값을 출력하시오.
//2. 10진수 255를 16진수로 출력하시오.
//3. 185.39485702를 소수점 이하 4자리까지 출력하시오.

#include <stdio.h>
int main(void)
{
    printf("%d\n",'a');//1. ‘a’ 문자의 아스키 코드 값을 출력하시오.
    printf("%#X\n",255); //2. 10진수 255를 16진수로 출력하시오.
    printf("%.4f\n",185.39485702);//3. 185.39485702를 소수점 이하 4자리까지 출력하시오.
    return 0;
}

실행 결과
97
0XFF
185.3949

25. putchar, puts 함수

int putchar(int ch);

putchar 함수는 하나의 문자를 콘솔 화면에 출력하는 함수예요.

putchar 함수의 원형을 보면 반환 형식이 int이고 입력 매개 변수도 int죠.
입력 인자로 출력을 원하는 문자의 아스키 코드 값을 전달해요.
물론 ‘a’ 처럼 문자 리터럴 상수 표현을 사용할 수 있어요.
그리고 반환 값은 입력 인자로 전달한 문자의 아스키 코드 값이예요.

◈ putchar 사용 예

#include <stdio.h>
int main()
{
    int re = 0;
    re = putchar('h');
    printf(" re: %c \n",re);
    return 0;
}

◈ 실행 결과

h re: h
int puts(const char *str);

puts 함수는 입력 문자열을 콘솔 화면에 출력하는 함수예요.
반환 값은 성공 시에 음수가 아닌 값을 반환해요.
만약 실패하면 -1인 EOF를 반환하죠.

그리고 puts 함수에서는 입력 인자로 전달한 문자들 외에 개행 문자를 추가로 출력해 줘요.

◈ puts 함수를 사용하는 예

#include <stdio.h>
int main()
{
    int re = 0;
    re = puts("Hello");
    printf("re: %d \n",re);
    return 0;
}

◈ 실행 결과

Hello
re: 0

◈ 기본연습

1. putchar 함수를 이용하여 ‘a’를 입력 인자로 전달하였을 때와 97을 입력 인자로 전달하였을 때와 0x61을 입력 인자로 전달하였을 때의 결과를 확인하시오.

더보기

답:
모두 a 출력

2. putchar 함수를 이용하여 ‘0’을 입력 인자로 전달하였을 때와 48를 입력 인자로 전달하였을 때와 0x30을 입력 인자로 전달하였을 때의 결과를 확인하시오.

더보기

답:
모두 0 출력

3. putchar 함수를 이용하여 ‘A’를 입력 인자로 전달하였을 때와 65를 입력 인자로 전달하였을 때와 0x31를 입력 인자로 전달하였을 때의 결과를 확인하시오.

더보기

모두 A출력

//1. putchar 함수를 이용하여 ‘a’, 97, 0x61을 입력 인자로 전달하였을 때의 결과 비교
//2. putchar 함수를 이용하여 ‘0’, 48, 0x30을 입력 인자로 전달하였을 때의 결과 비교
//3. putchar 함수를 이용하여 ‘A’, 65, 0x31을 입력 인자로 전달하였을 때의 결과 비교
#include <stdio.h>
int main(void)
{
    putchar('a');
    putchar(97);
    putchar(0x61);
 
    putchar('0');
    putchar(48);
    putchar(0x30);
 
    putchar('A');
    putchar(65);
    putchar(0x41);
    return 0;
}

▷ 실행 결과

aaa000AAA

4. 다음의 두 개의 구문을 수행하는 프로그램을 작성하여 차이점을 확인하세요.

printf(“hello”);
puts(“hello”);
더보기

답:

printf 함수는 개행을 포함하지 않고 출력하고 puts는 개행을 포함하여 출력합니다.

//printf(“hello”);와 puts(“hello”); 비교
#include <stdio.h>
int main(void)
{
puts("hello");
puts("a");

    printf("hello");
    printf("a");

    return 0;
}

▷ 실행 결과

hello
a
helloa

26. scanf, scanf_s 함수

int scanf(const char * format, … );
int scanf_s(const char * format, … );

scanf와 scanf_s 함수는 표준 입력(키보드)에 입력한 내용을 포멧에 맞게 얻어오는 함수예요.
그리고 scanf_s 함수는 scanf 함수의 안전한 버전의 함수죠.

scanf 함수의 반환 값은 포멧 사양자에 맞게 변환한 개수이며 포멧 사양자는 printf에서 사용하는 것과 거의 같아요.
double 형식 실수를 입력받을 때 %f 대신 %lf를 사용하는 정도가 차이점이죠.

scanf 함수와 scanf_s 함수는 포멧에 맞지 않는 부분이 있으면 더 이상 작업을 진행하지 않아요.
그리고 %s 포멧은 공백이나 탭, 엔터를 만나기 전까지 문자열만 변환해요.
공백을 포함해서 문자열을 입력받을 때는 gets나 gets_s를 사용하세요.

그리고 scanf 함수에서는 입력한 내용을 호출한 곳의 변수에 설정하는 일을 합니다.
따라서 호출하는 곳에서는 변수의 주소를 전달하세요.
변수의 메모리 주소를 얻을 때는 주소 연산자(&)를 사용해요.

◈ scanf 함수를 사용하는 예

#include <stdio.h>
int main()
{
    int num=0;
 
    printf("번호를 입력하세요.\n");
    scanf("%d", &num);
    printf("입력한 번호는 %d 번입니다.\n", num);
    return 0;
}

◈ 실행 결과

번호를 입력하세요.
23
입력한 번호는 23 번입니다.

다음의 예제 코드를 보면 scanf 함수를 두 번 호출하고 있어요.
두 번째 scanf 함수를 호출하는 곳에서는 포멧 사양자로 “%d.%d.%d.%d”를 전달하고 있죠.
최종 사용자가 포멧과 다르게 123.45.23ab45 를 입력하면 포멧이 맞게 입력한 123.45.23 부분만 변환해요.
그리고 변환 성공한 개수 3을 반환하죠.

◈ scanf 함수를 사용하는 예

#pragma warning(disable:4996) //경고 메시지를 오류 목록에 표시하지 않게 함
#include <stdio.h>
int main()
{
    int a=0, b=0, c=0, d=0;
    int re = 0;
 
    printf("네 개의 정수를 다음의 예처럼 입력하세요.\n");
    printf("입력 예: 255.34.198.34\n");
    re = scanf("%d.%d.%d.%d",&a,&b,&c,&d); //포멧에 맞게 입력한 부분만 변환함
    printf("입력 포멧에 맞게 전달하여 변환이 성공한 개수는 %d개 입니다.\n",re);
    printf("입력한 것을 변환한 결과: %d.%d.%d.%d \n",a,b,c,d);
    return 0;
}

◈ 실행 결과

네 개의 정수를 다음의 예처럼 입력하세요.
입력 예: 255.34.198.34
123.45.23ab45
입력 포멧에 맞게 전달하여 변환 성공한 개수는 3개 입니다.
입력한 것을 변환한 결과: 123.45.23.0

그런데 scanf 함수를 사용하면 컴파일 경고 메시지가 나와요.
포멧에 맞게 변환하여 입력 인자로 받은 주소에 값을 지정하는 과정에서 전달받은 메모리 크기를 벗어날 위험이 있거든요.
이러한 버퍼 오버플로우 문제를 개선한 함수가 scanf_s 함수예요.
scanf_s함수는 대부분 scanf함수 사용법과 거의 같아요.
차이가 있는 부분은 문자나 문자열을 입력받을 때 버퍼 길이도 전달해야 한다는 것이죠.

◈ scanf_s 함수를 사용한 예

#include <stdio.h>
#define AVAIL(x) ((x>=0)&&(x<=255)) //유효한 수인지 판별하는 매크로
//IPv4주소로 유효한지 판별하는 매크로
#define AVAIL_IPv4(a,b,c,d) (AVAIL(a)&&AVAIL(b)&&AVAIL(c)&&AVAIL(d))
int main()
{
    char hostname[256];
    int a, b, c, d;
    int result;
    //IPv4 주소 입력
    printf("호스트 명과 IPv4 주소 입력(호스트 명 xxx.xxx.xxx.xxx) \n");
    result = scanf_s("%s %d.%d.%d.%d",hostname, sizeof(hostname), &a, &b, &c,& d);
 
    if (result < 5)//변환 개수가 4보다 작을 때
    {
        printf("포멧에 맞게 입력하지 않았습니다.\n");
    }
    else
    {
        if (AVAIL_IPv4(a, b, c, d))//유효한 IPv4 주소일 때
        {
            printf("%s의 IPv4 주소 %d.%d.%d.%d \n",hostname, a, b, c, d);
        }
        else
        {
            printf("유효한 IPv4 주소가 아닙니다.\n");
        }
    }
    return 0;
}

◈ 실행 결과

호스트 명과 IPv4 주소 입력(호스트 명 xxx.xxx.xxx.xxx)
ehclub.net 192.168.34.50
ehclub.net의 IPv4 주소 192.168.34.50

그리고 scanf 함수를 사용하고 경고 메시지를 확인하기 싫다면 #pragma 매크로를 이용하세요.

#pragma warning(disable:4996)

27. getchar, gets, gets_s 함수

int getchar(void);

getchar 함수는 최종 사용자가 입력한 스트림에서 하나의 문자 아스키 코드 값을 얻어오는 함수예요.

◈ getchar 함수를 사용한 예

#include <stdio.h>
int main()
{
    char c = '\0'; //char 형 변수 c를 선언하고 '\0'(널문자)로 초기화
    c = getchar();
    printf("문자: %c  아스키 코드 값: %d \n",c, c);
    c = getchar();
    printf("문자: %c  아스키 코드 값: %d \n",c, c);
    c = getchar();
    printf("문자: %c  아스키 코드 값: %d \n",c, c);
    return 0;
}

◈ 실행 결과

ab (최종 사용자가 ab를 입력했다고 가정)
문자: a 아스키 코드 값: 97
문자: b 아스키 코드 값: 98
문자:
    아스키 코드 값: 10
char * gets(char *buf);
char * gets_s(char *buf ,size_t size);

gets와 gets_s 함수는 최종 사용자가 입력한 스트림을 입력 인자로 받은 메모리에 문자열로 설정하는 함수죠.
반환 값은 입력 인자로 전달받은 메모리 주소를 그대로 반환해요.

gets 함수는 버퍼의 크기를 전달하지 않아 버퍼 오버플로우 버그가 발생할 수 있어요.
이를 개선한 함수가 gets_s 함수예요.

그리고 gets와 gets_s 함수는 공백을 포함하여 문자열을 입력받을 수 있어요.

◈ gets_s 함수를 사용한 예

#define MAX_NAME_LEN    100
#define MAX_ID_LEN         100
#include <stdio.h>
int main()
{
    char name[MAX_NAME_LEN+1] ="";
    char id[MAX_ID_LEN+1]="";
    printf("이름을 입력하세요.\n");
    printf("입력한 이름은 %s입니다.\n",gets_s(name,sizeof(name))); // 리턴 값을 이용해 출력
    printf("%s의 아이디를 입력하세요.\n",name);
    gets_s(id);
    printf("%s의 아이디는 %s입니다.\n",name,id);
    return 0;
}

◈ 실행 결과

이름을 입력하세요.
홍길동 (최종 사용자가 홍길동을 입력했다고 가정)
입력한 이름은 홍길동입니다.
홍길동의 아이디를 입력하세요.
eh.hong (최종 사용자가 eh.hong을 입력했다고 가정)
홍길동의 아이디는 eh.hong입니다.

28. 정리하기 (표준 입출력 개요)

1. 최종 사용자로부터 번호, 이름, 주소를 입력받아 출력하는 프로그램을 작성하시오.

더보기
//최종 사용자로부터 번호, 이름, 주소를 입력받아 출력하는 프로그램
#include <stdio.h>
int main(void)
{
    int num=0;
    char name[20]="";
    char addr[100]="";
 
    printf("번호:");
    scanf_s("%d",&num);
    printf("이름:");
    scanf_s("%s",name,sizeof(name));
    //fflush(stdin);//Visual Studio 2015에서는 fflush(stdin); 호출한다고 stdin 버퍼가 지워지지 않습니다.
    printf("주소:");
    gets_s(addr,sizeof(addr));
    printf("=== 입력한 데이터 ===\n");
    printf("번호:%d 이름:%s 주소:%s\n",num,name,addr);
    return 0;
}
//*개발 환경에 따라 표준 입력 처리가 조금씩 다릅니다.*

2. 다음의 두 개의 구문을 수행하는 프로그램을 작성하여 차이점을 확인하세요.

printf(“hello”);
puts(“hello”);
더보기

답:
printf 함수는 개행을 포함하지 않고 출력하고 puts는 개행을 포함하여 출력합니다.

//printf(“hello”);와 puts(“hello”); 비교
#include <stdio.h>
int main(void)
{
    puts("hello");
    puts("a");
 
    printf("hello");
    printf("a");
    
    return 0;
}

▷ 실행 결과

hello
a
helloa