-
CS50x 2023 - Lecture 4 - MemoryIT/CS50x 2023 2023. 12. 24. 00:34
Hexadecimal(16진수)
16진수(Hexadecimal)는 0부터 9까지의 숫자와 A부터 F까지의 알파벳으로 이루어진 숫자 체계이다. 2진수와의 변환에 유용하며, 컴퓨터 메모리 주소와 색상 코드 등을 표현하는 데에 자주 사용된다. 10진법과의 혼동을 피하기 위해 앞에 0X를 붙여서 표현하는것이 관례이다. (e.g. 0X10는 10진법으로 16임)
6번째 라인에서 보다시피 format을 %p로 하고 출력하려는 n앞에 &를 붙이면, 저 integer가 현재 저 c file을 러닝하는 space의 메모리의 주소값을 hex형태로 출력할 수 있다.
pointer
포인터란 C언어에서 쓰이는 개념으로, 변수 등의 메모리상 주소를 나타낸다.
아래 예시의 6번째 줄은 int n의 주소를 *p로 받는 모습이다. 변수의 주소를 나타낼때는 그 주소 앞에 &을 붙이고, 변수의 주소를 받을때는 주소를 받는 변수 앞에 *를 붙여야 한다. 변수 타입이 integer라서 5, 6번째 모두 int로 시작하는데, n이 int가 아니고 float이었다면 둘다 float으로 시작해야한다. 6번째 줄의 int *p에서 int는 *p의 타입이 아니라, *p가 어느 integer의 주소임을 의미한다.
그리고 p 자체도 어쨌든 메모리상에서 공간을 차지하기때문에 p의 주소도 있다. 이 p의 주소를 또 가르치는 다른 포인터를 만들수도 있다. 그걸 더블포인터라고 함.
7번째 줄을 아래처럼 바꾸면(p대신 *p) p가 가리키는 바로 그 변수로 가게되어 n이 가진 값을 출력하게 된다.
참고로 C에는 string이란 타입이 없다. 대신 문자열을 배열처럼 써서 string처럼 나타낸다.
아래 예시는 "HI!"라는 문자열의 조합을 *s라는 포인터로 받아 6번째 라인에 전체의 주소, 7~10번째 라인에 각 문자의 주소를 나타내는데,
보다시피 전체 주소 = 첫번째 문자의 주소이고, 나머지 문자들은 연속적으로 배정된 주소를 갖고있음을 알 수 있다.
저렇게 주소가 한 바이트씩 떨어져있기 때문에 특정문자의 주소에 +1, +2 등 연산하여 몇바이트 떨어진 문자의 주소를 구하는등의 pointer 연산을 할 수 있다.
Copying
위의 예시에서는 string library를 활용하여 8번쨰 줄에서 string s를 입력값으로 받고, 10번째줄에서 t에 s의 값을 넣어주고 있다.
그리고 12번쨰 줄에서 t의 첫번째 문자열만 대문자로 바꿔주었고 s값은 변경하지 않았는데, 터미널 출력에서 보면 s, t둘다 바뀌어있다.
10번째 줄에서 t에 s의 값을 넣는게 아니라, s의 메모리 주소를 넣고 있기 때문이다.
이건 C에서 존재하지 않는 string type을 위한 library를 써서 언뜻 이상해보이지만, 사실 저 library없이 C언어에서 지원하는 char타입으로 저 상황을 나타내면 아래와 같다.
8번, 10번째 줄처럼 사실 저 데이터 타입은 string이라기보다 char *s, char *t, 즉, s, t라는 character의 주소값이기 때문이다.
이 문제를 해결하기 위해서 stdlib.h 라이브러리를 불러와서 malloc(for memory allocation) 펑션을 활용할 수 있다.
코드에서 보이진 않지만, 1~5번째줄 사이에 #include <stdlib.h> 가 추가되었다. standard library란 뜻으로, 사용자가 직접 메모리를 제어할 수 있게 해주는 라이브러리이다.
11번째줄에서 malloc 펑션을 써서 s의 길이보다 1개 더 많은 메모리를 t에게 부여하는 것을 볼 수 있다. 길이가 1개 더 많아야 하는 이유는 string의 끝에 \0(null 값)이 반드시 붙기 때문이다.
13번째~16번째 줄에서는 t의 문자열 하나하나에 s의 각 인덱스에 상응하는 문자열을 넣고 있다. 이때 이전의 예시와는 다르게 t는 고유의 메모리 할당량을 갖고 있어서 s값에는 영향이 없다. s와 t의 주소가 이전과 달리 다르기 떄문이다.
18~21번째줄에서 t만 첫번쨰 문자를 대문자화 하고 있으며, 저 코드에 hi!란 입력값을 넣어 돌리면 다음과 같다.
s값에 영향없이 t만 변하였다!
이렇게 malloc을 사용하여 메모리를 부여받아 활용한 경우, 반드시 코드 끝에서 해당 메모리를 free(더이상 쓰지 않겠다는 뜻)하는 습관을 들여야 한다. 이 펑션 또한 stdlib.h에 함께 포함되어있으며, 활용 하지 않는 메모리는 free하는것이 C언어에서의 관례라고 한다.
참고로, 미연의 에러 방지를 위해서 아래의 10~13번째 줄과 같은 코드를 추가할 수도 있다.
9번째 줄에 따라 입력된 값이 지나치게 길어서 memory allocation fail이 나는 경우, 해당 변수의 주소는 null(0)이 된다.
그러한 경우 프로그램을 종료하라는 뜻으로 return 1;을 할수 있다.
Valgrind
메모리 디버깅 및 프로파일링에 쓰이는 도구로, 별도로 설치해서 써야한다.
> valgrind ./파일명과 같이 커맨드 명령어를 입력해서 해당 파일을 실행하는데 memory leakage등의 문제가 없는지 확인할 수 있다.
Garbage values
위 코드와 같이 1024개의 integer가 들어가는 scores란 배열을 만들었는데 아무런 값도 넣지 않으면, 이 프로그램은 1024 바이트 메모리를 랜덤하게 써서 해당 주소에 있는 값을 가져온다. 이 경우 1024개의 정수가 랜덤하게 출력되는데, 파일을 돌리는 서버에서 랜덤하게 가져온 garbage values들이다.
Swap
a, b의 값을 tmp를 매개로 swap하려 할 때, C에서 아래처럼 코드를 짤 수 있겠으나 실제로 swap이 이뤄지지는 못한다. 해당 주소에 가서 값을 가져오지 못하기 때문.
아래처럼 짜줘야 실제 그 a, b의 주소로 가서 값을 가져온 뒤 성공적인 swap을 할 수 있다.
scanf
여태까지 cs50 library의 get_string 함수를 사용했지만, 사실 이것은 파고들어가보면 scanf 함수를 대체하는 것이다.
scanf함수는 사용자로부터 값을 입력받아 변수에 저장할 수 있게 해준다.
정수형 변수를 입력받는 예시는 아래와 같다.
7번줄에서 scanf를 하고 있는데 x를 그대로 넣어주는게 아닌 x의 reference란 의미로 &x를 넣어주는것에 유의한다.
string을 받을때는 아래와 같은데, int를 받을때랑 사뭇 다르다.
7번줄에서 보다시피 &s가 아니라 그냥 s로 입력한다. 5번에서 정의되는 s가 이미 주소값이기 때문이다.
5번의 array는 스트링의 맨 끝에 붙는 \0를 포함하여 총 4바이트의 메모리를 할당받겠다는 뜻이고, 결국 3개 글자로 이뤄진 string을 입력받는다는 뜻이다.
array그 자체로 주소에 대한 포인터 역할을 하기 때문에 (그것이 array의 특징) 7번줄에서 &s를 할 필요가 없다.
'IT > CS50x 2023' 카테고리의 다른 글
CS50x 2023 - Lecture 6 - Python (0) 2023.12.26 CS50x 2023 - Lecture 5 - Data Structures (0) 2023.12.25 CS50x 2023 - Lecture 3 - Algorithms (1) 2023.12.23 CS50x 2023 - Lecture 2 - Arrays (0) 2023.12.23 CS50x 2023 - Lecture 1 - C (0) 2023.12.22