부스트코스에서 진행한 '모두를 위한 컴퓨터 과학(CS50 2019)'을 수강한 뒤 작성한 내용입니다.
몰랐던 내용을 위주로 정리했기 때문에 빠진 부분이 많을 수 있습니다.
1. 메모리 주소
16진수는 알파벳을 이용하여 한자리로 15까지 셀 수 있다.
16의 거듭제곱을 사용한다.
RGB 값을 표현할 때 사용한다.
진수 간의 모호함을 없애기 위해 16진수 앞에는 '0x'를 붙인다.
C는 변수의 메모리 위치를 확인할 수 있다. &이라는 주소 연산자를 사용한다. %P(가져온다는 의미).
메모리 주소는 16진수이다.
포인터 : 컴퓨터 메모리의 주소를 가리키는 것.
*는 그 주소로 가달라는 의미이다.
'*&n'의 의미는 주소를 받아서 다시 그 주소로 가서 n에 저장된 값을 볼 수 있다는 것이다.
C에서 자료형을 알려주는 기능은 없다.
2. 포인터
어떤 변수에 주소를 저장하고 싶다면 변수 앞에 *을 붙인다.
그 앞에 int는 *(포인터)가 가리키는 값의 type이다.
최근 컴퓨터는 보안상 문제로 메모리를 여기저기로 바꾼다.
주소를 저장하려고 할 때, *를 입력하지 않으면 에러가 난다. int p = &n, 주소는 반드시 포인터에 저장한다.
포인터는 추상화를 위해 사용된다.
포인터 변수는 다른 변수를 가리킨다. 아주 정교한 자료형을 만들 수 있다. 가계도, 배열 등.
최신 컴퓨터는 64bits 포인터로 사용한다. long 타입과 같은 크기이다.
3. 문자열
문자열을 저장하는 변수 s(예시)는 포인터로 메모리에 있는 저장된 문자열을 가리킨다. 첫글자가 있는 위치를 저장한다.
문자열의 첫번째 글자를 가리키면 널 종단 문자를 마주칠 때까지 루프를 돌면서 끝을 알아낸다.
C에는 문자열이라는 자료형이 존재하지 않는다.
포인터로 문자를 가리키는 주소를 저장한다. 문자열을 문자 배열의 첫번째 바이트 주소이다.
마지막 바이트에는 0을 저장하여 끝을 알려준다.
String은 char *와 동일하다(추상화).
문자의 나열을 결국에는 주소 하나로 나타낼 수 있다는 사실을 단순화 시킨 것이다. 문자열은 여러 문자의 묶음을 추상화한 것이다.
4. 문자열 비교
*(변수명+1)을 하면 문자열의 두번째 문차가 출력된다. 컴퓨터 내부에서 '변수명[1]과 같은 문법을 위처럼 바꿔주고 실행한다.
문자열 비교가 ==으로 안되는 이유는 값이 아닌 주소로 비교하기 때문에 같은 값을 입력해도 다르게 인식한다.
5. 문자열 복사
문자열 복사는 임시 변수나 다른 무언가처럼 =으로 되지 않는다. 그렇게 하면 주소가 복사된다.
서로 다른 메모리 공간에 문자열을 복사하려면 메모리를 추가로 사용해서 복사하려는 문자열과 동일한 크기의 변수를 만들고 복사하려는 문자열의 글자를 하나씩 복사한다.
malloc : 메모리 할당 함수, 인자로 받는 것은 할당받을 메모리의 크기이다. 할당된 메모리 공간의 시작 주소를 반환한다.
동일한 질문을 반복해서 물으면 나쁜 설계이다.
malloc은 요청한 메모리 크기를 할당할 뿐, 위치는 중요하지 않다.
쓰레기 값 : 초기화하지 않은 변수의 값
6. 메모리 할당과 해제
메모리 할당이란 메모리 일부분을 가져와서 그곳을 가리키는 포인터를 주는 것이다.
malloc의 반대(해제)는 free이다. 즉, 메모리 반환.
메모리 할당 후 반환을 해줘야 더 많은 메모리를 사용할 수 있다. 그렇지 않으면 느려진다. 그래서 사용하지 않는 메모리는 해제하는 것이 좋다.
이러한 실수를 찾기 위한 디버깅 도구는 valgrind를 사용한다.
sizeof를 쓰고 괄호 사이에 자료형을 쓰면 크기를 알려준다. 이는 동적으로 알아내는 방법이다.
버퍼 오버플로우 : 지정된 메모리, 메모리 배열의 공간을 넘어 접근한다면 발생한다. 버퍼는 배열이다.
프로그램에서 '쓰기'란 값을 바꾼다는 의미이다.
7. 메모리 교환, 스택, 힙
함수는 인자를 받을 때 변수 자체가 아니라 변수의 복사본을 전달 받는다.
메모리는 조직적인 방법으로 사용한다.
메모리의 맨 위에는 clang이 컴파일한 0과 1의 값(머신코드)가 들어간다.(./이름, 아이콘 더블클릭)
그 아래에는 전역 변수가 놓인다.
그 아래에는 힙(메모리를 할당 받을 수 있는 커다란 영역)이 놓이는데, malloc을 호출하면 메모리를 이 영역에서 가져온다.
힙은 아래로 자라기 때문에 메모리를 더 사용할수록 점점 더 아래로 내려간다.
힙 아래에는 스택(프로그램에서 어떤 함수를 호출할 때마다 함수의 지역변수들은 스택이라는 메모리 제일 아래 영역에 놓인다).이 놓이는데, 기본 함수 main에서 한 개 이상의 인자와 지역변수가 있다면 변수들은 스택에 놓인다.
다른 함수를 호출하면 그 위에 있는 메모리를 사용한다.
main 함수를 호출하면 메모리 맨 아래에 C의 기본 작동 방식으로 스택 프레임이라는 공간이 주어진다. 이곳은 지역 변수를 저장하는 공간이다.
memory | clang | |
global | ||
heap | 여기서 값 교환이 완료되면 삭제된다. 이 프로그램을 위해 더이상 사용되지 않는다. | |
stack |
main에게 x와 y의 주소를 알려줘서 값을 교환하는 함수가 그 주소로 가서 값을 바꾸게 한다.
int *a의 의미는 정수의 주소를 받아 a라고 부르는 것이다.
8. 파일 쓰기
스택 오버플로우 : 시작점 없이 자기 자신을 계속 호출
힙 오버플로우 : malloc을 계속 호출해서 너무 많은 메모리를 할당해 메모리 속 다른 내용을 덮어쓴다.
버퍼 오버플로우 : 컴퓨터가 너무 많은 메모리를 쓰면 정지하거나 아예 동작하지 않는다.
scanf는 사용자의 입력을 저장하고 싶은 변수의 주소를 적는다.
지정한 타입 외의 것을 입력하면 프로그램이 죽거나 예상하지 못한 방식으로 동작한다. scanf에는 에러 확인 기능이 없다.
포인터 변수는 그 자체가 주소로 정의된다.
null은 특별한 포인터로 가리키는 곳이 없다는 뜻이다.
배열 : 메모리가 연속적으로 할당된 공간.
문자열 : 문자가 연속적으로 있는 것, 그 메모리 공간의 첫번째 주소를 의미한다.
이러한 문맥에서 포인터는 배열과 같다.
clang 컴파일러는 문자 배열의 이름을 포인터처럼 다룬다. scanf의 상황에서는 배열 첫 바이트 주소를 넘겨준다.
fopen은 해당 파일을 가리키는 포인터를 반환한다.
fprintf : 파일용 printf로 파일에 출력할 수 있다.
csv : 쉼표로 분리된 값
9. 파일 읽기
fopen, malloc, get, string과 같은 함수는 에러가 생기면 null을 돌려준다.(반환)
모든 JPEG 파일의 첫 세바이트는 무조건 FF, D8, FF로 시작한다.
C에서는 16진수를 그대로 적을 수 있다.
변수 타입 앞에 붙는 unsigned char는 -128부터 127(char)이 아닌 0부터 255 범위의 값을 의미한다.
포인터로 파일에 적을 뿐 아니라 읽을 수도 있다.