정수 변환 규칙
- Misra Dir 4.6
- 기본 수치 유형 대신 크기와 부호가 표시된
typedef
를 사용해야한다.
C99
에서 stdint.h
에서 제공하는 유형을 사용해야 한다.
- explicit convsersion: 변환 연산자를 사용한 변환
- implicit convsersion: 특정 연산에 의한 변환
- int보다 작은 정수 타입은 연산이 수행될 때!! int, unsigned int로 변환
- 오버플로로 인한 산술 연산 에러 방지하기 위함
- bit 수가 많은 타입이 높은 순위 가진다.
- 각기 다른 signed 정수는 순위가 다르다.
- signed 정수 타입의 순위 자신보다 정밀도가 낮은 다른 signed보다 순위 높음
long long int
> long int
> int
> short int
> signed char
unsigned long long int
> unsigned long int
> unsigned short int
> unsigned char
- 이항 연산의 경우 두 피연산자는 같은 타입으로 변환된다.
- 두 개의 피연산자가 같은 타입이면 변환하지 않는다.
- 두 개의 피연산자가 같은 정수 타입이면 범위가 큰 타입으로 변환된다.
- unsigned 정수 타입의 피연산자가 다른 피연산자의 순위보다 크거나 같은 경우, signed 정수 타입의 피연산자는 unsigned 정수 타입으로 변환된다.
- 특히
unsigned int
+ int
할 때 주의!
- signed 정수 타입의 피연산자가 unsigned 정수 타입의 모든 값을 표현할 수 있는 경우, unsigned 정수 타입의 피연산자는 signed 정수 타입으로 변환된다.
- signed 정수 타입의 피연산자가 unsigned 정수 타입의 모든 값을 표현할 수 있는 경우, signed 정수 타입의 피연산자는 동일 타입의 unsigned 타입으로 두 피연산자 모두 변환된다.
1. 부적절한 코드
- 서로 다른 타입에 대해 연산을 수행할 경우 주의
1. 해결방법
- 타입 일치
오버플로우
unsigned
: wrapping 주의
- C99
unsigned
피연산자를 사용한 계산은 결코 오버플로가 발생하지 않는다. -> 대신 원하지 않는 값 래핑
- 값이 저장될 정수 타입으로 표현될 수 없는 경우, 나머지 연산으로 값을 줄여(wrap around) 표현하기 때문이다.
- 따라서, 신뢰할 수 없는 src 로 부터 얻어진 정수 값이 아래와 같은 곳에 사용되면 절대 래핑을 허용해서는 안된다.
- 배열 인덱스
- 포인터 연산의 일부
- 루프 카운터
- 메모리 할당 함수의 인자
- 그 밖의 보안에 민감한 코드
warpping 방지 코드
- 다음과 같은 코드를 넣어서 warapping 방지
signed
:오버플로X 보장하기
- C에서 정수 오버플로는 정의되지 않은 동작이다. 신뢰할 수 없는 src로 부터 얻은 값에 대한 연산이 아래에 사용되면 XX!!
1. 배열 인덱스
2. 포인터 연산의 일부
3. 루프 카운터
4. 메모리 할당 함수의 인자
5. 그 밖의 보안에 민감한 코드
'+'
overflow 방지
- 다음의 코드를 넣어 signed 연산 overflow 방지하자
'*'
overflow 방지
- signed 정수의 곱셈 연산은 정수 오버플로우가 일어날 수 있기 때문에 다음의 코드는 의도하지 않는 결과를 초래할 수 있다.
- 오버플로우 발생 전 미리 테스트
'/'
overflow 방지
- signed 정수의 나눗셈 연산은
- 피제수가 최소값(INT_MIN)
- 제수가 음수일 때
: 2's complement 표기에서 정수 오버플로가 일어날 수 있다.
해결 방법
- 오버플로우가 발생하기 전에 미리 테스트
'/'
floating point
- overflow는 아니지만 나누어 떨어 지지 않을 때!!! 부동 소수점 변수에 값을 할당한 경우 정보 손실
- 표현식의 정수 중 하나를 부동 소수점 타입으로 변환해 피할 수 있다.
1. 부적절한 코드
- 정수 타입의 연산 결과는 항상 정수
- 부동 소수점 결과가 나왔을 경우, 소수 부분은 잘려 나갈 수 있다.
그냥 나누면 오답!
1-1. 해결 방법
- 피연산자 하나를 부동 소수점으로 표기
1-2. 해결 방법
- 정수 값을 부동소수점 변수에 저장하여 처리
'%'
: 나머지가 양수 가정 X
- C99
- 나눗셈의 결과는 첫 번째 피연산자를 두 번째 피연산자로 나눈 몫이다.
나머지 연산자의 결과는 나머지다.
- 두 연산에서 두 번째 연산자가 0인 경우, 동작은 지정되어 있지 않다.
- 정수가 나눠질 때, 나머지 연산자의 결과는 수학적인 몫으로 소수점 이하의 값은 버려진다.
a/b
의 몫이 표현 가능하다면 표현식 (a/b)*b+a%b
는 a와 동일하다.
- 버려지는 나머지의 소수점 이하 부분을 '0쪽으로 잘림(truncation toword zero)'라고 한다.
- C99에서 결과의 부호는 피제수(첫 번째 피연산자)의 부호를 따른다.
즉, 피제수가 signed int 이고, 음수라면 나머지도 -
부호를 가질 수 있다.
해결 방법
- 다음의 코드에서
front
와 rear
는 signed 정수로 구현 -> 항상 양수임을 보장할 수 없다.
- 나머지 연산 결과 음수가 될 수 있으며 결과적으로 매우 잘못된 메모리 참조가 발생할 수 있다.
- unsgined타입을 사용해 문제를 해결하자!
sizeof 문제: arch別
- 아키텍처별로 int의 실제 Byte할당 문제
{,s}ize_t
활용하자.
size_t
: unsigned long
ssize_t
: long
Data Type | iAPX86 | IA-32 | IA-64 | SPARC-64 | ARM-32 | Alpha | 64-bit Linux, FreeBSD, NetBSD, OpenBSD |
---|
char | 8 | 8 | 8 | 8 | 8 | 8 | 8 |
short | 16 | 16 | 16 | 16 | 16 | 16 | 16 |
int | 16 | 32 | 32 | 32 | 32 | 32 | 32 |
long | 32 | 32 | 32 | 64 | 32 | 64 | 64 |
long long | N/A | 64 | 64 | 64 | 64 | 64 | 64 |
Pointer | 16/32 | 32 | 64 | 64 | 32 | 64 | 64 |
limits.h
limits.h
는 파일 안에 모든 일치하는 플랫폼에 대한 표준 정수 타입의 범위를 결정하고 매크로를 가지고 있다.
UNIX_MAX
: unsigned int 가 가질 수 있는 최댓 값
LONG_MIN
: long int 가 가질 수 있는 최소 값
stdint.h
uintmax_t
- 코드를 사용하는 Archi에서
unsigned int
가 담을 수 있는 최대값의 자료형
1. 부적절한 코드
- unsigned int 두 개를 곱할 때 모든 비트가 유지될 것이라고 가정하고 있다.
- 그러나 이코드는 64bit Linux에서는 정상작동하지만, Windows에서는 실패할 수 있다.
1. 해결방법
- 아주 큰 타입 사용
inttypes.h
- 아키텍처別 int 제공
intptr_t
uint64_t
bit 연산
- bit 연산:
~
, >>
, <<
, |
, ^
- int 자료형 정수에 bit 연산 피할 것
- signed 정수에 대한 비트 연산 구현마다 다르게 정의
- 따라서.. unsigned !!!! 정수 일 때만 비트 연산자 사용하자.
- unsigned int 는
>>
<<
bit shift 연산 시, 0으로 채워짐
- C99 표준에 따르면 만일
int
타입으로 승계될 때, 원래 타입의 모든 값을 표현할 수 있다면 값은 int
로 반환되며 그렇지 않은 경우 unsigned int
로 변환된다.
1-1. 1번 int 일 때 비트연산 지양의 예:
- 비트 필드에서 int bit field를 지정하면 구현에 따라 그 값(-1 || 255)이 달라 질 수 있다.
1. 해결 방법
- 부호의 유무를 명시적으로 선언하면 의도가 명확해진다.
1-2. 1번 int 일 때 비트연산 지양의 예
- int 일 때, bit 연산 지양
1-2. 해결 방법
- unsigned 정수를 사용하면 비어 있는 비트가 0으로 채워지게 되므로 오버 플로우 발생 XX
음수나 피연산자의 비트보다 더 많은 비트 시프트 금지
- 오른쪽 피연산자의 값이 음수 || 승계된 왼쪽 피연산자의 값과 같거나 큰 경우 undefined behaviour
1. 부적절한 코드
1. 해결 방법
- 오른쪽 피연산자가 음수인지? 검사한다.
2. 부적절한 코드
- 피연산자의 비트보다 더 많은 비트를 시프트하는 것은 undefined expected
2. 해결 방법
- 시프트 연산 수행 전 검사 수행