핵심 요약
C·
본문
자료형이 필요한 이유
자료형은 값의 표현 범위, 저장 방식, 허용 연산, 메모리 사용량을 결정한다. 같은 비트열도 자료형에 따라 정수, 실수, 문자, 주소 등으로 다르게 해석된다.
소스의 리터럴·변수
│
▼
자료형 결정
│
├─ 저장 크기와 표현 범위
├─ 가능한 연산
├─ 형변환 규칙
└─ 결과 자료형
다음 경계를 구분한다.
- 정수와 실수 연산 결과
- 정수 나눗셈과 나머지
- 암시적·명시적 형변환
- 부호 있는 정수와 부호 없는 정수
- 연산자 우선순위와 단락 평가
- 오버플로·정밀도 손실
C· Java· Python의 자료형 비교
| 구분 | C | Java | Python |
|---|---|---|---|
| 정수 | char, short, int, long 등 | byte, short, int, long | int |
| 실수 | float, double 등 | float, double | float |
| 문자 | 정수형 문자 코드로 처리 | char는 UTF-16 코드 단위 | 길이 1인 str |
| 논리 | C99 이후 _Bool, 일반식에서는 0/비0 | boolean | bool |
| 문자열 | 문자 배열 또는 포인터 | String 객체 | str 객체 |
| 타입 결정 | 정적 타입 | 정적 타입 | 동적 타입 |
C의 정수 자료형 크기는 표준이 최소 범위와 순서 관계를 정하고, 실제 크기는 구현에 따라 달라질 수 있다. Java의 기본 정수형 크기는 언어 사양에서 고정되어 있다. Python의 정수는 일반적인 기계어 정수보다 큰 값까지 표현할 수 있지만 메모리 한계는 존재한다.
정수 표현과 오버플로
n비트 부호 없는 정수의 범위는 0 ~ 2^n - 1이다. 일반적인 2의 보수 기반 n비트 부호 있는 정수의 범위는 -2^(n-1) ~ 2^(n-1)-1이다.
8비트 unsigned: 0 ~ 255
8비트 signed : -128 ~ 127
Java의 정수 연산은 정해진 비트 폭을 넘어가면 하위 비트만 남는 방식으로 순환한다. 반면 C에서 부호 있는 정수 오버플로는 정의되지 않은 동작이므로 특정 결과가 반드시 나온다고 가정하면 안 된다. 부호 없는 정수는 모듈러 연산처럼 순환한다.
int x = Integer.MAX_VALUE;
System.out.println(x + 1); // -2147483648
Python의 int는 고정 폭이 아니므로 같은 방식의 정수 오버플로는 일반적으로 발생하지 않는다.
실수 표현과 정밀도
부동소수점은 제한된 비트로 실수를 근사한다. 따라서 10진 소수 중 일부는 2진 부동소수점으로 정확히 표현할 수 없다.
print(0.1 + 0.2 == 0.3) # False가 될 수 있다.
실수 비교에서는 문제 목적에 따라 허용 오차를 사용한다.
a = 0.1 + 0.2
print(abs(a - 0.3) < 1e-9) # True
연산자 종류와 우선순위
| 종류 | 대표 연산자 | 핵심 |
|---|---|---|
| 산술 | + - * / % | 정수·실수 자료형에 따라 결과가 달라진다. |
| 비교 | < <= > >= == != | 결과는 참·거짓이다. |
| 논리 | && || !, Python의 and or not | 단락 평가에 주의한다. |
| 비트 | & | ^ ~ << >> | 정수 비트열에 적용한다. |
| 대입 | = += -= *= | 오른쪽 계산 후 왼쪽에 저장한다. |
| 조건 | 조건 ? 참값 : 거짓값 | C·Java의 삼항 연산자이다. |
일반적인 우선순위의 큰 흐름은 다음과 같다.
괄호·호출↓단항 연산↓곱셈·나눗셈·나머지↓덧셈·뺄셈↓이동 연산↓비교·동등↓비트 연산↓논리 AND·OR↓조건·대입괄호로 의도를 명확히 표현하고, 괄호가 없는 식은 단계별로 분해해 평가 순서를 확인한다.
정수 나눗셈과 나머지
int a = 7 / 2; // 3
double b = 7 / 2; // 3.0: 나눗셈이 먼저 정수로 수행됨
double c = 7 / 2.0; // 3.5
int x = 7 / 2; // 3
double y = (double) 7 / 2; // 3.5
print(7 / 2) # 3.5: 일반 나눗셈
print(7 // 2) # 3: 몫 연산
print(7 % 2) # 1
음수의 몫과 나머지는 언어 규칙이 다를 수 있으므로 언어를 확인해야 한다. Python의 //는 음의 무한대 방향으로 내림한다.
형변환
형변환은 값의 자료형을 다른 자료형으로 바꾸는 과정이다.
피연산자 자료형 확인
│
▼
연산을 위한 승격
│
▼
연산 결과 생성
│
▼
대입 대상형으로 변환
- 확대 변환: 표현 범위가 넓은 형으로 이동한다. 일반적으로 정보 손실 가능성이 작다.
- 축소 변환: 표현 범위가 좁은 형으로 이동한다. 값 손실이나 잘림 가능성이 있다.
- 명시적 변환: 프로그래머가 캐스트를 작성한다.
- 암시적 변환: 언어 규칙이 자동으로 변환한다.
int i = 10;
double d = i; // 확대 변환
int n = (int) 3.9; // 3, 소수부 절삭
double d = 3.9;
int n = (int)d; // 일반적으로 3
Python은 변수 자체의 고정 자료형을 바꾸는 것이 아니라 변환 함수가 새 객체를 만든다.
n = int("42")
s = str(42)
f = float("3.5")
단락 평가
논리식은 결과가 이미 확정되면 뒤의 피연산자를 평가하지 않을 수 있다.
if (p != NULL && p->value > 0) {
/* p가 NULL이면 오른쪽을 평가하지 않는다. */
}
items = []
if items and items[0] > 0:
print("positive")
단락 평가는 안전한 조건 검사에 활용되지만, 오른쪽 식의 부수효과에 의존하면 코드를 이해하기 어려워진다.
코드 판독 절차
① 변수의 선언 자료형 표시
② 리터럴 자료형 확인
③ 괄호와 우선순위대로 부분식 계산
④ 암시적 승격·형변환 적용
⑤ 정수 나눗셈·오버플로·정밀도 확인
⑥ 대입 후 최종 자료형과 값 기록
정수 승격과 혼합 연산
작은 정수형은 연산 전에 더 큰 정수형으로 승격될 수 있다. Java에서 byte, short, char의 산술 결과는 일반적으로 int가 된다.
byte a = 10, b = 20;
// byte c = a + b; // 컴파일 오류: a+b의 형은 int
int c = a + b; // 30
복합 대입은 연산 뒤 대상형으로의 암시적 축소 변환을 포함한다.
byte x = 120;
x += 10; // (byte)(x + 10)과 유사, 결과 -126
C에서도 char와 short는 정수 승격을 거친다. 부호 있는 형과 부호 없는 형을 섞으면 예상과 다른 비교 결과가 나올 수 있으므로, 문제에서 비트 폭과 형을 먼저 표시해야 한다.
비트 이동과 마스크
비트 k 설정: x | (1 << k)
비트 k 해제: x & ~(1 << k)
비트 k 검사: (x & (1 << k)) != 0
비트 k 반전: x ^ (1 << k)
Java의 >>는 부호를 유지하는 산술 이동이고, >>>는 왼쪽을 0으로 채우는 논리적 오른쪽 이동이다. C에서는 음수인 부호 있는 정수의 이동과 표현 범위 밖 왼쪽 이동이 이식성 문제나 정의되지 않은 동작을 만들 수 있으므로 비트 연산에는 부호 없는 형을 쓰는 것이 안전하다.
System.out.println(-8 >> 1); // -4
System.out.println(-8 >>> 1); // 2147483644
평가 순서와 부수효과
같은 식 안에서 한 변수를 여러 번 변경하면 언어별 규칙이 크게 다르다.
int i = 1;
int y = i++ + ++i; // Java는 피연산자를 왼쪽부터 평가: y=4, i=3
C에서 i++ + ++i처럼 순서가 정해지지 않은 변경을 겹치면 정의되지 않은 동작이므로 특정 출력값을 계산하지 않는다. 코드를 해석할 때는 언어가 무엇인지, 평가 순서가 규정되어 있는지, 한 변수의 변경과 읽기가 충돌하는지를 먼저 확인한다.
형변환 경계 판독
| 표현 | 결과 원리 |
|---|---|
Java (int)-3.9 | 0 방향으로 소수부를 버려 -3 |
Python int(-3.9) | 0 방향으로 변환해 -3 |
Python math.floor(-3.9) | 음의 무한대 방향으로 내려 -4 |
Java byte b=(byte)130 | 하위 8비트만 남아 -126 |
실수에서 정수로 바꾸는 규칙과 정수 나눗셈의 규칙은 별개다. 또한 C에서 표현할 수 없는 실수를 정수형으로 변환하는 상황은 이식 가능한 단일 결과로 단정하지 않는다.
복습 문제
답을 생각한 뒤 정답과 근거를 펼쳐 보세요.
Java에서 byte a=10, b=20;일 때 a+b의 결과 자료형은 무엇인가?
정답과 근거
정답: int
Java의 byte, short, char는 일반적인 산술 연산 전에 int로 승격된다.
Java에서 int x=-8;일 때 x >>> 1의 값은 무엇인가?
정답과 근거
정답: 2147483644
>>>는 32비트 값의 왼쪽을 0으로 채운다. -8의 비트열 0xFFFFFFF8을 한 칸 이동하면 0x7FFFFFFC가 된다.
정수 x의 k번째 비트를 0으로 만드는 일반적인 비트식은 무엇인가?
정답과 근거
정답: x & ~(1 << k)
k번째 비트만 0인 마스크와 AND하면 해당 비트만 지워진다.
Java에서 (int)-3.9와 Python에서 math.floor(-3.9)의 결과는 각각 무엇인가?
정답과 근거
정답: -3, -4
정수 캐스트는 0 방향으로 소수부를 버리지만 floor는 음의 무한대 방향으로 내린다.
C에서 i++ + ++i의 결과를 특정 값으로 계산하면 안 되는 이유는 무엇인가?
정답과 근거
정답: 한 변수에 대한 변경이 순서가 정해지지 않은 채 겹치는 정의되지 않은 동작이기 때문이다.
C에서는 이 표현의 특정 출력이나 최종값을 보장하지 않는다.