관리 메뉴

History

c 언어 온라인 무료강좌 4-1차시 정리 본문

Tipslab 강좌 복습/김성엽 선생님 c 강의 복습

c 언어 온라인 무료강좌 4-1차시 정리

luckybee 2021. 1. 19. 17:51
728x90
반응형

해당 게시물은 김성엽 선생님의 강의를 바탕으로 만든 게시물입니다.

 

●포인터

 

-포인터는 다른 변수처럼 자기 자신은 메모리에 존재하고 있지만 포인터 변수 자체가 다른 메모리의 주소를 저장하고 있는 특징을 가지고 있다.

-포인터든 포인터가 아니든 명령어는 비슷하다.

 

-주소도 데이터이고, 정수도 데이터이다.

-주소 값은 정수이다.  EX) 1000번지라고 하면 1000은 정수이다.

 

1000번지라는 주소 값이 있다. 그래서 그 주소 값을 정수형 변수에 대입을 하려고 한다. 그래서 int a=1000; 이러한 코드를 작성한다.

 

그러나 

 

과연 이렇게 변수에 들어간 1000이라는 데이터는 주소 값으로 들어간 걸까? 아니다. 이 1000이라는 숫자는 그냥 단순한 정수 1000일뿐이다. 그러면 주소 1000을 대입하려면 어떻게 해야 할까?

 

그럴 때는 포인터를 쓰면 된다. 

 

단순히 int a=1000;이라는 것이 아니라 int *a=1000; 이렇게 쓰면 어떤 정수에 대한 주소라는 값이 1000이라는 뜻이 된다.

 

포인터 변수 선언 및 대입 예시

결국 int a=1000; 은 정수 값 1000을 가지는 변수이고 int *a=1000은 1000번지 주소 값을 가지는 포인터 변수이다. 

 

만약 int *a;라는 포인터 변수 a를 선언을 하고 포인터 변수 a에 1000을 대입하고 싶다.

 

int main()
{
   int *a;
   a=1000; //이렇게 하면 될까?
}

이러면 오류가 난다! 왜 오류가 나냐? a=1000에서 a는 주소를 의미하는 int*의 자료형이고 1000은 int의 자료형이기 때문에 오류가 난다. int값은 int * 값에 대입할 수 없다!

 

그래서 단순히 a=1000이라는 정수 값이 아니라 a=주소 값; 이렇게 대입을 해야 한다. 그러면 그 주소 값은 어떻게 대입해야 하나?? 

 

int*로 캐스팅을 해버리면 된다! 한마디로 형 변환하라는 소리다.

int main()
{
    int *a;
    int b;
    a=(int *)1000;   //이렇게
}

무슨 뜻이냐면 1000이 정수인데 이 1000은 일반 정수가 아니라 int*로 형 변환한 주소 값이다.

 

그럼 컴파일러가 "오호 이 자식 정수가 아니라 주소군? 너 인정" 이러면서 잘 해석해준다^^

 

그. 러. 나 이 방법은 요즘엔 잘 안 쓴다... 그냥 설명을 위해서 쓰는 방법이니 이해하기 위해서 알아두자!

 

요즘에는 &(엔드) 연산자를 사용하면 된다. 그러면 주소 값이 도출이 되어서 오류가 나지 않는다!

 

int main()
{
    int *a;
    int b;
    a=&b;   //이렇게
}

 

★추가 상식 1: 포인터 연산이 느리다??

 

일반 변수에 값을 대입해서 연산하는 것과 포인터 변수에 값을 대입해서 연산하는 것의 속도 차이는 없다!

 

포인터 변수가 느리다는 것은 포인터 변수가 가지고 있는 주소를 이용해서 다른 변수를 참조할 때 느려진다.

왜냐하면 바로 가지 못하고 다른 주소를 참조해서 가기 때문에

 

쉽게 설명하면 버스 탈 때 경유해서 간다고 생각하면 편하다. 직행버스와 경유버스 누가 더 빠를까? 답은 직행이다. 경유라고 말하는 사람은.. 여기까지...

 

결론: 값 대입은 일반 변수와 포인터 변수의 속도 차이는 없지만 다른 값을 참조할 때 속도 차이가 난다.

 

★추가 상식 2 :void*(보이드 포인터)??

 

나중에 Windows 프로그램하다 보면 많이 보일 void* 뭘까?

 

그냥 4바이트라는 용량을 사용하고 싶을 때 int로 변수를 선언해서 사용할 수 돼있지만, void *를 이용하면 4byte라는 용량을 사용할 수가 있다. 왜냐하면 *(포인터)변수는 주소를 저장하면 변수이기 때문에 4byte이기 때문이다. 결국 두 변수의 크기는 같다!!

 

우리 상식선에서는 두 변수가 비교가 안될 수 도있지만, 메모리 입장에서는 그냥 크기가 같은 4byte 변수이다. 

 

그러나 int a라는 정수형 변수는 포인터 연산을 사용하지 못한다.

ex)*a=2-> x

 

반면 void* p는 포인터로 선언된 4byte 변수이기 때문에 포인터 연산과 주소 연산이 가능하다.

ex)*(int*) p 이렇게 사용 가능

 

둘 중에 void* 가 더 융통성이 크다. 왜냐! 같은 메모리(4byte)이고 연산도 가능한데 void*는 주소연 산도 가능하기 때문이다!

 

여기서 질문 하나 왜 void*를 사용하는데 형 변환을 해야 하는 건가?

 

답: void는 정해져 있지 않기 때문에 형 변환이 필요하다.

 

+더 알아두기)

int와 int*의 차이

 

 

 

p자료형이 뭐냐고 물어보면 int * 자료형이다. 그러나 int와 *는 다른 의미임 *는 뒤에 오는 포인터 변수 이름(p)이 주소를 저장하는 변수임을 알려주는 것이고 int는 해당 주소에 사용량을 4byte라고 정의해주는 것이다. 

 

자 그럼 a의 주소를 포인터 변수 p에 한번 저장해보자

 

p를 a에 넣는 과정 1

bp는 base pointer이다. 

 

bp가 100번지에 있다고 가정했을 때 a의 주소 값은 bp-3번째 위치이다 고로 97번째 위치이다.

 

이제부터 p=&a라는 코드가 어떠한 연산 과정을 거쳐서 p에 대입이 되는지 알아보자

 

먼저 p=&a 코드는 2번의 연산을 한다 &연산과 =연산이다.

 

2가지 연산

먼저 &연산을 설명하겠다.

 1: 대입은 p의 주소 값에 들어가야 하기 때문에 대입이 들어간 것이다.

 2: 어셈블리의 특징으로 AX레지스터에 값을 임시로 보관한다. 그러면 무슨 값을 보관하느냐?

 

어셈블리어의 특징: 이름으로 하는 메모리(레지스터) ex) AX레지스터, BX레지스터 등등

 

 3: bp-3 값(a의 주소임)을 대입

 

이제 =연산을 설명하겠다.

 1: 대입은 포인터 변수 p에 a의 주소 값을 대입해야 하기 때문에 4byte(포인터 변수)의 크기로 대입한다.

 2:  p의 시작 주소 값은 ptr[bp-7]이다.  아 하기 싫어  

 3: 그럼 a의 주소값은 어디에 보관되어 있는가? AX레지스터에 보관

 

그럼 어떤 결과로 도출되는가?

 

 

P에 성공적으로 a를 대입한 모습

a의 시작 주소(bp-3)는 16진수로 61이기 때문에 00000061 이렇게 대입을 해야 한다. 대입할 때는 거꾸로 대입을 하기 때문에 저렇게 된 거다.

 

또 다른 연습을 해보자 a값을 직접 대입해보자.

 

a=0x1234; 이렇게 쓰면 직접 대입이고 어셈블리 해석을 해보면 {대입, 4, ptr [bp-3], 00/00/12/34h}이다. 해석해보면

4byte 크기로 ptr [bp-3](a의 시작 주소에), 1234h(c언어에서는 0x로 대입하지만 어셈블리에서는 숫자 끝에 h를 붙인다.)

 

그러면 그 결괏값은?

자 그러면 *p=0x3377이라고 적었을 때 아까와 같이 *번지 지정 연산자와 = 대입 연산자가 사용된다. 즉 2번을 연산하는 것이다.

이것을 해석해보면 p가 가지고 있는 주소에 가서 0x3377을 대입하라는 뜻

그러면 p가 가지고 있는 주소를 꺼내오는 것이 우선이다.

*p=0x3377연산

 

이제 어셈블리로 해석을 해보자.

 

*연산을 보면 p의 주소 값을 알아와야 한다. 그래서 4byte의 크기의 ptr[bp-7]의 주소값을 BX에 대입한다. ptr [bp-7] 주소 값에는 기존에 있던 값이 존재하기 때문에 BX는 기존의 값이 있던 주소(0x00000061)를 가지고 있다.

 

이제 =연산을 보겠다. 4byte의 크기만큼 ptr [BX]의 위치에 0x3377을 대입한다.

 

 

그러면 다음과 같은 결과가 도출한다.

파란색 박스로 a의 값이 수정

 

 

아 생각보다 시간이 길어져서 1, 2로 나눠야겠다...

 

728x90
반응형
Comments