26. scanf, scanf_s 함수

C언어 – 표준 입출력

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)