티스토리 뷰

2.1 정보의 저장

  • 기계 수준의 프로그램은 메모리를 가상메모리라고 하는 거대한 바이트의 배열로 취급
  • 메모리의 각 바이트는 주소라고 하는 고유한 숫자로 식별
  • 모든 가능한 주소들의 집합을 가상 주소공간이라고 부른다.
  • 기계 수준의 프로그램은 각 프로그램의 객체를 단순히 바이트들의 블록으로 취급하고, 프로그램 자신은 바이트의 연속으로 취급한다.

2.1.1 16진수 표시

  • 이진수 표시는 너무 장황하고, 십진수 표시는 비트 패턴으로 변환해야 하므로 불편하다. 따라서, 우리는 비트 패턴을 16진수로 표시하고자 한다.
  • 16진수는 '0'에서 '9'까지의 숫자와 'A'에서 'F'까지의 문자를 사용해서 16개의 가능한 값을 나타낸다.
  • C에서 0x 혹은 Ox로 시작하는 숫자 상수들은 16진수로 해석한다.
  • 기계수준 프로그램으로 하게 되는 일반적인 작업은 비트 패턴을 십진, 이진, 16진수간에 수동으로 변환하는 것이다.
  • 예를 들어 0x173A4C가 주어졌다고 하자. 이는 각 16진수의 숫자를 확장해서 이진수 형태로 변환할 수 있다.
    • 0001 / 0111 / 0011 / 1010 / 0100 / 1100
  • 반대로 이진수가 주어졌다면 이를 4비트씩 나누어 16진수로 변환하면 된다.
    • 만약 4의 배수로 비트가 주어지지 않았다면 맨 왼쪽 그룹에 0을 추가하여 4의 배수로 만들어 준 후 16진수로 변환한다.

2.1.4 주소지정과 바이트 순서

  • 여러 바이트에 걸쳐 있는 프로그램 객체들에 있어서 두 개의 관습을 설정해야 한다.

    • 객체의 주소가 무엇이 되어야 하는지
      • 사용된 바이트의 최소 주소를 사용
    • 메모리에 바이트들을 어떻게 정렬해야 하는지
      • 연속된 바이트에 저장
  • 어떤 객체를 나타내는 바이트들을 정렬하는 데는 두 가지 일반적인 관습이 존재한다.

    • 가장 중요한 비트(most significant bit)
      • 가장 중요한 비트를 먼저 저장하는 방식을 빅 엔디안
    • 가장 덜 중요한 비트
      • 가장 덜 중요한 비트를 먼저 저장하는 방식을 리틀 엔디안
  • 바이트의 순서가 중요한 경우

    • 이진 데이터가 네트워크를 통해 다른 컴퓨터로 전송될 때
      • 리틀 엔디안 컴퓨터에서 만들어진 데이터를 빅 뎅ㄴ디안 컴퓨터에 보내야 해서 수신 측 프로그램에서는 워드들 내 바이트의 순서가 뒤바뀌는 경우
      • 네트워크 응용프로그램으로 작성된 코드는 송신 측 컴퓨터가 내부 표시를 네트워크 표준으로 변경하고, 수신측 컴퓨터가 네트워크 표준을 자신의 내부 표시방식으로 변환하는 방식으로 문제 야기를 방지
    • 정수 데이터를 나타내는 바이트들을 살펴볼 때
    • 프로그램이 정상적인 타입 체계를 회피하도록 작성되었을 때

2.1.9 C에서의 쉬프트 연산

  • C는 비트 패턴을 좌우로 이동시키는 쉬프트 연산 집합을 제공한다.
  • x << k
    • x는 좌측으로 k비트 이동하고, 중요한 좌측의 k비트가 밀려서 삭제되며 우측에는 k개의 0으로 채워진다.
  • x >> k
    • 논리 우측 쉬프트: 좌측 끝을 k개의 0으로 채운다.
    • 산술 우측 쉬프트: 좌측 끝을 가장 중요한 비트를 k개 반복해서 채운다. 이는 부호형 정수 데이터의 연산에서 유용하게 작용한다.

2.2 정수의 표시

  • 정수를 인코드하기 위해 사용할 수 있는 두가지 방법
    • 양수만 표시할 수 있는 방법
    • 음수, 0, 양수 모두를 표시할 수 있는 방법
  • 인코딩 된 정수를 다른 길이의 표현에 맞도록 조절하기 위해 확장하거나 축소하는 효과

2.2.5 C에서 부호형과 비부호형의 비교

  • C는 모든 정수 데이터 타입에 대해 부호형과 비부호형 산술연산을 지원한다.
  • C는 비부호형과 부호형 간의 변환을 허용한다.
    • 기본 비트 표시는 바뀌지 않는다는 규칙을 따른다.
  • 변환은 다음의 코드들에서 발생할 수 있다.
// 명시적인 캐스팅
int tx, ty;
unsigned ux, uy;

tx = (int) ux;
uy = (unsigned) ty;

// 묵시적인 캐스팅
int tx, ty;
unsigned ux, uy;

tx = ux;
uy = ty;
  • 숫자값을 printf로 출력할 때 디렉티브 %d, %u, %x는 수를 부호형 십진수, 비부호형 십진수, 16진수 형식으로 출력하기 위해 사용한다.
    • printf는 타입 정보를 활용하지 않는다는 점에 유의해야한다.
    • int 타입은 %u, unsigned 타입의 값을 %d를 사용하여 출력할 수 있다.
  • 한 개의 오퍼랜드가 부호형이고, 다른 것이 비부호형인 경우의 연산에서 C는 묵시적으로 부호형 인자를 비부호형으로 변환하고, 숫자들이 비음수라고 가정하고 계산을 수행한다.

2.2.6 수의 비트 표시를 확장하기

  • 보다 작은 길이에서 더 큰 길이의 자료형으로 변환하는 것은 언제나 가능해야 한다.
  • 0의 확장(zero extension)
    • 비부호형 수를 보다 길이가 긴 데이터 타입으로 변환하기 위해서 단순히 앞에 0들을 추가
  • 부호 확장(sign extension)
    • 2의 보수를 보다 긴 데이터 타입으로 변환하는 방법
    • 가장 중요한 비트를 복사해서 확장하는 앞부분에 추가
  • 한 데이터 길이에서 다른 길이로 변환하는 작업과 비부호형과 부호형 사이에 변환하는 작업 간의 순서가 프로그램의 동작에 영향을 줄 수 있다.

2.2.7 숫자의 절삭

  • 비트를 추가해서 값을 확장하는 대신 수를 나타내는 비트의 개수를 줄이는 경우를 살펴보자.
    int x = 53191; 
    short sx = (short) x; /* -12345 */
    int y = sx; /* -12345 */
  • x를 short로 타입 casting할 때 32bit int를 16bit short로 절삭한다. 이 16bit 패턴은 -12345를 2의 보수로 표시한 것이다. 이것을 다시 int로 casting할 때 부호 비트는 상위 16bit를 1로 세팅해서 -12345의 32비트 2의 보수 표시를 만들게 된다.
  • 수를 절삭하면 값이 바뀔 수 있다. 이것은 일종의 오버플로우와 같다.
  • 비부호형 수의 절삭
    • x’ = x mod 2^k 이다.
    • 삭제되는 모든 비트들은 i >= k인 2^i의 자리값을 가지며, 이 자리값들은 모듈 계산으로 모두 0이 된다
  • 2의 보수 숫자의 절삭
    • x’ = U2T(x mod 2^k) 이다.
    • 가장 중요한 비트인 x_k-1이 자리값 2^(k-1) 대신 -2^(k-1)을 갖는 효과를 가진다.

2.2.8 Signed와 Unsigned에 관한 조언

  • 부호형을 비부호형으로 묵시적인 타입 변환을 하면 종종 프로그램 버그가 발생하기도 하고 묵시적 타입 변환의 미세한 효과로 인해 생기는 버그들은 알아내기가 상당히 어려울 수 있다. 그 이유는 타입 변환이 코드 내에서 명확한 표시 없이 발생하기 때문이다.
  • 이러한 버그들을 피하는 한가지 방법은 비부호형 수를 절대로 사용하지 않는 것이다.
    • 실제로 C이외의 다른 언어들은 unsigned 정수를 지원하지 않는다.
  • 비부호형 값들은 워드 길이 데이터를 숫자 값으로 해석하지 않고 비트들의 집합으로 생각하려는 경우에 유용하다.
    • 예를 들어 여러가지 부울 조건들을 설명하는 플래그들로 한개의 워드를 압출할 때가 여기에 해당한다.
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/12   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
글 보관함