프로그래밍 언어/[ C ]

[ C ] 05. 메모리와 포인터의 사용

kim.svadoz 2020. 8. 13. 10:06
반응형

[ 메모리와 포인터의 사용 ]


#include <stdio.h>
#include <stdlib.h>        // malloc, free 함수

int main(){
    int *numPtr;        // int형 포인터 선언
    numPtr = malloc(sizeof(int));        // int의 크기 4바이트만큼 동적 메모리 할당

    *numPtr = 10;        // ****포인터를 역참조 한뒤 값 할당****

    printf("%d\n", *numPtr);        // 10 , 포인터를 역참조하여 메모리에 저장된 값 출력

    free(numPtr);        // 동적 메모리 해제
    return 0;
}
  • malloc함수에 실제로 값을 저장할 때는 시스템의 한계(RAM, 디스크)이상 저장할 수 없다.

메모리 내용을 한꺼번에 설정하기(memset)

memset함수를 사용하면 메모리의 내용을 원하는 크기만큼 특정값으로 설정할 수 있다.(string.h)

memset(포인터, 설정할값, 크기);
    - void *memset(void *_Dst, int _Val, size_t _Size);
    - 값 설정이 끝난 포인터를 반환
#include <stdio.h>
#incldue <stdlib.h>
#include <string.h>

int main(){
    long long *numPtr = malloc(sizeof(long long));        // long long의 크기 8바이트만큼 동적 메모리 할당

    memset(numPtr, 0x27, 8);        // numPtr이 가리키는 메모리를 8바이트만큼 0x27로 설정

    printf("0x%llx\n", *numPtr);        // 0x2727272727272727 : 27이 8개 들어가있음

    free(numptr);
    return 0;
}
# 실행 결과
0x2727272727272727
  • 먼저 memset 함수를 사용하려면 string.h 또는 memery.h 헤더파일을 포함해야 한다. 그리고 memset함수에 포인터, 설정할 값, 설정할 크기를 넣으면 된다.

  • memset함수는 주로 다음과 같이 설정할 값을 0으로 지정하여 메모리의 내용을 모두 0으로 만들 때 주로 사용한다.

    memset(numPtr, 0, 0);        // numPtr이 가리키는 메모리를 8바이트만큼 0으로 설정

> 자료형의 크기와 포인터의 크기

// memset 함수에 설정할 크기를 지정할 때 보통 숫자대신 sizeof를 사용한다.
long long *numPtr = malloc(sizeof(long long));
memset(numPtr, 0, sizeof(long long));
// 여기서 메모리를 sizeof(long long)크기만큼 할당했으므로 설정할 크기도 sizeof(long long)과 같이 지정해야 하며 sizeof(long long *)과 같이 포인터의 크기를 지정하면 안된다! 포인터의 크기는 메모리주소의 크기일뿐 실제 메모리가 차지하는 크기가 아니다. 이부분은 char 포인터에 메모리를 할당해보면 잘 알 수 있을것이다.

char *cPtr = malloc(sizeof(char));        // char의 크기 1바이트만큼 동적 메모리 할당
memset(cPtr, 0, sizeof(char));        // char의 크기 1바이트만큼 0으로 설렁(올바른 방법)
memset(cPtr, 0, sizeof(char *));    // 32비트 : char 포인터의 크기 4바이트만큼 0으로 설정(잘못된 방법)
                                // 64비트 : char 포인터의 크기 8바이트만큼 0으로 설정(잘못된 방법)

// 할당해줄 크기가 포인터의 크기가 일치한다고 해서 옳은 방법이 아니므로 해당 메모리가 차지하는 크기를 선언해주자.

널포인터

#include <stdio.h>

int main(){
    int *numPtr1 = NULL;        // 포인터에 NULL 저장
    printf("%p\n", numPtr1);        // 00000000
    return 0;
}
# 실행 결과
00000000
  • NULL이 들어있는 포인터를 널 포인터라고 하며 아무것도 가리키지 않는 상태를 뜻한다. 따라서 역참조를 할 수 없다.

  • 실무에서는 다음과 같이 포인터가 NULL인지 확인한 뒤 NULL이면 메모리를 할당하는 패턴을 주로 사용한다.

    if(ptr = NULL){                // ptr이 널 포인터라면
        ptr = malloc(1024);        // 1024바이트만큼 메모리 할당
    }

포인터에 할당된 메모리를 배열처럼 사용하기

#include <stdio.h>
#include <stdlib.h>

int main(){
    int *numPtr = malloc(sizeof(int)*10);        // int 10개 크기만큼 동적 메모리 할당

    numPtr[0] = 10;        // 배열처럼 인덱스로 접근하여 값 할당
    numPtr[9] = 20;        // 배열처럼 인덱스로 접근하여 값 할당

    printf("%d\n", numPtr[0]);        // 배열처럼 인덱스로 접근하여 값 출력
    printf("%d\n", numPtr[9]);        // 배열처럼 인덱스로 접근하여 값 출력

    free(numPtr);        // 동적 메모리 해제
       return 0;
}
# 실행 결과
10
20
int numArr[10];                                 // int형 요소 10개를 가진 배열 생성
int *numPtr = malloc(sizeof(int) * 10);            // int 10개 크기 만큼 메모리 할당

numArr[0] = 10;
numPtr[0] = 10;
free(numPtr);
  • 배열 numArr은 한 번 선언하면 끝이지만 포인터 numPtr은 malloc함수로 메모리를 할당했기 때문에 free함수로 해제해야한다.

  • *numPtr처럼 포인터를 역참조한 것과 numPtr[0] 처럼인덱스 0에 접근한 것은 같은 값을 가져온다. 그리고 numPtr[1]*(numPtr+1)도 같은 값을 가져오는데 후자와 같은 방식을 포인터 연산이라고 한다.

    image-20200805113154568

반응형