16진법
메모리를 다룰 때 16진법이 적절하다.
0, 1, 2, 3, ... , 9, 10이 아니라 A, B, C, D, E, F
그런데 그냥 쓰면 10진법이랑 헷갈려서 앞에 0x를 붙여준다. 0x는 아무런 의미가 없음! 16진법이라고 알려주는 역할.
0x0, 0x1, 0x2, 0x3, ..., 0x9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF ...
우리가 쓰는 RGB 색상 표기법 또한 16진법이다.
빨,초,파가 하나도 없다 = 00 00 00 (검은색)
빨간색만 있다 = FF 00 00
초록색만 있다 = 00 FF 00
파란색만 있다 = 00 00 FF
빨,초,파 다 있다 = FF FF FF (흰색)
메모리 세계에서 어떤 일이 일어날까?
다음과 같은 코드가 있다.
#include <stdio.h>
int main(void)
{
int n = 50;
printf("%i\n", n);
}
컴퓨터의 메모리 어딘가에 변수 n이 4바이트의 크기로 저장되었을 것이다. (4칸으로 표현)
(왜 4바이트냐면 int가 4바이트의 메모리를 차지하기 때문이다.)
사실은 32bits로 구성된 0과 1이 50을 표현하고 있을 것이다.
변수 n이 0x12345678 이라는 위치에 있다고 가정해보자. (어딘지 중요하지 않고, 임의의 큰 수로 가정)
코드를 살짝 바꿔서 다음과 같이 써 보자.
#include <stdio.h>
int main(void)
{
int n = 50;
printf("%p\n", &n); // &는 '~의 주소'를 의미하는 연산자. 즉 n의 주소를 가져오는 역할.
}
%i 대신 %p 라고 쓰면 주소를 출력해준다.
>> 컴파일하고 돌려보니 0x7ffe00b3adbc 라고 나왔다. 이게 바로 포인터 값이다.
즉 포인터란, 컴퓨터 메모리의 주소를 가리키는 것이다. 그래서 %p라고 씀!
&가 '~의 주소'를 의미하는 연산자였다면, *는 '그 주소로 가줘'라는 의미이다.
따라서 n의 주소가 아닌, n의 값(50)을 출력하려고 한다면
#include <stdio.h>
int main(void)
{
int n = 50;
printf("%i\n", *&n); // 50이 출력됨
}
좀 바보같지만 위와 같이 쓰면 된다. n의 주소가 뭐냐고 물은 후 그 주소로 가달라고 했기 때문에 n의 값인 50이 출력된다.
(실제로 이렇게 쓰지는 않는다.)
포인터
#include <stdio.h>
int main(void)
{
int n = 50;
int *p = &n; // 변수 p에 n의 주소를 저장
}
어떤 변수(위 코드에서 p)에 주소를 저장하고 싶다면
- 그 변수의 자료형과
- 별 연산자(*)를 써줘야 한다.
int, float, char 처럼 포인터 또한 자료형이다.
int *p = &n; 에서 포인터(*p)가 가리키는 값(n) 이 int라는 뜻
printf("%p\n", p); // 0x어쩌고저쩌고 16진법의 주소가 출력됨
p의 값을 출력하면 암호같은 16진수 0x어쩌고 가 나온다.
printf("%i\n", *p); // n의 값을 출력해줌. 50 출력
p가 가리키고 있는 주소에 있는 값을 출력하면 n의 값인 50이 출력된다.
p 또한 변수이기 때문에 메모리의 어딘가에 위치해 있다.
p의 값 (0x어쩌고) 은 신경 쓸 필요 없고 p가 n의 주소를 가리킨다는 것이 중요하다.
n과 p를 우편함이라고 생각했을 때 n을 열어보면 50이라는 값이 저장되어 있고, p를 열어보면 n 우편함의 주소가 나오는 것이다.
정리!
&n : n의 주소 (0x어쩌구..)
*n : n의 주소로 가서 그 값을 알려줘
포인터(*)란 기본적으로 주소를 저장하는 변수이기 때문에 int a = 30; 이라는 변수 a가 있다면 int *p = &a; 이렇게 a의 주소를 나타내는 연산자 &를 붙여줘야 한다.
stirng
문자열의 끝에는 종단 문자인 \0이 존재한다.
a = EMMA 라는 게 있다면 a[4] = "\0"
s라는 포인터가 EMMA의 첫 character인 E의 주소를 가리키는데, 어떻게 끝나는 위치를 알까?
그건 바로 null terminating character (종단 문자)를 통해서 알게 된다. 중요중요!!
컴퓨터는 종단 문자가 나올 때까지 루프를 돌면서 끝을 알아낸다.
더 이상 문자열 같은 건 없다. 그러면 문자열은 어떻게 구현되어 있을까?
엄밀히 말하면 문자열이란 포인터이다. cs50 라이브러리의 도움을 받아
string s = "EMMA";
라고 적던 string은
char *s = "EMMA";
라고 적을 수 있다.
즉, 포인터 s 는 자료형이 char인 "E"의 주소를 가리키고, 컴퓨터는 그 "E"의 주소에서부터 \0 이 나올 때까지 루프를 도는 것이다.
이전에 typedef 라는 키워드를 이용해 새로운 자료형 (data type)을 만든 적이 있다. 동일한 방법으로 cs50라이브러리에는 편의를 위해 typedef를 이용해 string이라는 자료형을 만들었던 것이다.
#include <stdio.h>
int main(void)
{
char *s = "EMMA";
printf("%s\n", s); // EMMA 라고 잘 출력된다.
}