본문으로 바로가기

변수, 함수, 주소

"&변수명" 은 그 변수가 위치한 메모리 주소값 (상수형식, 참조포인터) 이다
"배열명"은 그 배열이 위치한 메모리 주소값(포인터) 이다.
"함수명"은 함수 코드가 있는 메모리 주소값(포인터) 이다.

 

메모리 공간

지역변수에 경우 메모리에 있는 스택에 저장이 된다.

전역변수와 상수는 전역 영역에 저장이 된다.

함수에 경우는 코드 영역에 들어간다.

 

  • 스택
  • 힙(heap)
    • 스택과 전역 공간 사이에 비어있는 공간
  • 코드
  • 전역 영역

포인터 구조 이해하기

2021.03.26 - [이론공부/자료구조] - 자료구조 공부#8 (포인터)

 

자료구조 공부#8 (포인터)

2021.03.23 - [이론공부/자료구조] - 자료구조 공부#7 (희소 행렬) 참고하기 좋은 이전 내용 이다. 포인터(Pointer) 다른 변수의 주소를 가지고 있는 변수 비유를 하자면 편지가 변수의값 우체통이 편지

thesauro.tistory.com

이전 자료구조 시간에 배운 포인터 구조에 대해 다시 살펴보자 

 

포인터를 사용하는 이유

  1. 포인터를 이용하면 편리한 경우가 있다.
    - 함수 포인터
  2. 포인터를 이용해야 하는 경우가 있다.
    _ 주소를 이용한 호출, 혹은 값 교환
  3. 포인터를 이용하면 더 효율적인 경우가 있다.
    - 포인터 수식의 코드는 크기가 작고 실행속도가 빠르다
int a;
a = 3;

int* pa;
pa = &a;

*pa = 7 // pa 가 가리키는 a값에 7을 넣는다
 
printf("%d \n", a);
C에서 포인터 문법 이해하기

 

배열에서의 포인터 주소

char array[5] = {"1", "2", "3", "4", "5"};
char* pa;
pa = array;

printf("%c \n", *(pa+1));
// 2 를 출력

char 에 경우 사이즈가 1 이므로 배열 첫주소 +1 다음 주소 순으로 저장이 된다.

+ sizeof(char)*(n) 를 쓰는것이 좋을거 같다.

 

int array[2][3] = {{0, 1, 2}, {3, 4, 5}};

int (*pa)[3];
pa = array;
// 1차원 배열 주소값을 닮을수 있는 pa 포인터 생성 후 array 주소값을 담는다

// 두 결과가 같다
printf("%d", *(*(pa+0)+0);
printf("%d", pa[0][0])

교수님 말로는 소중앞출소제거 느낌으로 int (*pa)[3]을 이해하라 하셨다.

해석할때 소괄호나 중괄호가 앞으로 가면 제거를 해서 해석을 하라는 느낌

위에 두개 결과가 같은 경우는 컴퍼일러 자체에 문법적으로 같게 정의를 해두어서 같은 결과가 나오는 것이지, 일일이 연산해서 해석하면 맞는 표현이 아니다.

 

함수에서의 포인터

 

기본적인 함수 포인터 선언 방법

void hello(){
 printf("HELLO");
}

void main(){
	// 인자가 없고, 리턴값이 void인 함수를 가리키는 포인터 변수 생성
    void (*p)();
    p = hello; // p포인터에 hello 주소값 삽입
}

 

 

레퍼런스(참조형 변수)

레퍼런스는 별명 혹은 변수의 또 다른 이름이라 생각하면 될 것이다.

int a;
int& babo = a;

여기서 a 와 babo 변수는 같은 주소값을 가지게 된다. 그는 (하지만 여기서 &를 포인터주소 가 아닌 참조라고 의미를 이해해야 한다)

레퍼런스에 경우는 위와 같이 선언과 동시에 초기화를 해야만 한다.

상수형과 마찬가지로 초기화 이후에는 읽을 수 있지만, 쓰기 작업은 할 수 없다.

 

void hello(int a, int b){
 printf("HELLO");
}

void main(){
	// 인자가 int a,b 이고, 리턴값이 void인 함수를 가리키는 포인터 변수 생성
    void (*p)(int a, int b);
    p = hello; // p포인터에 hello 주소값 삽입
}

인자가 있는 경우 이경우엔 변수명을 적어도 되지만 변수 타입만 적어도 된다

사용 용도

대부분 함수 호출 시 자주 사용된다.

void print(int& babo){
	printf("%d \n", babo);
}

void main(){
	int a = 2;
    print(a);
}

위와 같은 경우에서 처럼 레퍼런스를 인자로 받는 경우에는 초기화를 할 필요는 없다. 호출시에 자동으로 초기화 되기 때문에

 

void swap(int x, int y){
    int tmp;
    tmp = x;
    x = y;
    y = tmp;
}


void main(){

    int iX = 2;
    int iY = 3;
    
    printf("%d, %d \n", iX, iY);
    
    swap(iX, iY);
    
    printf("%d, %d \n", iX, iY);

}

위와 같은 경우(call by value)에는 iX iY의 변수는 변경 되지 않고 그대로 2, 3 으로 출력이 될 것이다.

 

void swap(int* x, int* y){
    int tmp;
    tmp = *x;
    *x = *y;
    y = tmp;
}


void main(){

    int iX = 2;
    int iY = 3;
    
    printf("%d, %d \n", iX, iY);
    
    swap(&iX, &iY);
    
    printf("%d, %d \n", iX, iY);

}

하지만 포인터를 이용한 호출 (call by address)를 이용하면 함수 호출시에 iX, iY 값도 바뀌게 되어 함수 호출후에 각 변수의 출력 값이 바뀔 것이다.

 

void swap(int& x, int& y){
    int tmp;
    tmp = x;
    x = y;
    y = tmp;
}


void main(){

    int iX = 2;
    int iY = 3;
    
    printf("%d, %d \n", iX, iY);
    
    swap(iX, iY);
    
    printf("%d, %d \n", iX, iY);

}

레퍼런스를 이용하여 호출(call by reference)을 하면 주소를 이용한 호출과 같이 변수가 바뀌게 되어 출력될 것이다.

 

함수호출에 경우에서 변수 값을 받고 그 변수 값또한 바뀌게 되는 상황인 경우에는 포인터를 이용한 호출을 사용해도 되겠지만, 좀더 간편하게 레퍼런스 호출을 이용하면 작성을 좀더 편하게 하여서 코드를 짤 수 있을 것 이다.

 


느낀점 : 포인터의 구조를 이해할 수 있었고, 레퍼런스 타입을 이해할 수 있었다.