ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • C - Pointer
    컴퓨터 구조 2021. 1. 8. 13:03

    Pointer

     

     

    아래의 코드는 a와 b를 swap하는 함수를 흉내낸 코드이다.

    분명 문제가 없어 보이지만, 실제로 프로그램을 실행 시켜보면, a와 b의 값이 바뀌지 않았음을 알 수 있다.

     

    #include <stdio.h>
    #include <stdlib.h>
    
    void swap(int* x, int* y);
    
    int main() {
    	int a = 123;
    	int b = 321;
    
    	printf("%d %d\n", a, b);
    	swap(&a, &b);
    	printf("%d %d\n", a, b);
    	
    	return 0;
    }
    
    void swap(int* x, int* y) {
    	int tmp = *x;
    	*x = *y;
    	*y = tmp;
    }
    //output
    123 321
    123 321

     

     

    왜 바뀌지 않았을까?!

     

     

    위의 그림과 같이 메모리의 code segment, data segment, heap segment. stack segment에 데이터가 저장된다. 함수를 호출할 때 지역변수는 stack segment에 저장되게 된다.

     

    그렇다면 위의 코드에서 a, b와 x, y는 어떻게 저장되고 있을까?

    C언어는 call by value방식으로 함수에 인자를 전달한다. call by value는 쉽게 말해서 값을 복사해서 전달한다는 뜻이다.

    따라서, swap함수가 호출되었을 때, 일어나는 일은 아래와 같다.

     

     

     

     

    swap 함수가 실행되면 stack segment에 x와 y의 메모리 공간이 추가로 할당되고 이 공간에 a와 b의 value가 복사된다. 이후, x와 y는 실제로 서로 swap되지만, a와 b에 직접적인 영향을 미치지는 못한다. 함수가 종료되면, swap함수에게 할당되었던 메모리가 해제되고, x와 y는 사라진다.

     

    이러한 이유로 함수 내에서 값을 swap하더라도, 함수 외부의 변수에 영향을 끼치지 못하는 것이다.

     

    그렇다면, a와 b를 swap하는 함수를 만들기 위해서는 어떻게 해야 할까?!

     

    포인터를 사용하면 가능하다!

     

    swap함수에 a와 b의 주소값을 전달하고, 해당 주소값에 저장된 정보를 포인터로 swap한다면, a와 b가 저장되어있는 메모리를 직접 건드리기 때문에 함수가 종료되더라도 a와 b에 변경된 값이 그대로 유지된다!

     

     

    아래와 같이 함수에 주소값을 전달해보자.

    #include <stdio.h>
    #include <stdlib.h>
    
    void swap(int* a, int* b);
    
    int main() {
    	int a = 123;
    	int b = 321;
    
    	printf("%d %d\n", a, b);
    	swap(&a, &b);
    	printf("%d %d\n", a, b);
    	
    	return 0;
    }
    
    void swap(int* a, int* b) {
    	int tmp = *a;
    	*a = *b;
    	*b = tmp;
    }

     

     

    malloc

     

    memory allocation

    메모리를 동적으로 할당하고, 포인터에게 확보된 메모리의 시작주소를 저장해주자.

    • malloc으로 확보한 int세개를 저장할 수 있는 메모리에 1,2,3을 넣어주었다.
    • 이후, 배열의 크기를 늘리기 위해서 tmp라는 포인터에 int네개가 들어갈 수 있는 크기의 메모리를 할당시킨 후 list에 저장된 3개의 int를 옮기고, 4번째 자리에 4를 추가로 저장했다.
    #include <stdio.h>
    #include <stdlib.h>
    
    int main() {
    	int* list;
    	list = malloc(3 * sizeof(int));
    
    	if (list == NULL) {
    		printf("failed");
    		return 1;
    	}
    
    	list[0] = 1;
    	list[1] = 2;
    	list[2] = 3;
    
    	int* tmp = malloc(4 * sizeof(int));
    	if (tmp == NULL) {
    		printf("failed");
    		return 1;
    	}
    
    	for (int i = 0; i < 3; i++) {
    		tmp[i] = list[i];
    	}
    	tmp[3] = 4;
    	free(list);
    
    	list = tmp;
    	for (int i = 0; i < 4; i++) {
    		printf("%i\n", list[i]);
    	}
    }

     

     

    realloc

    위의 방식은 다소 비효율적이다. 리스트의 길이가 n일때, 시간복잡도는 O(n)이다. 이때, realloc을 활용해 조금더 효율적으로 작업을 수행할 수 있다.

    realloc은 동적 할당되어 있는 메모리의 내용을 그대로 유지하며 확보된(할당된)메모리의 사이즈를 변경해주는 명령어이다.

     

    기존 메모리 영역에서 추가적으로 사용자가 요구한 만큼 메모리를 할당할 수 있다면, 주소를 그대로 유지시킨채 메모리만 추가로 할당한다. 추가적으로 메모리를 할당할 수 없는 경우 다른 공간에서 메모리를 할당해 주어 주소가 변경될 수도 있다. 

     

     

    주소가 동일하다

    #include <stdio.h>
    #include <stdlib.h>
    
    int main() {
    	int* list;
    	list = malloc(3 * sizeof(int));
    	if (list == NULL) {
    		printf("failed");
    		return 1;
    	}
    
    	list[0] = 1;
    	list[1] = 2;
    	list[2] = 3;
    
    	int* tmp = realloc(list,4 * sizeof(int));
    	if (tmp == NULL) {
    		printf("failed");
    		return 1;
    	}
    	printf("%p\n%p\n", list, tmp);
    
    	free(tmp);
    }
    //output
    01526AA0
    01526AA0

     

    1억개의 int가 들어갈 수 있는 메모리공간을 할당하고자 한 경우 주소가 변경되었다.

    #include <stdio.h>
    #include <stdlib.h>
    
    int main() {
    	int* list;
    	list = malloc(3 * sizeof(int));
    	if (list == NULL) {
    		printf("failed");
    		return 1;
    	}
    
    	list[0] = 1;
    	list[1] = 2;
    	list[2] = 3;
    
    	int* tmp = realloc(list,100000000 * sizeof(int));
    	if (tmp == NULL) {
    		printf("failed");
    		return 1;
    	}
    	printf("%p\n%p\n", list, tmp);
    
    	free(tmp);
    }
    //output
    00FB6D38
    011BE040

     

     

    realloc을 활용해 4개의 int가 들어갈 수 있는 메모리를 추가로 확보해보자.

    realloc(list, 4*sizeof(int))

    #include <stdio.h>
    #include <stdlib.h>
    
    int main() {
    	int* list;
    	list = malloc(3 * sizeof(int));
    	if (list == NULL) {
    		printf("failed");
    		return 1;
    	}
    
    	list[0] = 1;
    	list[1] = 2;
    	list[2] = 3;
    
    	int* tmp = realloc(list,4 * sizeof(int));
    	if (tmp == NULL) {
    		printf("failed");
    		return 1;
    	}
    
    	list = tmp;
    	list[3] = 4;
    
    	for (int i = 0; i < 4; i++) {
    		printf("%i\n", list[i]);
    	}
    
    	free(list);
    }
    //output
    1
    2
    3
    4

     

     

     

    틀린 부분이 있을 수 있습니다. 피드백 주시면 고치도록 하겠습니다! 감사합니다.👍

     

     

Designed by Tistory.