20140331 (2차원 배열, 이중포인터, 배열포인터)

31일차

 

 

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

2차원 배열 메모리 구조

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

-

char Arr[3][3];

int * ip = &Arr[0][0];

2차원 배열이 실제 메모리에서는

 

이런 식으로 저장되어 있다.

그래서 아래와 같이 모두 같은 값을 가리킨다.

ip[3] == Arr[0][3] == Arr[1][0]

 

 

 

- 줄 이동 (주소값)

Arr + 0    =    &Arr[0] + 0    =    &Arr[0]

Arr + 1    =    &Arr[0] + 1    =    &Arr[1]

Arr + 2    =    &Arr[0] + 2    =    &Arr[2]

 

- 칸 이동 (주소값)

&Arr[0][0]    =    Arr[0] + 0    =    *(Arr + 0) + 0    =    *( &Arr[0] + 0 ) + 0

&Arr[0][1]    =    Arr[0] + 1    =    *(Arr + 0) + 1    =    *( &Arr[0] + 0 ) + 1

&Arr[0][2]    =    Arr[0] + 2    =    *(Arr + 0) + 2    =    *( &Arr[0] + 0 ) + 2

 

- 칸 이동 (값)

Arr[0][0]    =    *(Arr[0] + 0)    =    *( *(Arr + 0) + 0 )    =    *( *( &Arr[0] + 0 ) + 0 )

Arr[0][1]    =    *(Arr[0] + 1)    =    *( *(Arr + 0) + 1 )    =    *( *( &Arr[0] + 0 ) + 1 )

Arr[0][2]    =    *(Arr[0] + 2)    =    *( *(Arr + 0) + 2 )    =    *( *( &Arr[0] + 0 ) + 2 )

 

 

 

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

---배열에서 []숫자 생략

배열에서 []안에 숫자 생략이 가능한데 맨 왼쪽에 있는 숫자 하나만 가능하다.

다만 초기화시에만 가능.

int iaArr[ ] = {1, 2, 4, 5};

int iaArr[ ][2] = {  {1, 2}, {3, 4}  };

int iaArr[ ][2][2] = {  { {1, 2}, {3, 4} }, { {1, 2}, {3, 4} }  };

 

 

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

---포인터의 포인터

 

int iNum = 3;                                      iNum -> 3

                                                     &iNum -> iNum주소

int * ip = &iNum;                                     ip -> iNum주소

                                                         &ip -> ip주소

                                                          *ip -> 3

int ** ipp = &ip;                                    ipp -> ip주소

                                                        &ipp -> ipp주소

                                                        *ipp -> iNum주소

                                                       **ipp -> 3

 

 

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

---배열 포인터, 포인터 배열

int arr[3][4];                   //  배열

int * ptr2[4];                    // 포인터 배열 , 이 변수 크기 16byte

int (*ptr)[4];                    // 배열 포인터 (16byte씩 건너뛰는 포인터) , 이 변수 크기 4byte

 

int (* ptr)[4];    // 포인터방식 선언

int ptr[][4];      // 배열방식 선언

둘 다 4byte 크기의 포인터로 연산시 16byte씩 이동하는 같은 뜻이다.

 

ex) 예제 소스

#include <stdio.h>

void ShowArr2DStyle(int (*arr)[4]int column)
{
  int  i;
  int  j;

  for(i = 0; column > i; ++i)
  {
    for(j = 04 > j; ++j)
    {
      printf("%d "arr[i][j] );
    }
    printf("\n");
  }
  printf("\n");
  printf("arr : %d \n"sizeof(arr) );
}

int Sum2DArr(int arr[][4]int column)
{
  int  i;
  int  j;
  int  sum = 0;

  for(i = 0; column > i; ++i)
  {
    for(j = 04 > j; ++j)
    {
      sum = sum + arr[i][j];
    }
  }

  printf("arr : %d \n"sizeof(arr) );

  return sum;
}

int main()
{
  int  arr1[2][4= {1,2,3,4,5,6,7,8};
  int  arr2[3][4= {1,1,1,1,3,3,3,3,5,5,5,5};

  ShowArr2DStyle(arr1, sizeof(arr1)/sizeof(arr1[0]) );
  ShowArr2DStyle(arr2, sizeof(arr2)/sizeof(arr2[0]) );

  printf("arr1의 합 : %d \n", Sum2DArr(arr1, sizeof(arr1)/sizeof(arr1[0]) ) );
  printf("arr2의 합 : %d \n", Sum2DArr(arr2, sizeof(arr2)/sizeof(arr2[0]) ) );

  return 0;
}

위의 예제처럼 이 차원 배열로도 사용할 수 있다.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

설정

트랙백

댓글

20140328 (const, 2차원배열)

30일차

 

 

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

const

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

 

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

--- 포인터에 const가 붙으면?

 

int a = 10;

a = 40;  //  가능

const int b = 20;

b = 10;  // 불가능

int * c = &a;

*c = 20;  //  가능

c = &b;  //  불가능

cont int * d = &a;

*d = 30;  // 불가능

d = &b  //  가능

int * const e = &a;

*e = 40;  // 가능

e = &b  // 불가능

const int * const f = &a;

*f = 11;  //  불가능

f = &b;  //  불가능

 

 

 

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

2      차      원      배      열

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

 

- 전에 Arr[5] 이게 1차원 배열이라면

int iaArr[3][4]; 2차원 배열이라 부른다

 

--- 배열과 포인터의 관계

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

설정

트랙백

댓글

20140327 (포인터, 배열, call by address, call by value)

29일차

 

 

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

포    인    터      ,     배    열

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

 

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

--- 포인터 사용시 주의할 점.

 

int  iNum = 10;
double *  dpNum = &iNum;  // Warning!

double  dNum = 10.5;
int *  ipNum = &dNum;  
// Warning!

같은 포인터 타입이기에 Error는 안나서 실행은 되지만

다른 자료형(실수, 정수)이므로 Warning입니다.

상황에 따라 어떤 오류가 발생할지는 모르니 잘못된 것입니다.

 

int *  ipNum;

*ipNum = 1000// Warning!

ipNum에 주소값도 정해져 있지 않은데 그 주소값에 저장된 값을 바꾸면

어떤 문제가 발생할지 모르니 이것도 잘못된 것. warning, 런타임 에러

 

int *  ipNum = 1000;

*ipNum = 10;

ipNum에 1000번지를 넣었는데 1000번지가 어딘지 알고 넣었는지?

또 그 값을 변경도 시켰으니 Warning, 런타임 에러

 

int *  ipNum1 = 0;
int *  ipNum2 = NULL;

포인터는 NULL (0) 으로 초기화 값으로 주는게 좋다고 합니다.

그래서 아래 코드를 시험해 보니

#include <stdio.h>

int main()
{
  int *  ipNum = NULL;

  *ipNum = 10;

  return 0;
}

초기화 값을 뭘 주든 제대로된 주소가 아니면 런타임 오류가 납니다.

 

 

 

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

--- 포인터 연산

 

출력 화면

포인터를 연산할 경우 그 자료형 만큼 주소값이 움직입니다.

char * cpNum = 1000;

cpNum = cpNum + 2;    =>   1000 + 2 * 1byte   =>   1002   (2byte움직임)

int * ipNum = 2000;

ipNum = ipNum + 2;    =>    2000 + 2 * 4byte   =>   2008   (8byte움직임)

double * dpNum = 3000;

dpNum = dpNum + 2;    =>   3000 + 2 * 8byte    =>    3016   (16byte움직임)

 

 

 

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

--- *(ipNum+0) == arr[0]

 

배열도 포인터도 주소값을 저장하고 있어서

사용법도 비슷합니다. 그래서

*(ipNum+0) == arr[0]    <-- 이 등식이 성립합니다.

 

ex)

int main()
{
  int  arr[3= {112233};
  int *  ptr = arr;

  printf("arr[0] : %d \n", arr[0]);
  printf("arr[1] : %d \n", arr[1]);
  printf("arr[2] : %d \n", arr[2]);

  printf("ptr[0] : %d \n", ptr[0]);
  printf("ptr[1] : %d \n", ptr[1]);
  printf("ptr[2] : %d \n", ptr[2]);

  printf("*(arr + 0) : %d \n", *(arr + 0) );
  printf("*(arr + 1) : %d \n", *(arr + 1) );
  printf("*(arr + 2) : %d \n", *(arr + 2) );

  printf("*(ptr + 0) : %d \n", *(ptr + 0) );
  printf("*(ptr + 1) : %d \n", *(ptr + 1) );
  printf("*(ptr + 2) : %d \n", *(ptr + 2) );

  return 0;
}

출력 화면

*(ipNum+0) == arr[0]    <-- 이 등식이 성립합니다.

 

 

 

 

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

--- char str1[] = "My String";
     char * str2 = "Your String";  장단점

 

여기서 str2 포인터 변수는 스택에 변수를 가리키는게 아니라

소스에 저장된 Your String을 가리킨다는 것을 명심!

 

배열

메모리 더 먹음, 수정 가능.

포인터

메모리 4byte만 먹음, 수정 불가.

 

 

 

 

 

--- 포인트 배열

포인트 타입의 배열이다.

ex)

arr 배열에 0, 1, 2 주소에 각각 주소값이 저장되어 있습니다.

printf("%d \n", *arr[0] );

->  printf("%d \n", *0x1000 );

->  printf("%d \n", 10 );

이런 과정을 거치겠죠....

 

 

 

 

--- 함수의 인자로 배열로 받을 수 없다.

그래서 포인터로 받는다.

ex)

 

 

 

 

 

--- Call by Address, Call by Value

함수에 인수를 다룰 때 쓰는 방법.

-   Call by Address     : 주소를 넘겨 호출.

-   Call by Value         : 을 넘겨 호출.

ex) 예제 소스

#include <stdio.h>

void CallByValue(intint);
void CallByAddress(int *, int *);

int main()
{
  int  iNum1 = 10;
  int  iNum2 = 100;

  CallByValue(iNum1, iNum2);  //  Call By Value
  printf("CallByValue() -> %d %d \n", iNum1, iNum2);

  CallByAddress(&iNum1, &iNum2);  //  Call By Address
  printf("CallByAddress() -> %d %d \n", iNum1, iNum2);

  return 0;
}

void CallByValue(int n1, int n2)
{
  int  iTemp;

  iTemp = n1;
  n1 = n2;
  n2 = iTemp;
}

void CallByAddress(int * n1, int * n2)
{
  int  iTemp;

  iTemp = *n1;
  *n1 = *n2;
  *n2 = iTemp;
}

출력 화면

 

CallByValue 는 말 그대로 main에 있는 변수의

가져와 쓰기 때문에 CallByValue 에서 무슨 짓을 해도

main의 변수에 접근할 수 없다.

 

CallByAddress 는 말 그대로 main에 있는 변수의 주소값

가져와 쓰기 때문에 CallByAddress 에서 무슨 짓이든

main의 변수에 접근할 수 있다.

 

 

 

설정

트랙백

댓글

20140326 (배열)

28일차

 

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

Array ( 배 열 )

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

- 배열 초기화

char caString[256] = "";

char caString[256] = {0};

둘다 0(NULL)로 초기화 된다.

 

 

- Call by Address, Call by Value

이것은 함수에 인수를 다룰 때 쓰는 방법이다.

Call by Address : 주소를 넘겨 호출.

Call by Value : 값을 넘겨 호출.

ex) 예제 소스

#include <stdio.h>

void CallByValue(intint);
void CallByAddress(int *, int *);

int main()
{
  int  iNum1 = 10;
  int  iNum2 = 100;

  CallByValue(iNum1, iNum2);  //  Call By Value
  printf("CallByValue() -> %d %d \n", iNum1, iNum2);

  CallByAddress(&iNum1, &iNum2);  //  Call By Address
  printf("CallByAddress() -> %d %d \n", iNum1, iNum2);

  return 0;
}

void CallByValue(int n1, int n2)
{
  int  iTemp;

  iTemp = n1;
  n1 = n2;
  n2 = iTemp;
}

void CallByAddress(int * n1, int * n2)
{
  int  iTemp;

  iTemp = *n1;
  *n1 = *n2;
  *n2 = iTemp;
}

출력 화면

CallByValue 는 말 그대로 main에 있는 변수의 값

가져와 쓰기 때문에 CallByValue 에서 무슨 짓을 해도

main의 변수에 접근할 수 없다.

CallByAddress 는 말 그대로 main에 있는 변수의 주소값

가져와 쓰기 때문에 CallByAddress 에서 무슨 짓이든

main의 변수에 접근할 수 있다.

 

 

- 여기서 잠깐 무시무시한 scanf()의 무서움을 보자.

ex) 예제 소스

#include <stdio.h>

int main()
{
  char  cStr[8];

  printf("문자열 입력 : ");
  scanf("%s", cStr);

  printf("입력 받은 문자열 : %s \n", cStr);

  return 0;
}

출력 화면

 

여기까지는 별 문제 없어 보인다.

하지만

입력한 문자가 9개를 넘어가면서 오류가 발생한다.

위에서 cStr[8] 크기를 8byte로만 지정했기 때문에

9byte (9글자)를 받았을때에 오류가 난다.

 

지금까지 scanf("%d", &iNum); 이렇게 항상 변수 앞에 &를 붙였는데

포인터를 배우면서 보았듯이 주소값을 넘겨주기 때문에

scanf() 함수에서 main함수의 변수를 만질 수 있었다.

그래서 그 이상의 값이 들어왔을때 오류가 나는 것을 볼 수 있다.

상위 버전 컴파일러로 컴파일시 scanf 함수 사용만으로도 warning을 출력한다고 한다.

그러니 지금은 scanf() 함수를 쓰지만 나중에 안정성을 위해

다른 함수를 사용해야할 것이다.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

설정

트랙백

댓글

20140325 (배열, 문자열)

27일차

 

 

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

Array ( 배열 )

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

어제에 이어 계속 배열을 배웠다.

바로 예제 소스를 보자.

ex) 예제 소스

#include <stdio.h>

int main()
{
  int  arr1[5= {1,2,3,4,5};
  int  arr2[] = {1,2,3,4,5,6,7};
  int  arr3[15= {1,2};
  int  ar1Len;
  int  ar2Len;
  int  ar3Len;
  int  iCnt;

  printf("배열 arr1의 크기 : %d \n"sizeof(arr1) );
  printf("배열 arr2의 크기 : %d \n"sizeof(arr2) );
  printf("배열 arr3의 크기 : %d \n"sizeof(arr3) );

  ar1Len = sizeof(arr1) / sizeof(int);
  ar2Len = sizeof(arr2) / sizeof(int);
  ar3Len = sizeof(arr3) / sizeof(int);

  for(iCnt = 0; ar1Len > iCnt; ++iCnt)
  {
    printf("%d ", arr1[iCnt]);
  }
  printf("\n");

  for(iCnt = 0; ar2Len > iCnt; ++iCnt)
  {
    printf("%d ", arr2[iCnt]);
  }
  printf("\n");

  for(iCnt = 0; ar3Len > iCnt; ++iCnt)
  {
    printf("%d ", arr3[iCnt]);
  }
  printf("\n");

  return 0;
}

출력 화면 

위에서 보았듯이

int arr1[5] = {1,2,3,4,5}; 선언시 {} 를 사용해서 초기화가 가능하고

int arr2[] = {1,2,3,4,5,6,7}; 변수 뒤에 []만 붙이면 몇 개나 들어가는지 자동으로 샌다.

int arr3[15] = {1,2}; 첫번째와 두번째만 넣고 나머지는 0으로 초기화.

 

- 배열은 잘 사용해야 하는게 다른 메모리를 침범이 가능하므로

잘 확인하고 사용해야 한다.

 

- cp == cStr ?????

#include <stdio.h>

int main()
{
  char *  cStr = "Good morning!";
  char *  cp1;
  char *  cp2;

  cp1 = cStr;

  printf("%c \n", *cp1);

  printf("%s \n", cp1);
  printf("%s \n", cStr);

//  ++cp1;
  cp1 = cp1 + 5;

  printf("%s \n", cp1);
  printf("%s \n", cStr+5);

  cp2 = cStr;

  printf("%c \n", cStr[0]);
  printf("%c \n", cp2[0]);
  printf("%c \n", *(cp2+0) );
  printf("%c \n", *(cStr+0) );

  return 0;
}

출력 화면

 

이것을 통하여

cp == cStr

똑같이 주소값을 저장하고 있다.

조금 다른데

cp의 경우 주소값이 가리키는 값을 수정할 수 있는 반면

cStr의 경우 수정이 불가능하다.

const char * const cp == cStr 이게 정확한 똑같은 Data type이다.

 

 

 - 문자열, 문자배열

무조건 문자가 모여있다고 문자열이라고 안한다.

마지막에 NULL이 없으면 printf함수 사용 시 NULL 만나기 전까지

출력하므로 계속 출력을 하게되므로 의도치 않은 상황이 발생할 수 있으니

위험하다.

 

 

- 주소상수

#include <stdio.h>

int main()
{
  char  str[] = "Hello world!\n";
  char *  cp = "Hello world!\n";

  printf("str : 0x%08X \n", str);
  printf("&cp : 0x%08X \n"&cp);
  printf("cp : 0x%08X \n", cp);
  printf("main : 0x%08X \n", main);

  str[5= '!';
  cp[5= '@';

  printf(str);
  printf(cp);
  printf("Hello world!\n");

  return 0;
}

출력 화면

 

 

- 그리고 위에서 보면

cp[5] = '!'; 를 자세히 보면 cp포인터 변수가 가리키는 곳은 CODE 영역의 상수값이다.

윈도우에서는 실행이 되지만 리눅스에서는

이렇게 오류가 난다.

Windows 에서는 RO영역의 수정을 어느정도 허용하지만 (위험함)

linux 에서는 RO영역의 수정을 금지한다.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

설정

트랙백

댓글

20140324 (전역변수, 메모리 구조, 재귀함수)

26일차

 

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

전  역  변  수

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

- 지역변수와 다르게 어디서든 읽기 쓰기가 가능한 변수.

- 어떤 함수에서 쓰기 가능하기 위해 컴파일러가 제일 먼저 읽어야 하니

맨(include 밑에) 위에 선언한다.

- 기본적으로 0으로 초기화 돼있음.

- 변수가 만들어 지는 때

    지역변수 : Run Time

    전역변수 : Compile Time

 

- 지역변수는 Load 하면서 Mem에 할당됨.

- 전역변수는 컴파일당시에 생성되기 때문에 실행파일에 할당되어 있음.

 

ex) 예제 소스

#include <stdio.h>

void Add(int iVal);
int iNum;
int iNum1 = 10;
int iNum2;
int iNum3 = 20;
int iNum4;
int iNum5 = 30;
int iNum6;
int iNum7 = 40;

int main()
{
  printf("iNum : 0x%08X \n"&iNum);
  printf("iNum1 : 0x%08X \n"&iNum1);
  printf("iNum2 : 0x%08X \n"&iNum2);
  printf("iNum3 : 0x%08X \n"&iNum3);
  printf("iNum4 : 0x%08X \n"&iNum4);
  printf("iNum5 : 0x%08X \n"&iNum5);
  printf("iNum6 : 0x%08X \n"&iNum6);
  printf("iNum7 : 0x%08X \n"&iNum7);
  printf("Add : 0x%08X \n", Add);
  printf("main : 0x%08X \n", main);
  printf("printf : 0x%08X \n", printf);

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

  printf("문자열 주소 : 0x%08X \n""Welcome");
  return 0;
}

void Add(int iVal)
{
  if(0 == iNum)
  {
    printf("iVal : 0x%08X \n"&iVal);
  }
  
  iNum = iNum + iVal;
}

출력 화면

위에서 보았듯이 전역변수로 선언되 있는 변수는

main에서도 Add에서도 접근이 가능하다.

자세히 볼게 전역변수들을 관찰해 보면

초기화된 변수와 초기화 안된 변수의 주소값이 몰려 있는게

다르다.

초기화된 변수는 함수들의 주소값에 더 가깝다.

또 상수의 주소도도 초기화된 변수들의 주소와 가깝다.

 

 

 

 

 

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

재  귀  함  수

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

- 자신을 다시 부르는 함수

- 사용을 비추천

 

ex) 예제 소스

#include <stdio.h>

void Recursive(int iNum);

int main()
{
  Recursive(3);

  return 0;
}

void Recursive(int iNum)
{
  if(0 >= iNum)
  {
    return;
  }
  printf("Recursive call!! %d \n", iNum);

  Recursive(iNum - 1);
}

 출력 화면

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

설정

트랙백

댓글

20140321 (함수, 지역변수)

25일차

 

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

Function ( 함  수 )

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

- 어제 보았듯이 어떤 기능을 하는 역할.

- int Add(); 이런 형태는 함수의 원형( prototype, interface ) 라고 한다.

- int Add() { A } 이런 형태는 함수의 정의이다.

뭐 설명은 어려운데 예제를 보면 쉽다.

예제를 보자.

#include <stdio.h>

int Add(int iNum1, int iNum2);  // 함수 선언, 원형

/*
int Add(int iNum1, int iNum2)    // 이렇게 main 위에 와도 괜찮다.
{        // 선언, 정의? 다 같이 한다?
  int iResult = iNum1 + iNum2;

  return iResult;
}
*/


int main()
{
  int  iNum;

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

  return 0;
}

int Add(int iNum1, int iNum2)    // 함수 정의
{
  int iResult = iNum1 + iNum2;

  return iResult;
}

프로그램 진행 순서가

이렇게 흘러간다.

10, 20 이란 인수를 받아서 Add 함수에서 그 값만 받는다.

받은 값을 iResult에 넣어서 반환한다.

 


 

위에서 본 바와 같이

어떤 일정한 반복된 코드나,

역할을 나눌때

함수를 만들어 사용한다.

 

 

 

여기서 잠깐,

printf, scanf 등등 가져다 쓰는 함수들을 모아서

라이브러리 라고 부르고 컴파일 과정 중 링크때 연결된다.

 

우리가 만든 함수는 전처리 과정 중에 불러온다.

 

 

 

 

 

 

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

지역변수

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

- { 에서 } 까지가 지역변수의 수명.

ex) 예제 소스

#include <stdio.h>

main()
{
  int iNum;    // main 의 지역변수 iNum

  iNum = 0;

  if(0 == iNum)
  {
    int iNum = 1;    // if 의 지역변수 iNum
  }                      // if 의 지역변수 iNum 소멸

  printf("iNum : %d \n", iNum);    // main 의 지역변수 iNum

  while(2 > iNum)    // main 의 지역변수  iNum
  {
    int iTemp = 0;    // while 의 지역변수 iTemp 생성
                          // while문이 반복할 때마다 iTemp 생성, 소멸 반복
    ++iTemp;
    ++iNum;
    
    printf("iTemp : %d \n", iTemp);
    printf("iNum : %d \n", iNum);
  }                      // while문 지역이 끝나면서 iTemp도 소멸

  {
    int iNum = 0;    // 이름없는 지역의 지역변수 iNum

    
    iNum = 1000;
  }                     // 지역이 끝나면서 iNum도 소멸

  printf("iNum : %d \n", iNum);    // main 의 지역변수 iNum

  return 0;
}

출력 화면

위에서 보았듯이 지역변수끼리 이름만 같았지

서로 다른 주소를 사용하기 때문에 서로 영향을 끼칠 수 없다.

변수 이름이 같을 경우

더 작은 지역의 변수의 이름을 사용하고

없을 경우 한층 위 지역의 변수를 사용한다.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

설정

트랙백

댓글

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;

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

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

 

 

 

 

 

 

 

 

 

 

 

 

 

설정

트랙백

댓글