22.03.17 메모리

메모리 주소

  • 16진수로 표현
  • 16진수는 수 앞에 0x를 붙여서 표현 (0xFF)
  • 메모리 주소를 받는 연산자 : &
  • 주소에 있는 값을 받는 연산자 : *
  • 특정 값의 메모리 주소는 메모리 주소에 할당된 값을 "가리킨다"
  • 아래와 같은 방식으로 Pointer 역할을 하는 변수를 선언할 수 있다.
#include <stdio.h>

int main(void)
{
   int n = 50;
   int *p = &n;
   printf("%p\n", p);
   printf("%i\n", *p);
}
  • p 는 n의 메모리 주소를 저장한 "Pointer"이다.
  • p 는 int data type의 변수를 "가리킨다"

문자열 (String)

  • THERE IS NO STRING
  • string s = "EMMA" 라고 할 때, s는 결국 E(s[0])의 주소를 가리키는 Potinter이다.
  • 원래는, char *s = "EMMA" 라고 할 수 있다.
  • typedef char *string : char * (char의 주소를) string 이라는 자료형으로 선언할 것이다.
  • 문자열이란, char의 첫 번째 주소를 기억하고, 끝은 \0으로 끝난다는 것이 규칙이다.
  • 문자열 변수에는 문자열이 있는 메모리의 주소가 저장된다. (char *s = "...")
#include <cs50.h>
#include <ctype.h>
#include <stdio.h>

int main(void)
{
    string s = get_string("s: ");
    string t = s;

    t[0] = toupper(t[0]);

    printf("s: %s\n", s);
    printf("t: %s\n", t);
}
  • 그래서, 이렇게 설정하면, s의 주소값이 t라는 변수에 씌워지기 떄문에, t의 첫 글자를 대문자로 바꾸면 s의 첫 글자 또한 대문자로 바뀌게 된다.
  • 이럴 때 사용하라고 있는 것이 "메모리 할당 함수"인 "malloc"이다.
#include <cs50.h>
#include <ctype.h>
#include <stdio.h>
#include <string.h>

int main(void)
{
    char *s = get_string("s: ");
    char *t = malloc(strlen(s) + 1);

    for (int i = 0, n = strlen(s); i < n + 1; i++)
    {
        t[i] = s[i];
    }

    t[0] = toupper(t[0]);

    printf("s: %s\n", s);
    printf("t: %s\n", t);
}
  • malloc() 함수의 파라미터로 메모리 크기를 설정 해줄수 있다.
  • 위 예시에서는, \0까지 생각해줘야 하기 때문에 입력받는 string 길이에 +1을 해서 파라미터로 넘겨주었다.
  • 그리고, null terminator인 \0까지 복사하기 위해, i < n + 1 로 작성하여 for 구문을 실행했다.
  • 이는 간단하게 strcpy(t, s); 이렇게 표현해줄수 있긴하다.

메모리 할당 & 해제

  • malloc : memory allocation. 할당한 메모리의 첫 byte 주소를 돌려준다.
  • 메모리 할당 : 메모리의 일부를 가져와서, 그 곳을 가리키는 "Pointer"를 주는 것.
char *t = malloc(strlen(s) + 1);
  • 메모리를 할당 받았지만, 해제 해주지는 않았음. ( > free를 사용하여 해제해줘야 함)
  • free : 할당 되었던 메모리를 다시 반환함. 반환 해야, 프로그램이 더 많은 메모리를 사용할 수 있음. 사용하지 않는 메모리는 반드시 해제할 것.
  • 메모리 확인 디버깅은 valgrind를 사용하였다.
  • 이렇게 메모리 누수를 확인할 수 있다. 함수의 마지막 줄에 free(t);를 추가해서 해제하자.
  • free를 이용하여 해제를 하지 않으면, 메모리에 저장한 값은 "쓰레기 값"이 된다.
  • buffer overflow : 할당 메모리 공간을 넘어 접근하는 것. (아래 예시)
int *x = malloc(10 * sizeof(int));
x[10] = 0;  // Buffer overflow에 해당함. 

메모리 교환, Stack, Heap

메모리 안에는 데이터 저장 구역이 나뉘어져 있다.

  • Machine code = compiled binary
  • Globals = 전역 변수
  • Heap : malloc이 메모리를 할당하는 곳 (Top -> Bottom)
  • Stack : 함수가 호출될 때 지역변수가 쌓이는 공간 (Bottom -> Top)
  • main 함수의 변수들은 stack에 저장
  • 하지만, 해당 메모리 구조는 그닥 좋은 구조가 아님. malloc이 많이 호출될수록, 함수가 많이 호출될수록, heap과 stack의 영역이 서로 커질거고, 어디선가 두 메모리의 영역이 충돌하게 되어있음.
  • Heap overflow(malloc 과호출) & Stack overflow(함수 과호출)
  • -> Buffer overflow 발생 : 화면 정지, 파일이 안열리거나 동작하지 않는 그런 상황이 발생함..
#include <stdio.h>

void swap(int a, int b);

int main(void)
{
    int x = 1;
    int y = 2;

    printf("x is %i, y is %i\n", x, y);
    swap(x, y);
    printf("x is %i, y is %i\n", x, y);
}

void swap(int a, int b)
{
    int tmp = a;
    a = b;
    b = tmp;
}

  • swap 함수는 스택에 쌓여지고, 순차적으로 실행된 후, 완료되면 버려진다. (더이상 사용되지 않음)
  • a, b는 x, y의 복사된 값을 사용하였기 때문에, main 내의 x, y가 영향을 받지 않았다. (&a != &x, &b != &y)
  • 근본적 해결책 : x, y의 주소를 알려줘서, swap 함수가 그 주소로 간 후, 해당 값을 바꾸게 해야 함.
  • 즉, swap 함수의 a, b가 x, y를 "가리키게" 해야 함.
#include <stdio.h>

void swap(int *a, int *b);

int main(void)
{
    int x = 1;
    int y = 2;

    printf("x is %i, y is %i\n", x, y);
    swap(&x, &y);  // x, y의 메모리 주소를 넘겨줌
    printf("x is %i, y is %i\n", x, y);
}

void swap(int *a, int *b)  // 넘겨받은 주소를 a와 b에 저장
{
    int tmp = *a;  // a 주소에 저장된 값을 *로 불러와서 tmp에 저장
    *a = *b;  // a 주소에 저장된 값을 b 주소에 저장된 값으로 변경 
    *b = tmp;  // b 주소에 저장된 값을 tmp 값으로 변경
}

파일 읽기, 쓰기

  • 변수의 값을 바꾸고 싶다면, 값으로 전달하면 안됨(복사본이 전달되고, 사라짐) &x 와 같이 주소를 전달해줘야 함.
  • scanf("%s", s); -> 저장 할 주소를 넘겨줌. array의 경우, 첫 byte 주소를 넘겨받음.
#include <cs50.h>
#include <stdio.h>
#include <string.h>

int main(void)
{
    // Open file : FILE 이라는 새로운 자료형을 가리키는 포인터 변수 file
    FILE *file = fopen("phonebook.csv", "a");  // 파일 내용 저장 csv : comma seperated value

    // Get strings from user
    char *name = get_string("Name: ");
    char *number = get_string("Number: ");

    // Print (write) strings to file
    fprintf(file, "%s, %s\n", name, number);

    //Close file
    fclose(file);
}
  • 항상, 포인터와 메모리 주소 개념에 대한 내용을 상기시킬 것.

좋은 웹페이지 즐겨찾기