컴퓨터는 엄격해서 16-bit integer와 32-bit integer 간의 연산도 허용하지 않는다.
C는 서로 다른 type끼리의 연산을 허용한다. integer, floating-point numbers, characters
implicit conversion: 프로그래머의 관여 없이 컴파일러에서 자동적으로 변환하는 것.
다음의 경우에 implicit conversion이 이루어진다.
1. 산술 연산이나 논리연산의 피연산자(operand)가 같은 형이 아닌 경우(usual arithmetic conversions)
2. assign하는 변수(좌변)와 우변의 표현식의 type이 다를 때
3. 함수를 호출할 때 argument가 해당하는 parameter와 type이 다를 때
4. 함수의 return type과 return statement에 있는 expression의 type이 다른 경우
여기서는 앞의 두가지 경우를 다루고, 나머지는 9장에서 다룬다.
The Usual Arithmetic Conversions
binary, arithmetic, relational, equality operation에서 피연산자들의 type이 다를 때
"narrow": 거칠게 표현해서, A type을 저장하는 데 B type을 저장하는 것 보다 더 적은 바이트가 필요하다면, A type이 B type보다 더 narrow하다고 한다.
promotion: 서로 다른 두 type이 피연산자일 때, 더 narrow한 type을 다른 type으로 변환하는 것. 더 적은 범위를 표현하는 type을 더 넓은 범위의 type으로 변환
integral promotion: char, short int 를 int로 변환
promotion 단계
두 타입중 하나가 floating type인 경우
float -> double -> long double
두 타입 모두 floating type이 아닌 경우
int -> unsigned int -> long int -> unsigned long int
int가 unsigned int로 변환되는 것에 유의하자. unsigned int 10과 int -10을 비교할 때 -10은 unsigned int로 promote되면서 4,294,967,296이 더해지면서(4,294,967,295가 unsigned int의 가장 큰 값) 이상한 값이 나온다. unsigned integer는 최대한 사용하지 않는 것이 좋고, 만약에 쓰게 된다면 signed integer과 엮이는 일이 절대 없어야 한다.
int + char = int
int + short = int
unsigned int + int = unsigned int
long + unsigned int = long
float + unsigned long = float
double + float = double
long double + double = long double
Conversion During Assignment
assignment에 있어서는 arithmetic conversion이 아닌 더 단순한 룰이 적용된다.
우변에 있는 표현식의 type이 좌변의 변수와 같은 type으로 변환된다.
int = char -> 우변이 int로 변환
double = float -> 우변이 double로 변환
int i;
i = 842.97 -> 우변 842가 int로 변환되어 i = 842가 된다.
우변의 type을 더 narrow하게 변환해야 하는 경우, 의미없는 결과가 나올 수 있다
ex) i = 1.0e20;
부동 소수점 숫자를 입력하는 경우 기본적으로 double type이다. 만약 float으로 저장하고 싶다면, 뒤에 소문자 f를 붙인다.
3.14159 -> double
3.14159f -> float
C99에서의 usual arithmetic conversion에 대한 설명은 생략.
Casting
컴파일러에서 암시적으로 하는 것이 아닌, 프로그래머가 명시적으로 형변환을 하는 것
( type-name ) expression
floating type의 소수점 구하기
float f, frac_part;
frac_part = f - (int) f;
컴파일러의 기존 규칙과 다른 규칙을 적용하고 싶을 때
float q;
int a, b;
a = 3, b = 2;
q = a/b; // q는 float이지만 1.5가 아닌 1.0이다.
q = (float) a/b; // q = 1.5
오버플로우를 피하기 위해
long long int i;
int j;
j = 1000000;
i = (long long int) j * j;
// overflow가 일어나지 않도록, int의 범위를 벗어나는 결과가 예상되면 casting해줌
printf("%lld\n", i);
( type name ) 은 unary operator임. binary operator보다 더 우선순위가 빠르다. ( long ) i * j; 는 i를 long으로 변환시킨 후에 j와 곱한다.
7.5 Type Definitions
typedef int Bool;
Bool flag; /* same as int flag; */
Bool이라는 type을 정의하고, flag라는 변수를 Bool type으로 선언. 사실 int로 선언한 것과 다르지 않다.
Advantages of Type Definitions
이름을 잘 지으면 더 이해하기 쉬운 코드를 짤 수 있다. 또 나중에 수정하기가 쉽다
ex) typedef float Dollars; // 돈에 관한 변수임을 알기 쉽고, 만약 float 대신 double이 필요하다면 모둔 변수의 선언 부분 대신 typedef 부분만 수정하면 됨
Type Definitions and Portability
같은 이름의 type이 서로 다른 시스템에선 다른 범위를 나타낼 수 있기 때문에, typedef를 이용해 type의 범위를 지정하는 것이 코드의 portability에 도움이 될 수 있다.
C99에서는 <stdint.h>를 이용해 변수에 몇 비트를 할당할 것인지 지정해 줄 수 있다. 예를 들어 int32_t는 32bit의 signed integer를 의미한다.
typedef int32_t Int32; // 새로운 32비트의 signed integer 'Int32' type 정의
7.6 The sizeof Operator
타입을 저장하려면 메모리가 얼마나 필요한가?
sizeof ( type-name )
이 결과는 type-name(type의 이름일 수도 있고, 변수일 수도 있다)를 저장하는 데 몇 바이트가 필요한지 를 나타내는 양의 정수이다. sizeof의 type은 size_t라는 unsigned 정수형의 type이다. 괄호 안에 char를 넣으면 1, int는 (보통) 4. 이 값은 unsigned long의 범위를 벗어날 수도 있다. 그러나 casting을 해주지 않고도 overflow 걱정 없이 값을 나타낼 수 있다.
printf("Size of int: %zu\n", sizeof(int)); // C99 only
--추가
hexadecimal floating constants의 장점: 10진수 형태로 나타낸 소수는 2진수 형태로 변환될 때 나타나는 rounding error에 영향받기 쉽지만, hexadecimal 의 경우에는 그렇지 않다. 따라서 더 정확한 표현이 가능하다. 특히 숫자가 작을수록.
PROJECT
4번 알파벳을 전화번호로
#pragma warning(disable:4996)
#include <stdio.h>
int main(void)
{
char c;
scanf("%c", &c);
while (c != '\n')
{
if (65 <= c && c <= 67)
c = 50;// 2
else if (68 <= c && c <= 70)
c = 51;// 3
else if (71 <= c && c <= 73)
c = 52;// 4
else if (74 <= c && c <= 76)
c = 53;// 5
else if (77 <= c && c <= 79)
c = 54;// 6
else if (c == 80 || c == 82 || c == 83)
c = 55;// 7
else if (84 <= c && c <= 86)
c = 56;// 8
else if (87 <= c && c <= 89)
c = 57;// 9
printf("%c", c);
scanf("%c", &c);
}
printf("\n");
}
5번 스크래블 점수 계산
#pragma warning(disable:4996)
#include <stdio.h>
int main(void)
{
char c;
int value = 0;
printf("Enter a word: ");
scanf("%c", &c);
while (c != '\n')
{
c = toupper(c);
if (c<65 || c>90){
printf("only alphabetical input is allowed.\n");
return 0;
}
switch (c) {
case 'D': case 'G':
value += 2;break;
case 'B': case 'C': case 'M': case 'P':
value += 3;break;
case 'F': case 'H': case 'V': case 'W': case 'Y':
value += 4;break;
case 'K':
value += 5;break;
case 'J': case 'X':
value += 8;break;
case 'Q': case 'Z':
value += 10;break;
default:
value += 1;break;
}
scanf("%c", &c);
}
printf("Scrabble value: %d\n", value);
return 0;
}
5번(scanf말고 getchar를 사용하면 글자 읽는 함수 사용횟수를 2번에서 1번으로 줄일 수 있음)