20140320 (포인터, Hexa View, 함수)

24일차

 

----------------

포   인   터

----------------

 

- float형이 메모리에 IEEE 754방식으로 저장되어 있는지 확인해 보자

소스 코드

#include <stdio.h>

int main()
{
  float    fNum = 3.25;
  unsigned char *  ucpData = (unsigned char *)&fNum;

  printf("%02X \n", *ucpData);
  printf("%02X \n", *(ucpData+1));
  printf("%02X \n", *(ucpData+2));
  printf("%02X \n", *(ucpData+3));
  printf("%.3f \n", fNum);

  printf("\n");

  *(ucpData+0= 0x00;
  *(ucpData+1= 0x00;
  *(ucpData+2= 0xB9;
  *(ucpData+3= 0x41;

  printf("%02X \n", *ucpData);
  printf("%02X \n", *(ucpData+1));
  printf("%02X \n", *(ucpData+2));
  printf("%02X \n", *(ucpData+3));
  printf("%.3f \n", fNum);

  return 0;
}

출력 화면 

역시 어제 배운대로 거꾸로 저장되어 있다.

그런데 알아보기 힘들다. 왜? IEEE 754 방식으로 저장되어 있기 때문에

좀 해석?이 필요하다.

 

 

 

 

----------------

Hexa View

----------------

- 메모리영역을 16진수로 보는 것이다.

 

Visual Studio 2010 에서 열어본 것,

 

리눅스에서 출력해 본 화면이다.

소스코드를 보자면

#include<stdio.h>

int
 main()
{  
  int iNum = 0xABCDEF98;
  int iCnt;
  int iCnt2;
  unsigned char * ucpData = (unsigned char *)&iNum;
  
  printf("===============================================================================\n");
  printf("= ADDRESS                         HEXA                             ASCII      =\n");
  printf("=-----------------------------------------------------------------------------=\n");
  
  for(iCnt2=020>iCnt2; ++iCnt2)
  {
    //----------------------------//
    //---  Address Part Start  ---//
    printf("= %08X  ", ucpData);
    //---  Address Part End    ---//
    //----------------------------//
  
    //--- Hexa View Part Start ---//
    for(iCnt=016>iCnt; ++iCnt)
    {
      if(7 == iCnt)
      {
        printf("%02X  ",*ucpData);
      }
      else
      {
        printf("%02X ",*ucpData);
      }
      ++ucpData;
    }
    //--- Hexa View Part End  ----//
    //----------------------------//

    //----------------------------//
    //---   ASCII Part Start   ---//
    ucpData = ucpData - iCnt;
    for(iCnt=016>iCnt; ++iCnt)
    {
      if(0x20 <= *ucpData && 0x80 > *ucpData)
      {
        printf("%c",*ucpData);
      }
      else
      {
        printf(".");
      }
      ++ucpData;
    }
    //---   ASCII Part End     ---//
    //----------------------------//
    printf(=\n");
  }  
  printf("===============================================================================\n");

  return 0;
}

소스 중에 아스키코드를 출력하는 부분이 있는데

여기서 알아야 할 것이 아스키 코드 31번 밑으로는 제어 문자,

128번 이후는 확장아스키코드로 제대로된 문자가 나오지 않는다.

따라서 32~127번 까지만 아스키코드로 출력 가능하다.

 

 

---------------------

Function (함수)

---------------------

- 어떤 기능을 하는 것을 Function(함수)라고 하는데

이전까지 사용하던 main, printf, scanf도 다 함수이다.

이 함수들도 Symbol Table이 만들어진다.

그러면 함수들도 주소 값이 있다는 말이다.

여기서 잠깐

프로그램이 메모리를 이런식으로 사용하는데

 

자세한건 차차 배운다.

 

또 함수는 리턴 값이 있는데 이것은 컴파일러마다

다르기 때문에 그에 맞는 매뉴얼을 따로 봐야한다.

Microsoft Visual Studio는 MSDN이라 불리는 매뉴얼이 있고

리눅스 에서는 man 이라는 매뉴얼이 있는데

man "확인할 명령어 or 함수"

(printf 같은 경우 리눅스에도 prinft라는 명령어가 있기때문에

첫 페이지에 명령어가 나오는데 다른 페이지를 보려면

man 숫자 함수  <-- 이런식으로 써야된다

man 3 printf    printf같은 경우 이렇게 본다)

 

 

- 함수의 구조

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

설정

트랙백

댓글

20140319 (포인터, little endian, big endian, 포인터 연산)

23일차

 

______________

포    인    터

--------------

- 어제 이어 계속 포인터

포인터의 크기는 4byte이다.

포인터는 메모리의 주소를 저장하고 있기 때문에

메모리의 크기 만큼만 크기가 되면 된다.

32bit 컴퓨터에 메모리의 최대 크기가 약 4G가 최고이니

그에 맞게 32bit -> 4byte 이다.

 

만약 64bit 컴퓨터라면 아마 8byte가 되겠지..?

확인해 봐야 한다고 배웠다.

 

 

 

----------------------------

little endian, big endian

----------------------------

- CPU 마다 메모리 저장 방식이 다른데

little endian방식과 big endian방식이 있다.

 

설명을 위해 예제를 보자.

 

위와 같이 1byte크기로 거꾸로 저장되고 있었다.

intel 같은 경우는 산술을 빨리 하기 위해서 거꾸로 저장된다고 한다.

덧셈을 할 경우

이런 식으로 메모리에서 불러와 연산할 때에 셈이 더 빨라지므로

거꾸로 저장한다.

앞에서 언급했듯이 저장 방식이 두 가지가 있다.

 

- Little Endian

intel 같은 pc용으로 쓰이는 CPU는 little endian이다

메모리에 저장될 때 거꾸로 저장된다.

산술↑    대소비교↓

pc 같은 경우 데이터 처리, 게임 같이 산술을 많이 해서

거꾸로 저장하는게 유리하다고 한다.

 

- Big Endian

super 컴퓨터 같은 것 들은 big endian방식을 쓴다

메모리에 저장될 때 그대로 저장된다.

산술↓    대소비교↑

대소비교 같은 경우 맨 앞자리 부터 비교하면 어떤 수가 더 큰지

빨리 볼 수 있으므로 그대로 저장하기 때문에

대소비교시 더 빠르다고 한다.

 

 

------------------------

포  인  터  연  산

------------------------

- 포인터를 연산?

ex) 예제 소스

#include <stdio.h>

int main()
{
  int  iNum = 0x12345678;
  unsigned  char *cp = (unsigned char *)&iNum;
  short *  sp = (short *)&iNum;

  *cp = 0xAA;

  printf("0x%08X \n", iNum);

  printf("%02X \n", *(cp+0) );
  printf("%02X \n", *(cp+1) );
  printf("%02X \n", *(cp+2) );
  printf("%02X \n", *(cp+3) );

  printf("%08X \n", (cp+0) );
  printf("%08X \n", (cp+1) );
  printf("%08X \n", (cp+2) );
  printf("%08X \n", (cp+3) );

  printf("%04X \n", *(sp+0) );
  printf("%04X \n", *(sp+1) );

  printf("%08X \n", (sp+0) );
  printf("%08X \n", (sp+1) );

  return 0;
}

출력 화면

 

위에서 보듯이 주소에 수를 더하면 그 타입의 크기만큼 증가한다.

증감을 통해 메모리 주소를 왔다갔다 할 수있다.

 

 

- 또 한가지 주의 할 것이 있다.

아래 소스를 보자

#include <stdio.h>

int main()
{
  int    iNum = 0x12345678;
  char *    cpNum1 = (char *)&iNum;
  unsigned char *  cpNum2 = (unsigned char *)&iNum;

  *cpNum1 = 0xAA;

  printf("iNum = %08X \n", iNum);

  printf("*cpNum1 = %X \n", *cpNum1);

  printf("*cpNum2 = %X \n", *cpNum2);

  return 0;
}

출력 화면

 

출력 화면을 보듯

cpu에서 char 형도 정수로 보기 때문에 1010이 저장되었으므로

맨 앞이 음수이다. 그래서 register에서 받았을 경우 남는 칸도

다 111111111로 채우기 때문에 FFFFFFAA로 나온다

반면에 unsigned char는 양수형 정수이기 때문에 그대로 나온다.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

설정

트랙백

댓글

20140318 (포인터, &, *, (int *) )

22일차

 

---------------

포   인   터

---------------

- 주소 값을 저장하는 변수. 기호는 * 로 쓴다.

지금까지 배운 * 기호의 역할이 4가지가 있는데

1. * : 곱셈

2. (type *) : 캐스팅

3. * : 주소를 따라가 그 곳에 저장된 값을 가리킴.

4. * : 포인터 변수 선언.

 

1번은 아는거고 2번에 대해서 우선 알아보자

먼저 전에 배웠던 & (Ampersand)를 변수 앞에 붙이면

그 변수의 주소값을 나타낸다고 배웠다.

2번, 3번을 보면 &과는 다른 값을 볼 수 있다.

다음 예제 소스를 보자

ex) 예제 소스

#include <stdio.h>

int main()
{
  int  iNum = 0;

//  &iNum  =  100;
//  주소상수  =  상수
//  --> Error!

//  0x12FF74 =  100;
//  16진상수    상수
//  --> Error!

//  (int *)0x12FF74 =  100;
//  주소상수    상수
//  --> Error!


  *( (int *)0x12FF74 ) = 100;
// 16진  주소를 가르키는 값     상수

  printf("iNum = %d \n", iNum);
  printf("&iNum = 0x%08X \n"&iNum);


  *( (int *)1245044 ) = 300;
// 10진  주소를 가르키는 값     상수

  printf("iNum = %d \n", iNum);
  printf("&iNum = %d \n"&iNum);



  *( &iNum ) = 1000;

  printf("iNum = %d \n", iNum);
  printf("&iNum = 0x%08X \n"&iNum);

  return 0;
}

출력 화면

 

위에 예제 소스를 보시다시피

(type *)메모리주소 가 그 해당하는 메모리주소를 나타내게 된다.

그 앞에다 * 을 붙이면 그 해당하는 메모리주소의 값을 지칭하게 된다.

밑에 예제 소스를 더 보자.

ex) 예제 소스

#include <stdio.h>

int main()
{
  int  iNum = 0;
  int  iCnt = 0;

  printf("iNum : %d \n", iNum);
  printf("&iNum : 0x%08X \n"&iNum);

  printf("iCnt : %d \n", iCnt);
  printf("&iCnt : 0x%08X \n"&iCnt);

  *((int*)0x12FF70) = 100;
  *((int*)0x12FF74) = 100;

  printf("\niNum : %d \n", iNum);
  printf("&iNum : 0x%08X \n"&iNum);

  printf("iCnt : %d \n", iCnt);
  printf("&iCnt : 0x%08X \n"&iCnt);


  return 0;
}

출력화면

 

 

이렇게 메모리 주소를 통하여 다른 메모리를 접근 할 수 있게 된다.

그런데 다른 메모리에 접근 할 때마다 번거로우니

4번이 필요하다

예제를 보자

ex) 예제 소스

#include <stdio.h>

int main()
{
  int  iNum = 0;
  int  iAddr = 0;
  int *  iP;

  printf("&iNum : 0x%08X \n"&iNum);
  printf("&iAddr : 0x%08X \n"&iAddr);

  iAddr = (int)(&iNum);

  printf("iAddr : %08X \n", iAddr);
  printf("iNum : %08X \n", iNum);
  
  *(int *)iAddr = 1000;

  printf("iNum : %d \n", iNum);

  iP = &iNum;

  *iP = 999;

  printf("iNum : %d \n", iNum);  

  return 0;
}

출력 화면

 

어쨋든 정리를 하자면

100                  int형 정수

iNum                int형 정수

&iNum              int *형 주소상수 (자신 주소값)

(int *)iNum        int *형 주소상수 (저장된 주소값)

ipNum               int *형 주소상수 (저장된 주소값)

*((int *)iNum)    iNum에 저장된 주소값을 따라가 그 안에 저장된 값.

*ipNum             iPNum에 저장된 주소값을 따라가 그 안에 저장된 값.

 

여기서 그냥 하는 얘기인데

*( (int *)0x0012FF74 ) = 100;

를 왜 쓰나 싶었는데, 어셈블리에서는

변수 대신 저렇게 주소값으로 쓴다고 한다.

 

 

 

 

 

 

 

 

 

 

 

 

 

설정

트랙백

댓글