정보공간_1

[2기 광주 박정태]C language - type cast 본문

IT 놀이터/Elite Member Tech & Talk

[2기 광주 박정태]C language - type cast

알 수 없는 사용자 2012. 8. 14. 15:02

안녕하세요 광주 멤버십 21-1 박정태 입니다.

저는 C언어에 대한 포스팅을 할까 합니다. IT를 공부하는 사람이라면 C언어는 가장 기본적으로 배우는 언어라고 생각합니다. 책을 펼치면 나오는 기본적인 문법은 어느정도 아실거라 생각하고

좀더 근본적인 원리에 대해 궁금증을 갖고 알아가는 시간을 갖도록 하겠습니다.

 

우연히 이런 문제를 접하게 되었습니다

 

.

결과는 어떻게 나올까요 ?

unsigned  int에 대해서는 쉽게 결과가 떠올려 지지만 signed int..금방떠오르진 않죠.

바로 떠오른다면 정말 C를 잘하시는 겁니다!

 

결과를 볼까요.

 

 

 

 

Unsigned 음 생각했던 것 처럼 나왔는데 문제는 signed.

C언어 책에 나와있는 <<,>> 연산자는 정해진 비트수 만큼 옆으로 이동을 하고 넘어가버린 비트는 없어지고 채워지는 비트는 모두 0으로 채워진다고 설명하고 있습니다.

물론 고급C책에서는 signed형에 대한 설명도 있겠죠.

 

디어셈을 해보면 다음과 같이 나옵니다.

 

 

 

shr, shl, sar 세가지의 shift 연산자가 나오는 것을 확인할수 있는데요

이 세가지 명령어가 무엇인지 알아보겠습니다.

 

논리 시프트(SHL,SHR- Shift Left, Shift Right)

쉽게 말해서 초급 c언어 책에 나오는 shift 와 같습니다. 정해진 비트 수 만큼 비트들을 시프트하고 최후에 밀려난 비트는 상태 레지스터의 캐리 플래그에 저장됩니다. 밀고 들어오는 비트는 모두 0 이구요. 논리 시프트는 단순한 2의 승수로 곱하기 나누기에 사용될 수 있습니다. 왼쪽으로 밀면 x2 오른쪽으로 밀면 /2 입니다. us의 경우에만 사용된 것을 알 수 있습니다.

 

산술 시프트(SAR- Shift Arithmetic Right )

논리 시프트와 달리 부호를 따지는 시프트로써 비트를 오른쪽으로 밀되 최 상위비트는 계속 피드백되어 유지되고 새로 밀고 들어오는 비트는 최상위 비트에 의존한 값이 들어옵니다. 이는 음수의 나눗셈을 가능하게 하죠.

 

..말로만 봐서는 쉽사리 이해가 안 갈수도 있습니다.

그럼 쉽게 그림으로 보겠습니다.

 

Int를 비트로 표현하면 0이 무지 많이 들어가기 때문에 편의상 char로 표현하겠습니다.

 

signed char sc = 0x81 이 있습니다.

 

먼저 2의 보수법으로 읽어보면 1을 빼고 비트를 반전시키면 되죠.

 

최상위 비트를 빼면 127에 최상위비트가 1이니 -127입니다.

 

다시 원래의 값을 SAR 명령어로 2만큼 시프트 시켜보겠습니다.

최상위 비트가 1이기 때문에 새로 들어오는 비트는 최상위 비트를 따라 1로 채워지게 됩니다.

밀린 1은 없어지겠죠.

 

다시 읽기 편하게 2보수를 취해보면

 

 

-31 인것을 확인할수 있습니다.

부호도 살고 값도 제대로 나왔습니다.

 

음 그러고 보니 처음부터 char 형으로 했으면 좀더 보기가 쉬웠을건데..하는 생각이 들어

char형으로 바꿔서 해보았습니다.

뭐 똑같이 나오겠지

하고 f5를 누르니

 

…?

인트형으로 확장이 일어났네요.

 

 ]

 

디어셈을 해보니 ux sx eax 레지스터에 담아서 연산을 합니다.

 

이 과정에서 char형이 변환이 일어나게 됩니다.

 

좀더 자세히 알아보기 위해 잠시 다른 문제를 보겠습니다.

 

 

결과가 어떻게 나올까요?

이제 눈치가 조금 빠르신 분 들은 짐작이 가셨을 거라 봅니다.

 

 

 

연산자와 형변환의 규칙에 대해 찾아 보았습니다.

 

 

l  일반적으로 +,-,* 와 같이 두개의 오퍼랜드를 갖는 연산자에서 형이 다를 경우엔

계산을 진행하기 전 ‘작은’ 쪽에서 ‘큰’ 쪽으로 형 변환이 된다.

 

1.      정수진급

-       Char, short, integer등은 부호가 있건 없건 상관없이 정수가 사용될 수 있는 곳에서사용될 수 있다. int로 수식 모든 값을 나타낼 수 있다면 그 모든 값은 정수로 변환된다. 그렇지 않다면 unsigned int 로 변환된다.  

 

2.      정수변환

-       어떤 정수가 부호가 없는 경우 비트수가 적은 경우는 왼쪽에서의 절단이 일어난 것과 같고 비트가 많은 경우에는 앞에 0을 삽입한 것

-       어떤 정수가 부호 있는 형태로 변환될 때 컴파일러 의존. VS 경우 부호 있는 경우도 비트수 많은경우에서 적은경우로는 절단이 일어나고 반대의경우 부호연장이 일어난다.

 

3.      정수와 부동소수

-       부동소수 -> 정수  소수없어짐. 결과값이 정수형태로 나타낼수 없을경우 동작 정의x 특히 음의 부동소수 값을 부호없는 정수 형태로 변환한 결과는 지정되지 않는다.

-       정수 -> 부동소수 표현가능범위에 있다면 가장 비슷한 값이 된다.

 

4.      부동 소수형

-       정밀도가 떨어지는 부동 소수의 값이 더 높은 정밀도의 부동 소수 형태로 변환될 때 그 값은 변하지 않는다. 반대의 경우 값이 표현 가능한 범위 내에있는 경우 그 결과는 표현가능한 가장 비슷한 값이 된다.

 

5.      산술적 변환

-       int < long < float < double < long double 더 작은쪽이 더 큰쪽으로 변한다.

 

다음과 같은 규칙에 의해 형 변환이 일어난다고 합니다.

 

그럼 위의 문제로 돌아가서...

 

첫번째 문제의 경우는 디어셈에서 char unsigned char인 두 변수는 시프트가 되기전 eax로 복사되면서 signed int형으로 형 변환이 됩니다(정수진급).

부호를 따지는 signed 형이기 때문에 시프트 될 때 sar을 사용하는 것을 확인할 수 있구요. 그래서 결과가 위에서 처럼 나온 것 입니다.

만약 결과값을 다시 char 형이나 unsigned char형에 넣었다면 원하는 값이 나왔겠죠. 형변환에 대한 규칙은 규칙자체가 작은쪽이 큰쪽으로 변하기 때문에 큰쪽을 작은쪽에 넣는 규칙은 언급되지 않았는데 큰 쪽에서 작은 쪽으로 넣을 때는 하위비트부터 채우고 나머지는 그냥 짤립니다.

부호 상관없구요.

 

두번째 문제는 비교되기전 각각의 값이 signed int형으로 형변환이 되서 0xff와 비교되게 됩니다.

 

Unsigned char c = 0xff 의 경우엔 0xff

Signed char x = 0xff 의 경우엔 0xffffffff 로 바뀐뒤 0xff와 비교되기 때문에

첫번째 if true가 되고 두번째 if false가 되어서 앞에서의 결과가 나오게 된 것입니다.

그이유는 '==' 연산자 또한 2개의 오퍼랜드를 갖고 0xff 는 int로 간주되기 떄문에 작은 char가 형변환이 되는 것 입니다. 

물론 결과값도 변환이 일어난 결과구요.

 

마지막 문제로 정리하고 형변환에 대해서 마치도록 하겠습니다.

 

이건 문제라기 보단 이런 상황에서 어떻게 연산이 일어나는지 보여주는 것 입니다.

 

 

더하는 부분 3줄을 보면 모든 결과를 유추해 볼 수 있습니다.

 

다음과 같이 스택에 값이 저장이 되어 있을 것 입니다.

 

 

Mov 가 아닌 movsx 입니다. 원래 mov명령어는 두 오퍼레이터가 같을 때만 사용이 가능합니다. Movsx는 부호있는 산술값에서 바이트나 워드를 워드나 더블워드의 목적지에 전송해줄 때 사용하는 명령어 입니다. 이를 보면 이때 char signed int로 한번 형 변환이 일어남을 알 수 있습니다.

변환된 결과는 0xffffffff가 될것입니다(부호확장).

 

 

그리고 두 값이 서로 더해지게 되고 그결과 값은 0x12345677 이 되겠죠. 오버플로우난 비트는 짤려 나가게 되구요.

 

그리고 마지막으로 이 값은 short에 하위비트부터 들어가게 되고 상위 비트는 짤리게 됩니다.

결과적으로 다음과 같이 변하게 될 것 입니다.

 

간단한 문제와 함께 형변환에 대해 알아 보았습니다. 사실 if문 같은경우는 당연히 이랬을 거라 생각 했던게 전혀 다른 결과로 나온것을 확인 하실수 있는데요 이런부분을 간과하고 코딩을 하다보면 오래오래 문제를 해결하지 못하고 멘붕이 오는 결과가 올수 도 있겠네요^^;