[C] C lang 레지스터, 스택 & 힙, 동적 메모리. 다중 포인터

메모리의 종류

프로그램에서 자주 사용하는 부품

1 . CPU

  • 모든 코드의 로직(연산)을 실행하는 장치
  • 컴퓨터에서 가장 핵심적인 두뇌라 할 수 있다.

2 . 메모리

  • 실행중인 코드 및 연산의 중간 결과등을 저장하는 공간
  • 변수나 배열 등에 대입되는 데이터가 저장됨

  • 무언가를 저장한다는 점에서 메모장과 유사하다.

20221119_233242

메모리는 또 다쉬 나뉨

  • 프로그램 중 여러 데이터가 공유하는 메모리는 둘로 나뉨.

    • 스택(stack)메모리
    • 힙(heap) 메모리
  • 사실상 이 둘 물리적으로 같은 메모리

  • 이 외에 데이터 섹션, 코드 섹션 등 있으나 그건 특정 코드 및 데이터 용으로 고정



기본은 힙 메모리

  • 힙 메모리가 범용적인 기본형태





스택은 특별한 용도를 가진 메모리

  • 프로그램 마다 특별한 용도에 사용하라고 별도로 떼어놔준게 스택 메모리

    • 엄밀히 말하면 프로그램마다가 아니라 그 프로그램의 쓰레드이다.

그리고 CPU안에도 저장공간이 있다.

  • 레지스터: CPU에서만 사용할 수 있는 고속 저장공간

  • 엄밀한 의미의 메모리는 아님.



스택 메모리

20221119_234442

  • 좀 더 알고 싶다면 함수를 호출 할 때 스택에 대해 어떻게 돌아가는 지 는 함수 호출 규약 (calling convention) 에 따라 달라짐

    • 구조체 배울 떄 매개변수 4개까지는 값으로 전달하라는 회사 이야기를 했는데 이건 특정 호출 규약에서 성능 향상이 가능한 부분이기 떄문
  • 함수 호출규약에 대한 자세한 정보는 coding convention으로 구글 검색

레지스터

  • CPU가 뇌, 메모리가 공책이면 뇌에서 생각한 걸 공책에 옮기려면 시간이 걸리듯, CPU가 생각한 걸 메모리에 적거나 그로부터 읽을때도 시간이 걸림

메모리를 읽고 쓰는 게 느린 이유1

  • CPU가 메모리에 접근 할 때마다 버스 타야됨
  • 즉, CPU가 연산할 때마다 메모리에 접근하는 시간이 발생

    • 버스가 크면 한번에 많이 읽지만 메모리 낭비가 생기고
    • 버스가 작으면 메모리 낭비는 적을 수 있겠지만 여러번 왔다갔다함.

    메모리를 읽고 쓰는 게 느린 이유2

    • 대부분 컴퓨터에 장착하는 메모리는 DRAM

      • 영어로는 Dynamic Random Access Memory
    • Dram은 가격이 저렴한 대신 단점이 있다.

      • 기록한 내용을 유지하기 위해 주기적으로 정보를 다시 써야함
      • 다시 쓰는 동안 또 시간을 소모
    • 이러한 단점이 없는 메모리가 SRAM

      • static Ram
      • Dram에 비해 훨씬 비쌈
      • 이걸 몇기가 달기에는 부담

더 나은 방식을 찾기위해 고민함

  • 그래서 비용은 저렴하지만 속도가 빠른 컴퓨터들을 만들기 위해 고민

  • 그래서 나온 방법이 Sram을 cpu와 메모리 사이에 두는 것

    • 단, 너무 비싸니 매우 적은 용량만
    • 일반적인 sram과는 다름
  • CPU랑 가까이 두고 싶어서 아예 CPU안에 넣어버림
  • 그게 레지스터다



레지스터

  • 레지스터는 CPU가 사용하는 저장공간 중 가장 빠른 저장공간

  • CPU가 연산시 보통 레지스터에 저장된 레지스터를 사용

  • 그 연산 결과도 레지스터에 다시 저장하는 것이 보통이다
  • 레지스터는 흔히 말하는 메모리가 아님

    • cpu가 레지스터에 접근하는 방식과 메모리에 접근하는 방식이 다르다

    20221120_021638



어셈블리어로 보는 레지스터

20221125_160707

  • 레지스터는 CPU가 사용하는 저장 공간 중 가장 빠른 저장공간
  • CPU가 연산 할 떄 보통 레지스터에 저장되어있는 데이터를 사용
  • 그 연산결과도 다시 레지스터에 저장하는게 보통
  • 강조하지만 레지스터는 흔히 말하는 메모리가 아니다.
    • cpu가 레지스터에 접근하는 방법과 메모리 접근하는 방법이 다른게 보통이다.



20221125_162107



x86 아키텍처 에서 사용하는 레지스터

  • 8개의 범용 레지스터

    • ESP, EBP, EAX, EBX, ECX, EDX 등등
  • 6개의 세그 먼트 레지스터
  • 1개의 플래그 레지스터
  • 1개의 명령어 포인터



20221125_165457



register 키워드

프로그래머가 빠른 레지스터를 직접 쓸 수는 없나? 메모리 안 거치고 레지스터 쓰면 좋을 텐데 -> 어셈블리 쓰면 가능하긴 함

레지스터 사용을 요청하는 예

20221125_170712



레지스터 키워드

20221125_170956

  • 저장 유형 지정자
  • 가능하다면 해당 변수를 레지스터에 저장할 것으 ㄹ요청
  • 실제로 레지스터 사용 할지말지는 컴파일러가 결정
  • 레지스터는 메모리가 아님
  • 따라서 레지스터 변수들은 제약을 몇가지 받음

제약1. 변수의 주소를 구할 수 없음

20221125_174307

제약2. 레지스터 배열을 포인터로 사용 불가 20221125_174332

제약3. 블록범위에서만 사용 가능

20221125_174355

근데 사실상 현재 데스크톱 컴파일러들은 register키워드 넣는다고 특별히 해주는 일이 없다. 대부분 무시함

예전 임베디드 시스템에는 이렇게 구분하는 게 의미가 있었다

  • CPU용량도 작고 메모리 용량도 적었다
  • 최적화를 잘 안해주는 컴파일러 떄문에 프로그래머가 레지스터 사용까지 지시했어야 한다.

근데 위는 예전 이야기

  • 보통 컴파일러가 배포(release)모드에서 알아서 최적화

    • 불필요한 스택 메모리 없애고
    • 레지스터에만 있으면 빠를거 같은 변수들은 그렇게 해주고
  • 더이상 프로그래머가 수동으로 사용하지 않는 키워드



힙 메모리

스택 메모리의 단점 1 - 수명

  • 함수가 반환하면 그 위에 있던 데이터가 다 날아간다.

    • 즉, 함수 안에 있는 변수의 수명은 함수가 끝날 때 까지
  • 그렇지 않고 데이터를 오래 보존하려면 전역변수 또는 static 키워드를 사용해야 했다

    • 이런 변수의 수명은 프로그램 실행 내내
  • 이건 도 아니면 모?
  • 근데 어떤 경우는 그 중간 어딘가에서 타협 원할수도 있다.
    • 내가 원할때 만들거나 지울 수 있는 그런 저장공간

스택 메모리의 단점 2 - 크기

  • 앞에서도 말했듯 특정 용도에 쓰라고 별도로 떼어놓은 메모리
  • 그 크기는 컴파일 시 결정하므로 너무 크게 못 잡음
  • 프로그램을 실행할 시 메모리가 1MB 일수도 있고 4GB일수도 있다.

    • 최소한에 맞출 수밖에 없다
  • 그래서 엄청 큰 데이터를 처리해야 할 경우 스택 메모리에 못 넣는다.
    • 예: 4k로 녹화해서 파일 크기가 2GB인 영상 파일



힙 메모리

  • 컴퓨터에 존재하는 일단 범용적인 메모리
  • 스택 메모리처럼 특정 용도로 떼어놓은 게 아니다.
  • 스택과 달리 컴파일러 및 CPU가 자동적으로 메모리 관리를 안 해줌.
  • 따라서 프로그래머가 원할 때에 원하는 만큼 메모리를 할당받아와 사용하고 원하는 때에 반납(해제)할 수 있다.



힙 메모리의 장점

  • 용량 제한이 없음

    • 컴퓨터에 남아있는 메모리만큼 사용 가능
  • 프로그래머가 데이터의 수명을 직접 제어

    • 스택에 저장되는 변수 처럼 함수호출이 끝나면 사라지지 않음
    • 전액 변수처럼 프로그램이 실행 되는 동안 계속 살아있는 것도 아님.



힙 메모리의 단점 1

  • 빌려온 메모리를 직접해제 안하면 누구도 그 메모리를 쓸 수 없다.

    • 그 메모리는 계속 누군가에게 빌려준 상태
    • 만약 빌려준 상태에서 메모리를 잃어버리면 누수가 발생

20221125_193455



그래서 C는 언매니지드 언어

  • 매니지드 언어 (C#, java등)는 메모리 해제를 알아서 해주는 언어

  • 이를 메모리 관리 해준다 해서 (managed) 매니지드 언어라 한다.

  • 이 메모리 관리 기능은 다른 훌륭한 프로그래머들이 구현한 것

    • 메모리 누수가 날 가능성은 적음
    • 당연히 범용적으로 만든게 아니라 속도 등이 느릴 수 있음
  • C는 그런 언어가 아니라 직접 관리를 해줘야 한다.

    • 훌륭한 프로그래머는 메모리 관리는 할 수 있어야 한다.
    • 최대의 효율성을 선택하는 대신
    • 실수를 막기 위해서는 여러가지 원칙들을 습관화 한다.



비유: 매니지드 vs 언매니지드 이사

20221125_195736

힙 메모리의 단점 2

  • 스택에 비해 할당/해제 속도가 느림
    • 스택은 오프셋 개념 vs 힙은 사용/비사용 중인 메모리 관리 개념
    • 메모리 공간에 구멍이 생겨 효율적으로 메모리 관리가 어렵기도 함.

20221125_200349



정적 메모리 vs 동적 메모리

  • 스택 메모리는 정적 메모리

    • 이미 공간이 잡혀있음
    • 할당/해제가 자동으로 관리되게 코드가 컴파일 됨
    • 오프셋 개념으로 정확히 몇 바이트씩 사용해야 하는 지 컴파일 시 결정
  • 힙 메모리는 동적 메모리

    • 실행 중 크기와 할당/해제 시기가 결정 됨



동적 메모리

프로그램이 동적 메모리 쓸 떄는 세가지 단계 거침

1 . 메모리 할당 2 . 메모리 사용 3 . 메모리 해제



동적 메모리 사용단계 1: 메모리 할당

  • 힙 관리자에게 메모리를 xxx바이트만큼 달라고 요청
    • 관리자는 연속되는 그 만큼의 메모리를 찾아서 반환
    • 반환된 메모리는 어떤 자료형에 저장 가능? 메모리 주소니 당연히 포인터다.

20221126_173333



동적 메모리 사용단계 2: 메모리 사용

  • 그 메모리를 원하는 대로 사용
    • 예: int 배열에 성적 저장 한 뒤 평균 구해서 float 변수에 저장

20221126_173451



동적 메모리 사용단계 3: 메모리 반납/해제

  • 힙 관리자에게 그 메모리 주소를 돌려주면서 다 썼다고 알려준다.
    • 관리자는 그 메모리 주소를 아무도 사용하지 않는 상태로 바꿈

20221126_173649



메모리 할당 및 해제 함수, malloc()

20221126_174051

메모리 할당 함수 해제함수, 재할당 함수,

alloc = allocate

그 외 메모리 가져오면 메모리에 쓸수 있는 함수들.

우측 기타 메모리 관련 함수들은 포인터를 가져와서 사용할 수 있는 거. 그리고 우측은 동적 메모리에만 사용가능한게 아님. 정적 메모리에도 사용 가능



malloc()

20221126_174442

  • 메모리 할당의 약자
  • size바이트 만큼 메모리 반환
  • void 포인터로 반환.
  • 포인터 외 다른 자료형으로 반환 불가

20221126_174541

  • 반환된 메모리에 들어있는 값은 쓰레기값
  • 즉 , 초기화 안해줌
  • 메모리가 더 없거나 해서 실패시 널 반환



free(), malloc() 사용 예

malloc 의 짝궁함수 free()

  • 동적 메모리는 프로그래머가 직접 빌리고 반납해야 됨.

  • 빌렸으면 메모리 반납해야 함
  • 메모리에 건 속박을 풀어준다고 해서 free()
  • 안 지우게 되면 누수 발생

malloc()코드 작성 시 바로 free()코드도 추가하는 습관을 들이는 게 좋다.

20221126_185525 ]

malloc () 쓰는 예시들

20221126_185945

20221126_191348

제대로 된 free() 설명

  • 할당 받은 메모리를 해제하는 함수
  • 즉, 메모리 할당함수를 통해 얻은 메모리만 해제 가능
  • 그 외 주소를 매개변수로 전달 할 시 결과가 정의 되지 않음

20221126_191741



동적 메모리 할당 시 문제

할당 받아온 주소를 그대로 연산에 사용시

  • 메모리 할당 함수가 반환한 주소가 저장된 변수를 그대로 포인터 연산에 사용시 메모리 해제할 떄 문제 발생할 수 도 있다.

  • 최초에 받아온 주소가 아니라 다른 위치를 가리킴 -> 그 주소로 메모리 해제 요청 ->결과가 정의되지 않음->망함

20221126_201134

코딩 표준 : 할당 받은 포인터로 연산 금지

  • 메모리 할당 함수에서 받아온 포인터와 포인터 연산에 사용하는 포인터를 분리하자.

20221126_203519

해제한 메모리를 또 해제해도 문제

20221126_204001



코딩표준 : 해제 후 널 포인터를 대입

  • free()한 뒤 변수에 NULL을 대입해서 초기화
    • 안 그러면 해제된 놈인지 나중에 모르니
    • 널 포인터를 free() 매개 변수 전달해도 안전

20221126_204426

이렇게 설명할 거 왜 두번에 나눠한건가

  • 메모리 누수 떄문
  • malloc 한 뒤 free 까먹으면 메모리 누수
  • malloc으로 받아온 주소를 지역변수에서 저장해놨는데 해제 안하고 함수에서 나갈 시 주소가 사라져 지울 방법이 아예 없어진다.

  • 그래서 습관을 잘 들여야 한다.



free()는 몇 바이트를 해제할지 어떻게 알지?, calloc(), memset(), realloc()

free()는 몇 바이트를 해제할 지 어떻게 아는가?

1 . 구현마다 다르지만 보통 malloc(32)하면 그것보다 조금 큰 메모리를 할당한 뒤, 제일 앞 부분에 어떤 데이터들을 채워넣음.

앞에 데이터를 집어넣고 이 데이터는 그 힙 관리자가 볼 그런 데이터

2 . 그리고 그만큼 오프셋을 더한 값을 주소로 돌려준다.

  • 돌려받은 주소로부터 원래 요청한 32바이트를 사용.

3 . 나중에 그 주소 해제를 요청하면 free()가 다시 오프셋만큼 빼서 그 앞 주소를 본 뒤 실제 몇바이트가 할당 되었었는지 확인 후 해제

20221126_222350

20221126_222719

calloc()

20221126_222830

  • 의미는 아무도 모름
  • 메모리 할당시 자료형의 크기(size)와 수(num)을 따로 지정
  • 모든 바이트를 0으로 초기화 해줌
  • 잘 안씀

20221126_223450



memset()

20221126_223640

  • string.h에 있다
  • char로 초기화(1바이트씩)
  • 그외 자료형으로 하려면 for을 써야 됨
  • 다음과 같은 경우 겨로가 정의 안 될 수 있다.
    • count가 dest의 영역 넘어설 경우(소유하지 않은 메모리에 쓰기)
    • dest가 널 포인터일 경우 (널 포인터 참조)

20221126_223714

20221127_000811



realloc()

20221127_001230

  • 이미 존재하는 메모리(ptr)의 크기를 new_size 바이트로 변경
  • 새로운 크기가 허용하는 기존 데이터를 그대로 유지



크기가 커져야 할 떄, 두가지 경우가 있다 1

20221127_001637

1 . 지금 갖고 있는 메모리 뒤 충분한 공간이 없으면 새로운 메모리를 할당한 뒤, 기존 내용을 복사하고 새 주소 반환.

20221127_001708 20221127_001741

20221127_001757

크기가 커져야 할 떄, 두가지 경우가 있다 2

20221127_002739

2 . 지금 갖고 있는 메모리 뒤에 공간이 충분하다면 그냥 기존 주소를 반환(보장은 없음). 그리고 추가된 공간을 쓸 수 있게 됨.



크기가 작아질 떄도 비슷함

  • 기존 주소가 그대로 반환되거나
  • 다른곳에 메모리 새로 할당 후 , 새 주소 반환할 수도



realloc()의 메모리 누수 문제, memcpy()

realloc()에서 메모리 누수가 날 수 있다.

20221127_004856

  • 반환값

    • 성공시 , 새롭게 할당된 메모리 시작 주소를 반환하며 기존 메모리는 해제됨
    • 실패시 , NULL을 반환하지만 기존 메모리는 해제되지 않음.
  • 실패시 메모리 누수가 발생할 수 있다!



메모리 누수가 나는 경우

20221127_004955

void* nums;
nums = malloc(SIZE)
nums = realloc(nums,2*SIZE);
  • 원래 nums에 저장된 주소가 사라짐
  • NULL이 반환 됐다는 이야기는 재할당에 실패했다는 의미
  • 따라서 기존 메모리는 해제되지 않음
  • 그러나 그 주소를 잃어버려서 해제할 수 없다. 메모리 누수 발생.



20221127_014925

20221127_015017



memcpy()

20221127_015632

void * memcpy(void* dest, const void* src, size_t count);
  • string.h에 있다
  • src의 데이터를 count 바이트만큼 dest에 복사
  • 다음과 같은 경우 결과가 정의 되지 않음

    • dest의 영역 뒤에 데이터를 복사할 경우(소유하지 않은 메모리에 쓰기)

    • src나 dest가 널 포인터일 경우(널 포인터 역참조)



메모리 누수 안나게 코드를 작성해야한다.

  • realloc()을 쓸떄는 정말 조심해야 한다
  • 그래서 차라리 malloc()+memcpy()+free()로 좀 더 명시적으로 드러나게 코딩하는게 나을수도 있다.

  • 그냥 신경 안 쓰고 realloc()을 쓰는 경우도 많음
    • 메모리 시작 주소가 변하지 않은 경우 데이터를 복사하지 않아 성능상 이득
    • 그리고 메모리가 없어서 널포인터 반환시 어떻게 대처하나?
    • malloc()에서 실패하는 일이 없다고 가정하고 , 코딩을 하는 이유도 마찬가지.



realloc()의 특수한 경우

  • nums = realloc(NULL, LENGTH)
    • 새로운 메모리 할당
    • malloc(LENGTH)와 동일함



memcmp(), 정적 vs 동적 메모리

memcmp()

int memcmp(const void* lhs, const void* rhs, size_t count)
  • 첫 count 바이트 만큼 메모리를 비교하는 함수
  • strcmp()와 매우 비슷
  • 단, 널 문자를 만나도 계속 진행

  • 다음의 경우 결과가 정의 되지 않음
    • lhs과 rhs의 크기를 넘어서 비교할 경우(소유하지 않은 메모리에 쓰기)
    • lhs이나 rhs이 널 포인터일 경우(널포인터 역 참조)


20221127_023759

단, 구조체가 포인터를 가질 경우는

20221127_023929

동적 메모리 할당을 이용한 깊은 복사

20221127_024029



구조체 멤버 변수 - 배열 vs 포인터

20221127_024557



베스트 프랙티스: 정적 메모리 vs 동적 메모리

  • 정적 메모리를 우선적으로 사용할 것

    • 훌륭한 C프로그래머 최대한 정적으로 사용하려 함
  • 안 될떄만 동적 메모리



동적 메모리의 소유권 문제

동적으로 할당한 메모리의 큰 문제

  • 메모리의 소유주는 누구?
  • 바로 메모리를 생성한 함수
  • 소유주란? 그 메모리를 반드시 책임지고 해제해야 하는 주체
  • 소유주가 아닐 떄는 그냥 빌려 사용할 뿐 해제하면 안됨
    • 소유주와 비 소유자가 모두 해제하면 문제
    • 근데 한명도 해제 안해도 문제



문제의 예

20221127_031927

C++에서는 RAll로 해결

  • 자원 획득은 초기화(RAll, Resource Acquisition Is Intialization)

    • 여기서 자원은 메모리
  • C++은 개체지향을 지원하는 언매니지드 언어
  • 한 개체가 생성 될 떄 필요한 메모리를 할당(생성자란 특별한 함수)

  • 그 개체의 수명이 다할 떄 그 메모리를 해제(소멸자를 특별한 함수)

  • 즉, 개체의 수명이라는 범위에 메모리의 수명 종속 시킴
  • 이 원칙을 잘 따르면 실수할 여지가 적다.



근데 C는 개체가 없다

  • RAll를 최대한 흉내내면 좋긴 하다

  • C에서 RAll를 할 수 있는 부분

    • 한 함수 안에서 malloc(), free()를 다 호출할 수 있는 경우
  • 이런 이유 때문에 앞의 예에서 malloc을 추가시 곧바로 free하라는 이유.

20221127_032321


20221127_032349

20221127_032423



그럼 원래의 문제는 어떻게 해결할까?

  • C에서 굉장히 어려운 문제
  • 최선의 방법은?
  • 이런 함수가 최대한 없게 한다
  • combine_string()예 , 함수 안에 할당하는 대신 함수 밖에 할당 후 매개 변수로 전달

20221127_032632



동적 할당 후 반환을 피할 수 없다면?

  • 딱히 모두가 동의하는 표준은 없다.
  • 어떤 함수가 메모리 할당하면 그 사실을 함수에 표기

    • 주석으로 표기하는 법도 있음. 근데 사람들은 주석 잘 안 읽음
  • 동적 메모리를 할당하는 변수라면 변수명에 표기하는 방법도 있다.



20221127_033128



베스트 프랙티스 정리

20221127_033318

1 . malloc 작성 뒤 곧바로 free 추가

2 . 동적 할당을 한 메모리 주소를 저장하는 포인터 변수와 포인터 연산에 사용하는 포인터 변수를 분리해 사용하자

  • 원래 포인터 변수를 사용할 시, 주소를 잃어버려서 해제 못할 수 도 있다.

3 . 메모리 해제 후, 널 포인터 대입

4 . 정적 메모리를 우선으로 쓰고, 어쩔수 없을 때만 동적 메모리 사용

20221127_033451

5 . 동적 메모리 할당할 경우, 변수와 함수 이름에 그 사실을 알리자

20221127_033526

다중 포인터

  • 포인터란 뭐였나
  • 주소를 저장하는 변수
  • 그럼 그 변수의 주소를 또 저장할 수 있나?

20221127_204423

  • 그럼 주소를 저장한 변수의 주소는 어디에 저장하나?

    • 포인터
    • 그럼 포인터에 들어간 데이터의 자료형은?

이중 포인터

int num = 10;
int * p = #
int ** p = &p;
  • 포인터 변수의 주소를 저장하는 변수를 이중포인터라고 한다

20221127_205827

20221127_205937

20221127_205950



현실과 실제 데이터 비교

  • 사물함 번호, 집 주소 : 메모리 주소
  • 친구들 + 파티 : 실 데이터

20221127_210931



20221127_211223



다중 포인터를 쓰는 이유, 다중 포인터 예

이중 포인터는 주소의 주소

  • 즉, 주소를 반복
  • 그럼, 주소의 주소의 주소도 가능할까?

    • 가능
    int *** r = &q;
    
  • 원한다면 30중 포인터도 가능
int ***************** z = &y;
  • 근데 3중 포인터 쓰는 일도 매우 가끔
  • 4중 이상은 거의 안씀.



이중 포인터는 왜 쓰나?

  • 2차원 배열이 사실 이중 포인터와 비슷
    • 2차원 자료가 많이서 2D 배열 많이 씀
    • 구구단이라던가, 이미지라던가 20221127_213047

20221127_213104


메인함수의 매개변수에도 존재

  • 메인함수의 매개변수인 argv도 엄밀히 말하면 이중포인터

    • 포인터의 배열. 각 배열은 포인터

      20221127_213133

3차원 공간 다루는 곳은?

  • 이미지 같은 2차원 다룰 떄는 2차원 배열이나 2중 포이넡 썼으니
  • 3차원 공간 다루는 곳에선 3차원 배열 씀.
  • 즉, 3중포인터도 사용 가능.

포인터 변수를 서로 교체하기

20221128_094835



알아둬야 할 것

1 . 메모리 종류

  • 메모리 스택, 힙 메모리, 동적 메모리
  • 레지스터 및 CPU(register키워드)
  • 스택과 힙 차이

2 . 동적 메모리

  • 스택 메모리는 크기의 단점, 수명의 단점이 있었다.

  • 이거를 프로그래머가 알아서 제어하게 해주는 게 동적 메모리.

  • 동적 메모리 할당에 오는 순간 스택처럼 자동으로 메모리 관리 해주는 사람이 없어서 프로그래머가 기억해서 다 해제해줘야됨.

  • 그리고 동적 메모리 가져오면 느릴 수 있다.

  • 그래서 정적메모리를 가능하면 사용하고 정 안될 때 동적 메모리를 사용하자

  • 동적 메모리는 실수할 부분이 정말 많다.(메모리 누수 같은 부분)

3 . 다중 포인터

  • 포인터의 저장하는 값은? 주소






© 2021.03. by yacho

Powered by github