[cs50] 5. Memory

Joy·2020년 9월 8일
0

CS50

목록 보기
5/7

[cs50] 4강 Algorithms

1) 메모리 주소

16진수(Hexadecimal)

  • 컴퓨터에서 데이터를 처리하기 위해 16진수를 사용.
  • 2진수를 간단하게 표현 가능하기 때문
  • 0~9, A, B, C, D, E, F(15)
  • 0x 앞에 붙여서 표현
  • 2개의 16진수는 1byte의 2진수로 변환되기 때문에 정보를 표현하기 매우 유용

메모리 주소

메모리의 4bite를 차지하는 int n에 50을 저장하고 출력

#include <stdio.h>

int main(void)
{
    int n = 50;
    printf("%i\n", n);
}
>>> 50
  • C에서는 변수의 메모리상 주소를 받기 위해 & 연산자 사용
#include <stdio.h>

int main(void)
{
    int n = 50;
    printf("%p\n", &n);
}

>>>0x7ffe00b3adbc’  # 16진법으로 표현된 메모리의 주소
  • * 를 사용하면 그 메모리 주소에 있는 실제 값 가져옴
#include <stdio.h>

int main(void)
{
    int n = 50;
    printf("%i\n", *&n);
}

>>> 50

n의 주소를 얻고, 또 다시 그 주소에 해당하는 값을 얻어와 출력한 것이므로 결국 ‘50’이라는 값이 출력



2) 포인터

C의 포인터 : 변수의 주소를 쉽게 저장하고 접근

  • 포인터 역할을 하는 변수를 선언하기
    p라는 포인터 변수에 &n (변수 n의 주소)를 저장
    는 이 변수가 포인터라는 의미
    int 는 이 포인터가 int 타입의 변수를 가리킨다는 의미
#include <stdio.h>

int main(void)
{
   int n = 50;
   int *p = &n;
   printf("%p\n", p);
   printf("%i\n", *p);
}

>>> n의 주소를 출력
>>> p가 가리키는 변수의 값, 즉 변수 n의 값을 출력



3) 문자열

typedef char *string
string s = “EMMA”;

-> typedef : 새로운 자료형, char * : 문자에 대한 포인터, string : 자료형 이름

  • 문자열은 결국 문자의 배열
  • 마지막의 \0은 0으로 이루어진 바이트로, 문자열의 끝을 표시하는 약속
  • 변수 s는 결국 이러한 문자열을 가리키는 포인터 (문자열의 가장 첫번째 문자, 즉 주소 0x123에 있는 s[0]를 가리킴)

string 자료형 이용해서 문자 출력

#include <cs50.h>
#include <stdio.h>

int main(void)
{
    string s = "EMMA";
    printf("%s\n", s);
}

char 포인터로 문자 출력

#include <stdio.h>

int main(void)
{
    char *s = "EMMA";
    printf("%s\n", s);
}

s라는 변수는 문자에 대한 포인터가 되고, “EMMA”라는 문자열의 가장 첫 번째 값을 저장하기 때문

자료형 string을 정의하는 것의 장점?
라이브러리를 코드에 추가할 필요가 없어지므로 더 간결하게 코드를 짤 수 있다



4) 문자열 비교

  • s라는 포인터의 값, 즉 “EMMA”라는 문자열의 가장 첫 값인 “E”에 해당하는 메모리 주소를 출력
#include <stdio.h>

int main(void)
{
    char *s = "EMMA";
    printf("%p\n", s);
}
  • 각 글자 주소값 출력
printf("%p\n", &s[0]);
printf("%p\n", &s[1]);
printf("%p\n", &s[2]);
printf("%p\n", &s[3]);

&s[0]는 “E”의 주소값을, &s[1]은 “M”의 주소값을, &s[2]은 “M”의 주소값을, &s[3]은 “A”의 주소값을 의미

  • 주소값으로 글자 출력
printf("%c\n", *s);
printf("%c\n", *(s+1));
printf("%c\n", *(s+2));
printf("%c\n", *(s+3));

>>> E M M A

compare

#include <cs50.h>
#include <stdio.h>

int main(void)
{
    // 사용자로부터 s와 t 두 개의 문자열 입력받아 저장
    string s = get_string("s: ");
    string t = get_string("t: ");

    // 두 문자열을 비교 (각 문자들을 비교)
    if (s == t)
    {
        printf("Same\n");
    }
    else
    {
        printf("Different\n");
    }
}

위 코드로 s, t둘다 EMMA를 입력하면 Differenct가 나옴. 왜? 다른 장소에 저장되어 있어서. -> 같은 내용을 입력했다고 같은 곳에 있다는 것은 아님

이렇게 s, t는 다르다고 받아들임

-->> 따라서 정확한 비교를 위해서는 포인터를 활용하여 해당하는 변수의 주소 안의 값을 가져와서 비교해야 한다.



5) 문자열 복사

#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);
}

emma 입력
>>> Emma
>>> Emma

s라는 변수에는 “emma”라는 문자열이 아닌 그 문자열이 있는 메모리의 주소가 저장
t도 s와 동일한 주소
t를 통한 수정은 s에도 그대로 반영

  • 메모리 상에서 복사하려면?? 메모리 할당 함수 필요
#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);
}

>>> s는 “emma”가, t는 “Emma”가 출력

malloc이라는 함수(정해진 크기 만큼 메모리를 할당하는 함수)를 이용해서 t를 정의
s 문자열의 길이에 널 종단 문자(\0)에 해당하는 1을 더한 만큼 메모리를 할당
루프를 돌면서 s 문자열 배열에 있는 문자 하나 하나를 t 배열에 복사



6) 메모리 할당과 해제

malloc 함수를 이용하여 메모리를 할당한 후에는 free라는 함수를 이용하여 메모리를 해제해야 함
why? 메모리에 저장한 값은 쓰레기 값으로 남게 되어 메모리 용량의 낭비가 발생 ->> 메모리 누수



7) 메모리 교환, 스택, 힙

메모리의 데이터 저장 공간

  • 머신코드 : 프로그램이 실행될 때 그 프로그램이 컴파일된 바이너리가 저장
  • 글로벌 : 프로그램 안에서 저장된 전역 변수가 저장
  • 힙 : malloc으로 할당된 메모리의 데이터가 저장
  • 스택 :프로그램 내의 함수와 관련된 것들이 저장

swap

#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;
}

//>>> x:1 y:2
//>>> x:1 y:2

swap 되지 않음.
why? 교환하는 대상이 x, y 그 자체가 아닌 함수 내에서 새롭게 정의된 a, b이기때문. a와 b는 각각 x와 y의 값을 복제. 서로 다른 메모리 주소에 저장됨.

a, b, x, y, tmp 모두 스택 영역에 저장되지만 a와 x, b와 y는 그 안에서도 서로 다른 위치에 저장된 변수 -> a와 b를 바꾸는 것은 x와 y를 바꾸는 것에 아무런 영향도 미치지 않음

=>> 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); //swap 함수는 포인터를 입력으로 받음. 그래서 포인터가 저장할 주소 전달받아야 함. 변수의 주소값은 변수명 앞에 주소연산자 &
   printf("x is %i, y is %i\n", x, y);
}

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



8) 파일 쓰기

사용자로부터 값을 입력받아 파일에 출력하는 프로그램을 작성하기

힙 오버플로우 / 스택 오버플로우

  • 힙 영역에서는 malloc 에 의해 메모리가 더 할당될수록, 점점 사용하는 메모리의 범위가 아래로 늘어남.
  • 스택 영역도 함수가 많이 호출돌 수록 메모리의 범위 위로 늘어남

->> 제한된 메모리 용량 하에서는 기존의 값을 침범

포인터를 이용한 함수

[get_int 코드]

#include <stdio.h>

int main(void)
{
	int x;
    scanf("%i", &x); // 변수의 주소 받기
    printf("x: %i\n", x); //
}

//scanf : 형시지정자를 쓰면 그 형식대로 입력받음.

[get_string 코드]

#include <stdio.h>

int main(void)
{
    char s[5]; //무자열 크기 지정
    printf("s: ");
    scanf("%s", s); //scanf에 그대로 s를 입력
    printf("s: %s\n", s);
}


//  s를 크기가 5인 문자열, 즉 크기가 5인 char 자료형의 배열로 정의하였기 때문
//clang 컴파일러는 문자 배열의 이름을 포인터처럼 -  scanf에 s라는 배열의 첫 바이트 주소를 넘겨주는 것
  • 배열은 메모리가 연속적으로 할당된 공간
  • 문자열은 문자가 연속적으로 있는 것 -> 그 메모리 공간의 첫번 째 주소 의미

파일쓰기 예시

#include <cs50.h>
#include <stdio.h>
#include <string.h>

int main(void)
{
    FILE *file = fopen("phonebook.csv", "a");
    char *name = get_string("Name: ");
    char *number = get_string("Number: ");
    fprintf(file, "%s,%s\n", name, number);
    fclose(file);
}



9) 파일 읽기

파일을 읽고 JPEG 파일인지를 검사하는 프로그램을 작성


#include <stdio.h>

int main(int argc, char *argv[])
{
    //argc가 2가 아니라면, 파일명이 입력되지 않았거나 파일명 외의 다른 인자가 입력되었기 때문에 1(오류)을 리턴
    if (argc != 2)
    {
        return 1;
    }

	//입력받은 파일명(argv[1])을 ‘읽기(r)’ 모드로 불러옴
    FILE *file = fopen(argv[1], "r");

    if (file == NULL)
    {
        return 1;
    }
 
 //3인 문자 배열을 만들고, fread 함수를 이용해서 파일에서 첫 3바이트를 읽어
   unsigned char bytes[3];
    fread(bytes, 3, 1, file);


//읽어들인 각 바이트가 각각 0xFF, 0xD8, 0xFF 인지를 확인
    if (bytes[0] == 0xff && bytes[1] == 0xd8 && bytes[2] == 0xff)
    {
        printf("Maybe\n");
    }
    else
    {
        printf("No\n");
    }
    fclose(file);
}


// JPEG 형식의 파일을 정의할 때 만든 약속으로, JPEG 파일의 시작점에 꼭 포함되어 있어야함

Q1 다음 출력?

int main(void)
{
	int n = 20;
    printf("%i \n", *&n) 
}

답 : 20
why? &는 변수가 위치한 메모리의 주소, *는 주소에 위치한 값
자체
*&n은 n의 주소에 위치한 값

profile
roundy

0개의 댓글