7 minute read

4주차 회고

알고리즘 주차가 끝났다.. 하지만 알고리즘 문제를 하루에 한 문제는 풀것이다. 일단은 점심시간에 밥을 먹고 온 후에 30분 정도 문제를 풀 예정이다. 하루에 한문제씩 풀어서 뇌운동해야지.

이번주엔 c언어를 토대로 RB Tree를 구현하는 것이 최종 목표였다.

우선 C언어 공부 → 이진트리 구현 →RB Tree 구현 순서대로 진행했다. 그래서 6일의 시간동안

리눅스 환경설정 하루(및 알고리즘 주차 무사종료에 따른 축하파티), C언어, RB tree 개념공부 하루, C언어로 이진트리 구현 2일, 실제 구현 2일, CSAPP 책 및 C언어 퀴즈 준비 하루…의 스케줄로 소화했다. 하루 전에 이번주 목표를 소화하는 것을 목표로 했고, 목표가 있으니 목표를 지키기 위해 더 열심히 했다. 다음주도 일정을 조금 빡빡하게 잡아서 마지막 날에는 개념 정리하고 블로그를 정리하는 시간을 가지면 좋겠다.

우선 C언어 공부. 두 달전 42서울을 진행하면서 기초 개념은 공부했었다.

크게 3가지 개념을 추가로 공부했었다. 포인터, 이중포인터, 구조체

포인터

포인터는 메모리 공간의 주소를 가리키는 변수이다. 나는 한칸짜리 방이라고 이해했다. 메모리에 저장되어있는 포인터 변수를 똑똑 방문했더니 그곳에 값은 없고 ‘0X00016 으로 가시오.’ 라는 메모만 있는것이다! 그리고 그 주소를 따라 또 다른 변수를 방문하면 그곳엔 값이 있다. 방문했을때 주소값이 있는 변수를 포인터라고 부르는것. 그래서 주소를 가리킨다라는 표현을 쓰는것이다.

포인터가 아닌 변수들은 방문했을때 주소 대신에 값이 있을 것이다.

포인터는 말그대로 메모리에 주소값이 저장된다. 그런데 왜 포인터 앞에 자료형을 붙일까?

int a = 10;
int *ptr = &a;

포인터변수도 어쨋든 변수이다. 그런데 메모리에 저장된 변수를 어디서부터 어디까지 읽어야 주소값을 읽어올 수 있을까? 포인터변수 자체에는 자료값이 없다. 때문에 포인터주소로부터 몇바이트를 읽어야 올바른 주소값을 받아올지 알아야 하기때문에 포인터에 자료형을 명기해야 한다.

linked list

링크드리스트는 값이 작성되어있는 부분과 next 부분으로 이루어져있는 node들로 구성되어 있다.

typedef struct _node{
	int data;
	struct _node *next;
}Node;

만약 값이 한 개 밖에 없다면 첫번째 값에는 1이 들어가고 next는 NULL 이 될것이다.

다음값이 생겼을 때에는, 첫번째 노드의 next 포인터는 다음값을 가리키게 된다.(=다음 값의 주소값이 저장된다)

스크린샷 2023-09-01 21.45.17.png

그 다음에 또 새로운 값이 저장되는 위치는 알 수 없다. 하지만 2번 노드의 next가 가리키게 될것이다. 그래서 요소를 하나하나 확인하며 순회가 가능하다 🪁

이중포인터

포인터의 주소값을 담고있는 포인터를 가리키고 있는 포인터이다.

 int val = 0;
 int *p = &val;
 int **pp = &p; // **를 통해 pp안에 저장된 p 안에 저장되어있는 메모리 주소의 데이터 변경 가능

이중포인터를 함수의 매개변수로 사용한다면, 다른 함수에 있는 포인터 변수에 저장된 메모리 주소를 제어할 수 있다.

void swap(int **pp){
	int a
	int *p
	p = &a
	print("함수 안의 a의 주소 : %d\n", &a);
	*pp = &a #2 포인터로 p 포인터 안에 저장된 메모리값 수정
}

int main(){
	int val = 10;
	int * p ;

	p = &val;
	prinf("val의 메모리주소: %p", &val)
	print("함수 실행 전 p안에 저장된 메모리 주소: %p", p)
	swap(&p)
	print("함수 실행 후 p안에 저장된 메모리 주소 : %p", p)

스크린샷 2023-09-01 22.18.34.png

구조체

하나 이상의 변수를 묶어서 편리하게 사용할 수 있도록 도와주는 도구 ex) 전화번호부같이 이름, 전화번호 등 다른 타입을 여러번 사용해야 하는 프로그램을 만들때 사용함

객체가 아니라 상속은 안됨

보통 main 함수 전에 선언해서 사용함.

struct 구조체이름 { 구조체 멤버들 };
//struct 구조체 이름 까지가 int 와 같은 자료형임.

struct student goorm
//goorm 의 자료형은 struct student

구조체를 사용할때는 구조체이름.구조체멤버이름 으로 사용하면 된다.

할당할때는 순서대로 값이 들어가고, 값을 따로 넣어주지 않을때는 0으로 초기화 된다.

struct student goorm = { .age = 20, .phone_number = "010-1234-1234", 10}
struct student codigm = {22, "010-5678-5678"}

typedef를 이용한 구조체 선언

typedef struct _Student { // 일반적으로 별칭을 쓰면 구조체 이름 앞에 언더바를 붙임
// 구조체 이름은 생략 가능. = 익명구조 체
	int age;
	char phone_number[14];
} Student //이제 Student 로 구조체를 사용할 수 있음!!!!

int main(){
	Student goorm;

구조체 배열

일반적인 배열과 선언이 비슷함.

Student goorm[3] = { {.name = "해리 포터"}, {.name = "헤르미온느 그레인저"}, {.name = "론 위즐리"} };

구조체 포인터

구조체를 가리키는 포인터.

#include <stdio.h>

typedef struct {
	int s_id;
	int age;
} Student;

int main(){
	Student goorm;
	Student *ptr;

	ptr = &goorm;

	(*ptr).s_id = 1004;
	(*ptr).age = 20;

	printf("goorm의 학번 : %d, 나이: %d\n", goorm.s_id, goorm.age);
}

구조체 포인터를 사용하기 위해서는 항상 괄호를 사용해야 함. 왜그러냐면 괄호를 사용하지 않고

*ptr.s_id로 사용하게 되면 포인터 연산자(*) 보다 구조체 연산자(.) 이 더 우선순위가 높기 때문에 원하는 값이 나오지 않는다. 그래서 항상 괄호를 해야하는데 줄여서 ptr->age 로 사용한다.

포인터는 배열처럼 사용할 수 있다.

아래 연산은 같은 값을 출력한다.

*(arr+i) = arr[i]

그리고 포인터는 상수화가 가능하다.

inc *const ptr 로 사용하면 주소값을 변경할 수 없다.

중첩구조체

구조체 안에 구조체를 선언할 수 있다.

typedef struct {
	char name[15];
	int age;
	struct Student *ptr;
} Student;

구조체를 다른 함수에 넘길 떄, 명시적으로 주로 포인터를 넘긴다고 한다. (값을 변경하지 않는 경우에도)

malloc, free

메모리 동적 할당을 위해 사용하는 함수 = 컴퓨터 프로그램이 실행되는 도중인 런타임 중 사용할 메모리 공간을 할당하는것.

힙영역에 할당된다. 힙영역은 컴파일 타임에 메모리 크기를 결정하는 데이터 영역임. (↔ 스택영역의 정적메모리와는 대조적 개념)

malloc은 런타임 당시에만 성공/실패 여부를 확인 할 수 있으므로 사용 후 반드시 NULL 여부를 확인 한 후 사용해야 한다.

Updated:

Leave a comment