수강생 전용 보너스 시리즈예요. 수강생은 로그인하면 바로 볼 수 있어요.

2강 / 전체 40강

C언어 기초 — 자료형·연산자·입출력

12분 읽기 조회 0

int·float·double 형식 지정자와 연산자 우선순위(비트·시프트 포함), 암묵적 형변환·scanf 주소 연산자를 코드 추적으로 완전 정리

🎯 학습 목표

이 강을 마치면 C언어의 기본 자료형이 각각 어떤 값을 담는지, 그 값을 출력·입력할 때 어떤 형식 지정자를 써야 하는지 정확히 구분할 수 있습니다. 또한 산술·관계·논리·비트 연산자의 우선순위를 표로 암기하지 않고도 코드를 보면 계산 순서를 스스로 추론할 수 있게 되며, 서로 다른 자료형이 한 식에서 섞였을 때 벌어지는 암묵적 형변환을 예측할 수 있습니다. 마지막으로 scanf에서 왜 변수 앞에 &를 붙여야 하는지 그 이유까지 이해하게 됩니다. 이번 강은 정보처리기사 실기에서 C언어 코드 추적 문제를 풀기 위한 가장 기초적인 뼈대이므로, 다음 강(제어문)부터 나오는 모든 코드를 읽는 데 그대로 쓰이는 내용입니다.

📌 왜 실기에서 C언어 자료형·연산자가 이렇게 중요한가

이 섹션에서는 자료형과 연산자가 실기 시험에서 왜 사소해 보이면서도 결정적인 감점 포인트가 되는지 살펴보겠습니다. C언어는 정보처리기사 실기의 프로그래밍 언어 활용 영역에서 Java, Python과 함께 코드 분석 문제로 출제되는 대표 언어입니다. 그런데 C언어 문제는 대부분 "이 코드를 실행하면 무엇이 출력되는가"를 손으로 추적해서 답하는 방식이라, 문법을 대략 아는 것과 정확한 계산 결과를 숫자로 써 내는 것 사이에 큰 차이가 생깁니다. 예를 들어 정수형끼리 나눗셈을 하면 소수점 이하가 버려진다는 사실을 모르면 7 / 2의 결과를 2.5로 잘못 적게 되고, 이는 부분 점수 없이 그대로 오답 처리됩니다.

C언어는 Python이나 Java와 달리 변수를 선언할 때 자료형을 명시적으로 지정해야 하는 정적 타입 언어입니다. 이 때문에 자료형마다 저장할 수 있는 값의 범위와 정밀도가 다르고, 서로 다른 자료형끼리 연산할 때 컴파일러가 자동으로 형을 맞추는 규칙(암묵적 형변환)이 존재합니다. 이 규칙을 모르면 코드 추적 문제에서 정확한 출력값을 계산할 수 없습니다. 또한 C언어의 연산자는 종류가 많고 우선순위 체계가 촘촘하기 때문에, 같은 식이라도 우선순위를 잘못 적용하면 완전히 다른 결과가 나옵니다. 이번 강에서 이 기초를 확실히 다져두면, 다음 강부터 이어지는 조건문·반복문·배열·포인터 강의에서 훨씬 수월하게 코드를 따라갈 수 있습니다.

💡 기본 자료형과 형식 지정자

이 섹션에서는 C언어의 대표적인 기본 자료형과, 이 값을 printf·scanf로 주고받을 때 쓰는 형식 지정자를 정리하겠습니다. 자료형은 크게 정수형과 실수형, 문자형으로 나뉩니다.

자료형의미일반적인 크기printf 형식 지정자
int정수4바이트%d
char문자 1개(아스키 코드값)1바이트%c (정수로 다룰 때는 %d)
float실수(단정밀도)4바이트%f
double실수(배정밀도)8바이트%f (scanf에서는 %lf)
long큰 정수컴파일러·운영체제에 따라 4바이트 또는 8바이트%ld

여기서 표에 적힌 "일반적인 크기"는 절대적인 값이 아니라는 점을 주의해야 합니다. C 표준은 각 자료형의 최소 크기 범위만 규정할 뿐, 정확한 바이트 수는 컴파일러와 운영체제(정확히는 데이터 모델)에 따라 달라질 수 있습니다. 특히 long은 위와 같이 환경에 따라 다르게 취급되는 대표적인 자료형이므로, 실기 문제에서 long의 크기를 직접 묻는 경우가 아니라면 값 자체의 연산 결과에 집중하는 것이 안전합니다.

형식 지정자에서 가장 자주 실수하는 부분은 double입니다. printf에서는 float든 double이든 모두 %f로 출력할 수 있는데, 이는 가변 인자 함수인 printf에 값을 전달할 때 float가 자동으로 double로 승격되기 때문입니다. 하지만 scanf는 이러한 승격이 일어나지 않으므로, double 변수에 값을 입력받으려면 반드시 %lf를 써야 합니다. scanf에서 double 변수에 %f를 쓰면 메모리에 값이 잘못된 크기로 저장되어 엉뚱한 값이 들어가는 오류가 발생하므로, 이 둘을 구분하는 것은 실기 채점에서 실제로 자주 나오는 함정입니다.

💻 연산자 우선순위와 형변환 — 코드로 확인하기

이 섹션에서는 실제 코드를 통해 연산자 우선순위와 형변환이 결과에 어떻게 영향을 미치는지 살펴보겠습니다. C언어의 연산자는 우선순위가 높은 순서대로 단항 연산자(!, ++, --, sizeof) → 곱셈·나눗셈·나머지(*, /, %) → 덧셈·뺄셈(+, -) → 시프트 연산자(<<, >>) → 관계 연산자(<, >, <=, >=) → 등가 연산자(==, !=) → 비트 AND(&) → 비트 XOR(^) → 비트 OR(|) → 논리 AND(&&) → 논리 OR(||) → 대입 연산자(=, += 등) 순서로 적용됩니다. 모든 순위를 한 번에 외우기보다, "단항 → 산술 → 시프트 → 관계/등가 → 비트 → 논리 → 대입"이라는 큰 흐름만 기억해두면 대부분의 실기 문제는 충분히 풀 수 있습니다.

#include <stdio.h>

int main() {
    int a = 7, b = 2;
    double c = 7.0;
    int result1 = a / b;       // 정수끼리 나눗셈
    double result2 = c / b;    // 실수와 정수의 나눗셈
    int result3 = a % b + a * b;

    printf("%d\n", result1);
    printf("%.1f\n", result2);
    printf("%d\n", result3);
    return 0;
}

이 코드를 한 줄씩 추적해보겠습니다. result1은 a / b인데, a와 b가 모두 int이므로 나눗셈도 정수 나눗셈으로 계산됩니다. 7을 2로 나누면 수학적으로는 3.5지만, 정수 나눗셈은 소수점 이하를 버리므로 결과는 3입니다. result2는 c / b인데, c가 double이므로 암묵적 형변환이 일어나 b(int)가 double로 자동 승격된 뒤 실수 나눗셈이 수행됩니다. 따라서 7.0을 2.0으로 나눈 3.5가 그대로 계산되어 %.1f 형식으로 3.5가 출력됩니다. result3은 우선순위에 따라 %와 *가 +보다 먼저 계산됩니다. a % b는 7을 2로 나눈 나머지인 1이고, a * b는 7 곱하기 2인 14이므로, 최종적으로 1 + 14 = 15가 출력됩니다. 이렇게 "자료형이 섞였는가"와 "연산자 우선순위가 무엇인가"라는 두 질문을 스스로 던지며 한 줄씩 계산하는 습관이 실기 코드 추적의 핵심입니다.

scanf에서 &(주소 연산자)를 쓰는 이유도 함께 짚어보겠습니다. scanf("%d", &a);처럼 작성하는 이유는, scanf가 값을 반환하는 대신 지정된 메모리 주소에 직접 값을 써넣는 방식으로 동작하기 때문입니다. 만약 &를 빠뜨리고 scanf("%d", a);라고 쓰면, a에 저장된 값(쓰레기 값일 수도 있는 정수)을 주소로 착각해서 그 위치에 값을 쓰려고 시도하게 되어 프로그램이 비정상 종료되거나 예측할 수 없는 동작을 하게 됩니다. 다만 배열이나 문자열은 이름 자체가 첫 번째 원소의 주소로 자동 변환(배열의 포인터 감쇠)되므로, char 배열에 문자열을 입력받을 때는 scanf("%s", str);처럼 & 없이 배열 이름만 씁니다. 이 차이는 다음 배열·포인터 강의에서 더 자세히 다루겠습니다.

🔢 비트 연산자 — &, |, ^, ~, <<, >>

이 섹션에서는 정수를 2진수(비트) 단위로 직접 다루는 비트 연산자를 살펴보겠습니다. 지금까지 살펴본 &&·||가 "참·거짓"이라는 논리값을 다루는 논리 연산자였다면, 비트 연산자는 정수를 이루는 비트 하나하나에 연산을 적용한다는 점에서 완전히 다른 연산자입니다. &(비트 AND)는 두 비트가 모두 1일 때만 1이 되고, |(비트 OR)는 둘 중 하나라도 1이면 1이 되며, ^(비트 XOR)는 두 비트가 서로 다를 때만 1이 됩니다. ~(비트 NOT)는 모든 비트를 반전시키고, <<(왼쪽 시프트)와 >>(오른쪽 시프트)는 비트 전체를 지정한 칸수만큼 밀어냅니다.

#include <stdio.h>

int main() {
    int a = 6;   // 2진수 0110
    int b = 3;   // 2진수 0011

    printf("%d\n", a & b);
    printf("%d\n", a | b);
    printf("%d\n", a ^ b);
    printf("%d\n", a << 1);
    printf("%d\n", b >> 1);
    return 0;
}

이 코드를 비트 단위로 추적해보겠습니다. a는 2진수로 0110, b는 0011입니다. a & b는 같은 자리 비트가 둘 다 1인 곳만 1이 되므로 0010, 즉 2가 출력됩니다. a | b는 둘 중 하나라도 1이면 1이 되므로 0111, 즉 7이 출력됩니다. a ^ b는 서로 다른 자리만 1이 되므로 0101, 즉 5가 출력됩니다. a << 1은 0110의 모든 비트를 왼쪽으로 한 칸씩 밀고 오른쪽 빈자리를 0으로 채우므로 1100, 즉 12가 출력되는데, 이는 결과적으로 a에 2를 곱한 것과 같은 효과입니다. b >> 1은 0011을 오른쪽으로 한 칸 밀어 0001, 즉 1이 출력되며, 이는 b를 2로 나눈 몫과 같은 효과입니다. 이렇게 왼쪽 시프트는 2를 곱하는 효과, 오른쪽 시프트는 2로 나누는 효과를 낸다는 감각을 기억해두면 시프트 연산 문제를 빠르게 계산할 수 있습니다.

⚠️ 자주 하는 실수와 주의점

이 섹션에서는 자료형과 연산자를 다룰 때 수험생들이 실제로 반복하는 실수를 짚어보겠습니다. 가장 흔한 실수는 정수 나눗셈과 실수 나눗셈을 혼동하는 것입니다. 두 피연산자가 모두 정수형이면 결과도 정수형이 되어 소수점이 버려진다는 사실을 놓치면, 코드 추적 문제에서 소수점이 있는 답을 적어 틀리는 경우가 매우 많습니다. 반대로 실수 연산인데 정수로 반올림해서 답을 적는 실수도 자주 나옵니다.

두 번째는 %(나머지) 연산자를 실수형에 적용할 수 있다고 착각하는 경우입니다. C언어에서 % 연산자는 정수형 피연산자에만 사용할 수 있으며, float나 double에 %를 쓰면 컴파일 오류가 발생합니다. 세 번째는 printf와 scanf의 형식 지정자를 그대로 섞어 쓰는 실수입니다. 앞서 살펴본 것처럼 double 변수를 입력받을 때 %lf 대신 %f를 쓰는 오류가 대표적입니다. 네 번째는 우선순위를 무시하고 왼쪽부터 순서대로 계산하는 습관입니다. 곱셈·나눗셈·나머지가 덧셈·뺄셈보다 항상 먼저 계산된다는 규칙을 놓치면, 위 예제의 result3 같은 문제에서 계산 순서를 완전히 잘못 짚게 됩니다. 실기 답안지에는 계산 과정이 아니라 최종 출력값만 적기 때문에, 중간 계산 하나만 틀려도 부분 점수 없이 전체를 잃게 됩니다. 다섯 번째는 비트 연산자 &·|와 논리 연산자 &&·||를 같은 것으로 착각하는 것입니다. 겉모양은 비슷하지만 &·|는 정수를 비트 단위로 계산해 정수 결과를 내고, &&·||는 두 조건을 논리적으로 판단해 참(1) 또는 거짓(0)만을 결과로 냅니다. 예를 들어 2 & 1은 비트 단위 계산 결과인 0이지만, 2 && 1은 둘 다 0이 아니므로(둘 다 참으로 취급되므로) 1이 되어 완전히 다른 값이 나옵니다.

🚀 심화 — 형변환을 명시적으로 다루는 캐스트 연산자

이 섹션에서는 암묵적 형변환과 짝을 이루는 명시적 형변환(캐스트, cast)을 살펴보겠습니다. 지금까지 살펴본 형변환은 컴파일러가 자동으로 처리하는 암묵적 형변환이었지만, C언어는 프로그래머가 직접 자료형을 지정해서 변환하는 캐스트 연산자도 제공합니다. 문법은 변환하고 싶은 값 앞에 괄호로 감싼 자료형을 붙이는 형태로, (double)a / b처럼 사용합니다. 이렇게 작성하면 a가 int였더라도 double로 먼저 변환된 뒤 나눗셈이 실행되므로, b도 암묵적으로 double로 승격되어 실수 나눗셈 결과를 얻을 수 있습니다.

실기 문제에서는 정수 나눗셈의 결과를 실수로 정확히 얻고 싶을 때 이 캐스트를 의도적으로 사용하는 코드가 자주 출제됩니다. 예를 들어 (int)(a + b) / (double)c처럼 캐스트가 식의 일부에만 적용된 코드가 나오면, 캐스트가 어느 항에 걸리는지 괄호 범위를 정확히 파악하는 것이 중요합니다. 캐스트 연산자는 우선순위가 매우 높아 단항 연산자와 같은 수준으로 취급되므로, 바로 뒤에 오는 값 하나에만 적용된다는 점을 기억해두면 헷갈리지 않습니다. 이러한 형변환 감각은 이후 배열·포인터·함수 강의에서 자료형이 더 복잡하게 얽힌 코드를 읽을 때도 그대로 이어지는 기본기입니다.

📝 핵심 요약

  • 기본 자료형은 int(정수)·char(문자)·float(단정밀도 실수)·double(배정밀도 실수)·long(큰 정수)이며, 각각 값의 범위와 크기가 다릅니다.
  • printf에서는 float와 double 모두 %f를 쓸 수 있지만, scanf에서 double은 반드시 %lf를 써야 합니다.
  • 연산자 우선순위는 크게 "단항 → 산술(*, /, % → +, -) → 시프트(<<, >>) → 관계/등가 → 비트(&, ^, |) → 논리 → 대입" 순서로 적용됩니다.
  • 정수형끼리의 나눗셈은 소수점이 버려지며, 실수형이 하나라도 섞이면 나머지 피연산자가 암묵적으로 실수형으로 승격됩니다.
  • 비트 연산자(&, |, ^, ~, <<, >>)는 정수를 비트 단위로 계산하며, 논리 연산자(&&, ||)와는 결과가 완전히 다릅니다.
  • scanf가 변수 앞에 &를 요구하는 이유는 값을 반환하는 대신 지정된 메모리 주소에 직접 값을 써넣기 때문입니다(배열·문자열은 예외).

다음 강에서는 이번 강에서 다진 자료형과 연산자를 바탕으로, if·switch 조건문과 for·while 반복문이 섞인 코드의 출력 결과를 추론하는 방법을 실전 문제 위주로 다루겠습니다.

📚 참고 자료

관련 주제

  • C언어 자료형
  • 형식 지정자
  • 연산자 우선순위
  • 비트 연산자
  • 암묵적 형변환
  • scanf
  • 명시적 형변환
  • 시프트 연산
  • 자격증
  • 자격증 강의
  • 정보처리기사 실기 완전정복 40강
  • NUGUNA
  • 누구나

📚 시리즈 전체 공유

정보처리기사 실기 완전정복 40강

이 강의가 속한 시리즈는 총 40강(수강생 전용 보너스 · 1~3강 무료)입니다. 처음부터 배우려는 동료에게 시리즈 전체를 알려 주세요.

댓글

0/1000

불러오는 중...