본문 바로가기

전공

자료구조_자료구조와 알고리즘

자료구조와 알고리즘

자료구조란

자료의 정의 - 자료는 관찰이나 측정을 통해 발생한 값으로, 문자열, 정수, 실수 등 다양한 형태로 표현할 수 있으며, 컴퓨터에 입력 및 저장을 할 수 있어야 하고, 이를 처리하여 정보로 활용할 수 있다.

자료구조의 정의 - 자료구조는 자료가 어떠한 형태로 저장되어 있고, 이를 어떻게 사용하는지를 나타낸다. 가장 대표적인 자료구조로는 변수 배열 등이 있다.

자료구조의 종류 - 자료구조의 종류는 기본 자료구조, 파생 자료구조, 사용자 정의 자료구조가 있다.

기본 자료구조 파생 자료구조
 - 기본 자료형을 사용해 만듬
사용자 정의 자료구조
 - 개발자가 특정한 목적에 맞게 정의해서 사용하는 자료구조
선형 비선형
숫자(정수, 실수)
문자
문자열
배열
* 구조체
* 포인터
연결리스트
스택
배열

테이블의 레코드
트리
그래프

// 배열은 파생 자료구조이면서 선형 구조이다.
*  구조체 - C언어에서 사용되는 데이터 구조의 하나로, 여러 개의 변수를 하나의 자료형으로 묶어서 관리하는 데 사용된다.
*  포인터 - 메모리 주소를 저장하는 변수로, 다른 변수나 데이터 구조에 대한 참조를 가리키는 데 사용된다.

 

자료에 대한 기본 작업

초기화(initialization) - 자료를 생성할 때 특정 값으로 설정

조회(retrieval) - 자료구조 또는 데이터베이스에서 조건에 맞는 자료원소 또는 * 튜플들을 찾아 가져와서 이를 출력장치에 나타냄

순회(traversal) - 자료구조에서 모든 원소를 한 번만 방문하면서 전체 자료를 처리

탐색(searching) -자료구조에서 목표 원소 또는 탐색키를 갖고 있는 자료를 찾는 작업

삽입(insertion) - 자료구조에 새로운 원소 삽입

삭제(deletion) - 자료구조에서 기존의 원소 삭제. (기억장소가 유지된다는 점에서 소멸과 차이가 있음)

소멸(destruction) - 자료가 사용했던 기억장소를 다른 목적으로 재사용할 수 있게 점유를 해제함

정렬(sorting) - 자료구조에서 자료 원소를 순서대로 나열하는 작업

합병 / 병합(merging) - 두 개 이상의 자료 집합을 하나로 합치는 작업

*  튜플(tuple) - 데이터베이스에서 튜플은 테이블의 한 행(row)에 해당하며, 각각의 열(column)에 대한 값의 조합으로 구성된다.

*  가비지 컬렉션(Garbage Collection) - 프로그래밍 언어에서 동적으로 할당된 메모리 중에서 더 이상 사용되지 않는 객체들을 자동으로 감지하고 해제하는 메모리 관리 기법이다.

*  오름차순 정렬 - 주어진 데이터를 작은 값부터 큰 값으로 정렬하는 것을 의미한다. 

*  내림차순 정렬 - 주어진 데이터를 큰 값부터 작은 값으로 정렬하는 것을 의미한다.


알고리즘이란

알고리즘의 정의 - 알고리즘은 '어떤 문제를 해결하기 위한 확실한 방법'을 뜻한다. 즉 어떤 문제를 해결하기 위해 입력된 다양한 자료를 토대로 원하는 결과를 만들어 내기 위한 규칙의 집합이라고 정의할 수 있다.

알고리즘 충족 요건

  • 유한성 - 언젠가는 종료되어야 함
  • 명확성 - 각 단계별로 무엇을 하여야 하는지를 구체적으로 표현하여야 함
  • 입력 - 처리를 위하여 0개 이상 필요함
  • 출력 - 1개 이상의 출력을 얻기 위해 알고리즘은 존재함
  • 효과성 - 효과적인 방법으로 결과를 만들어 내야 함

알고리즘 분석 기준

구분 내용
작업 시간 수행 시간. *시간 복잡도로 정량적 평가
기억장소 사용량 주기억장치 사용량. *공간 복잡도로 정량적 평가
단순성 수행 과정의 단순성. 정성적 평가

*  알고리즘 - 어떠한 상황에 주어진 문제를 효율적으로 해결하기 위해 설계된 것을 의미한다. 알고리즘은 일상생활에서도 길 찾기, 여행 일정계획, 음악 추천, 상품 광고 등에 다양하게 활용되고 있다.

시간복잡도 - 특정 알고리즘이 어떤 문제를 해결하는 데 걸리는 시간. Big-O 표기법 사용

경우의 수 내용 표기법
최선의 경우 한 번에 찾음 Big-Ω
최악의 경우 배열의 길이만큼 걸림 Big-O
평균의 경우 배열의 길이 중간만큼 걸림 Big-θ

배열의 시간 복잡도 

종류 설명 대표적 사례
O(1) 상수형. 데이터 수에 관계 없음 해시 테이블
O(log₂ n) 로그형. 데이터 수에 따라 조금씩 증가함 이분 검색
O(n) 선형. 데이터 수에 따라 산술급수적으로 증가함 트리 운행
O(n * log₂ n) 선형 로그형. 로그형과 선형의 곱 퀵 정렬
O(n²) 평방형. 데이터 수에 따랄 기하급수적으로 증가함 삽입 정렬
O(n³) 입방형. 평방형보다 더 기하급수적으로 증가함 3차원 처리
O(2ⁿ) 지수형. 입방형보다 더 기하급수적으로 증가함 하노이 타워
O(n!) 계승형. 지수형보다 더 기하급수적으로 증가함 순열

// 수행 시간이 짧은 시간 복잡도

 -> O(1) < O(log₂ n) < O(n) < O(n * log₂ n) < O(n²) < O(n³) < O(2ⁿ) < O(n!)

 

알고리즘에 따른 시간 복잡도의 예

공간 복잡도 - 입력 크기에 대해 어떠한 알고리즘이 실행되는 데 필요한 메모리 공간의 양. Big-O 표기법 사용
 // 1차원 배열 = O(n), 2차원 배열 = O(n²)
 // n개의 int(4byte)형의 데이터 = n * 4byte = O(n)

 

알고리즘 표현 방법

서술적 표현 - 사람이 사용하는 자연어로 표현

순서도 표현 - 약속된 기호로 작업 과정을 나타내는 순서도로 표현
 // 순서도 기호(교과서 22p), 순서도 예시(교과서 23p - 유클리드 호제법으로 최대공약수 구하기)

  • 직선형 - 처리과정이 순차적으로 나열되는 순차 구조
     // 세 값을 입력받아 차례로 누적하여 출력하기
  • 분기형 - 조건에 따라 처리를 달리하는 선택 구조
  • 반복형 - 조건을 만족하는 경우에 처리 작업을 반복하는 반복 구조

의사코드 표현 - 일반적인 언어로 코드를 흉내 내어 표현

의사코드를 사용하는 이유
 - 전체 흐름을 사전에 파악하고, 논리적인 오류를 초기에 발견할 수 있다.

 - 나중에 프로그램 코드를 읽고 디버깅하거나 수정해야 하는 개발자에게 도움이 된다.

 - 컴퓨터 프로그램 알고리즘이 어떻게 실행되어야 할지, 또는 어떻게 실행될 수 있는지 보여준다.

 - 코드 입력, 테스트, 디버그 단계에서 수정하는 것보다 의사코드 설계 단계에서 미리 작업하는 것이 훨씬 경제적이다.

 - 프로그래믜 문제를 해결하기 위한 도구로, 또는 다른 사람과 프로그램의 흐름에 대해 소통하는 방법으로 활용한다.

  • 순차적 명령문
    1. 주문 목록 확인()
    2. 결제 내역 확인()
    3. 영수증 발행()
    4. 상품 포장()
    5. 상품 발송()
  • 조건문(선택구조) - if, then, else, endif 등
    if 현재시각 < 12시 then
       현재시각 앞에 AM을 붙인다.
    else
       현재시각 앞에 PM을 붙인다.
    endif
  • 반복문
    • for문 - 'for i = 숫자 1 to 숫자 2 do'명령의 형식으로 작성한다.
      for i = 1 to 100 do
         sum = sum + i
      endfor
    •  while문 - while로 시작, endwhile로 종료, 조건이 만족될 때만 반복문이 실행된다.
  • 의사코드 예제
     - 로그인 처리 과정
     "사용자로부터 필요한 정보(아이디, 비밀번호)를 입력받는다.
      데이터베이스에서 필요한 내용을 가져온다.
          아이디가 일치하는지 확인한다.
          비밀번호가 일치하는지 확인한다.
      비교 결과에 따라 로그인 처리를 하여 해당 페이지로 이동한다."
     - 의사코드 수정하기
     장점 - 수정하거나 추가해야 하는 부분을 나중에 채울 수 있다.

컴퓨팅 언어를 사용한 코딩 - 프로그래밍 언어로 직접적으로 표현
 - 알고리즘 자체가 코드가 됨 = 알고리즘을 구체화할 필요 x
 - 특정 프로그래밍 언어로 작성 -> 해당 언어 알지 못하면 이해 어려움. / 보편성 떨어짐.

 

알고리즘 작성에 사용되는 제어 구조

순차 구조 - 시작부터 끝까지 차례대로 작업이 진행되면서 명령 실행

선택 구조 - 조건에 따라 실행 흐름이 달라지는 것

반복 구조 - 작업이 진행되다가 어느 시점에서 반복하는 부분이 포함된 것

재귀 구조 - 함수 내부에서 함수가 자기 자신을 또다시 호출하는 구조

점프 구조 - 프로그램의 흐름을 도중에 건너뛰는 것

 

알고리즘 작성의 예

유클리드 호제법 / 유클리드 알고리즘 - 2개의 자연수 또는 방정식의 최대공약수를 구하는 알고리즘의 하나이다.
 1. 임의의 두 자연수 num1, num2가 주어졌을 때, 둘 중 큰 값을 num1으로 지정

 2. num1을 num2로 나누고 그 나머지를 n이라고 할 때, (num1% num2 = n)

 3. n이 0일 때, num2가 최대 공약수이다.

 4. 만약 n이 0이 아니라면, num1에 num2값을, num2에 n값을 다시 대입한 후 2. 의 과정을 반복한다.

#includ <stdio.h>
int euclidean(int num1, int num2){
	while(1){
	int temp = num2;
        num2 = num1 % num2;
        num1 = temp;
        if(num2 == 0) break;
	}
    return num1;
}
void main(){
	int answer = euclidean(78, 66);
    printf("%d \n", answer);	// 최대공약수 6 출력
}

 

매직카드

 - 0~31 중 숫자 하나를 마음속으로 정하고 다음 카드를 보며 순서대로 '있다', '없다'를 답한다.

 - 순서대로 +1, +2, +4, +8, +16을 있다고 답했을 때 더한다. -> 마음속으로 정한 0~31 중 숫자 하나가 나온다.

첫 번째 두 번째 세 번째 네 번째 다섯 번째
1 3 5 7 2 3 6 7 4 5 6 7 8 9 10 11 16 17 18 19
9 11 13 15 10 11 14 15 12 13 14 15 12 13 14 15 20 21 22 23
17 19 21 23 18 19 22 23 20 21 22 23 24 25 26 27 24 25 26 27
25 27 29 31 26 27 30 31 28 29 30 31 28 29 30 31 28 29 30 31

하노이 타워 - 재귀함수를 배울 때 나오는 대표적인 알고리즘 중의 하나로, 재귀함수의 하향식 계산 방식의 강력함을 볼 수 있다.
 P(A) = B   // A : 원판의 수, B : 이동 횟수
 // P(1) = 1

 // P(2) = 1+1+1 = 3

 // P(3) = 3+1+3 = 7

-> P(n) = P(n-1) + 1 + P(n-1) = 2P(n-1)+1 = 2ⁿ-1

   => 시간복잡도 = O(2ⁿ)

#include <stdio.h>
// a는 출발지, b는 경유지, c는 도착지 변수
void HanoiTower(int n, char a, char b, char c){
    if(n==1) printf("원판 %d, %c -> %c\n", n, a, c);
    else{
    	HanoiTower(n-1, a, c, b);	// (n-1)개를 출발지 -> 경유지
        printf("원판 %d, %c -> %c\n", n, a, c);	// n번 원판 이동
        HanoiTower(n-1, b, a, c);	// (n-1)개를 경유지 -> 도착지
    }
}
int main(void){
    int n = 4;	// 원판의 개수
    HanoiTower(n, 'A', 'B', 'C');
    return 0;
}

배열이란

배열 - 배열은 같은 타입(type)의 자료를 연속된 공간에 저장하기 위해 사용하는 선형 구조이다.

 // 배열 삽입 - 삽입 위치 뒤로 전부 한 칸씩 이동, 마지막 데이터 있으면 삭제됨.
 // 배열 삭제 - 삭제 데이터 뒤에 모든 값 앞으로 한 칸씩 이동

배열의 장점 배열의 단점
데이터의 접근이 빠르다. (직접 접근)
읽기, 쓰기 같은 참조에는 성능이 O(1)이다.
기억 장소를 효율적으로 사용할 수 있다.
(데이터 저장을 위한 추가 공간 필요 없음)
크기 예측이 힘들기 때문에 메모리 낭비가 발생할 수 있다. (실행속도 감소)
특정 위치에 데이터 삽입/삭제 시 자료이 이동이 많다.
(배열 데이터가 연속된 메모리 공간에 배치되어 있으므로, 중간에 데이터를 삽입하거나 삭제할 때의 성능은 좋지 않다.)
배열 크기를 충분한 크기로 준비할 때, 사용하지 않는 공간은 낭비를 초래한다.
#include <stdio.h>
int main(void){
	int i, j, b[5][3];
    int a[3][5] = {{5, 41, 24, 6, 10}, {50, 33, 20, 15, 11}, {7, 17, 35, 21, 16}};
    
    for(i=0; i<3; i++)
    	for(j=0; j<5; j++)
        	b[j][i] = a[i][j];
    return 0;
}

 

 

구조체란

구조체의 정의 - 배열보다 발전된 개념으로, 서로 다른 타입의 데이터도 하나로 묶어서 사용할 수 있다.

구조체의 선언 -구조체를 사용하려면 먼저 정의를 해야 한다. struct 명령어와 함께 구조체의 이름을 쓰고, {} 안에 하나의 구조체로 묶으려는 변수나 배열의 타입과 이름을 하나 이상 나열한다. 

// 구조체 정의와 선언을 따로
struct book{
	char title[30];
	char writer[20];
	int value;
	char publisher[30];
};
struct book book1, book2, book3;

// 구조체 정의와 선언 동시에
struct book{
	char title[30];
	char writer[20];
	int value;
	char publisher[30];
} book1, book2, book3;

구조체의 초기값 설정

 - 하나의 구조체 변수에 초기값 설정

struct book book1  = {"유몰로 읽는 역사", "이덕일 외", 15000, "세종서적"}

// book1의 데이터 타입 = struct book
// book1.title = "유물로 읽는 역사"
// book1.writer = "이덕일 외"
// book1.value = 15000
// book1.publisher = "세종서적"

 

 - 구조체 배열에 초기값 설정

struct book books[] = {
	{"김병호의 문화체험", "김병호", 10000, "푸른숲"},
    {"인류의 기원", "아서 니호프", 12000, "푸른숲"},
    {"0교시 문학시간", "이낭희", 14000, "나라말"}
};

// books[] = 배열로 선언 -> books[]배열 내애서 struct book 타입의 데이터 가질 수 있다.
// 배열의 인덱스는 0부터 시작

// books[1].title = "인류의 기원"
// books[1].value = 12000

// books[2].title = "0교시 문학시간"
// books[2].value = 14000

중첩 구조체 - 구조체의 멤버로 또다시 구조체를 내포하고 있는 구조체

 // 구조체의 멤버(member) / 필드(field) = 구조체 안에 선언된 변수나 배열

struct personal_info{
	char name[20];
    char address[50];
    char tel_no[16];
    char e_meil[50];
};

struct book{
	char title[30];
    struct personal_info writer;
    int value;
    char publisher[30];
} b1;
b1 title
writer name
address
tel_no
e_mail
value
publisher

b1.title = "0교시 문학시간";

b1.writer.name = "이낭희";

b1.writer.address = "관악구 호암로 546";

b1.writer.tel_no = "872-4072";

b1.writer.e_mail = "mirim1@e-mirim.hs.kr";

b1.value = 14000;

b1.publisher = "나라말";

구조체의 패딩(Padding) 현상 - 구조체의 멤버 사이에 빈 공간 생길 수 있다.
// 구조체는 구성원 중 가장 큰 데이터 타입을 기준으로 크기 정해진다.

 - 메모리를 효율적으로 접근하기 위해 사용한다.

 - 구조체를 정의할 때 패딩 현상을 고려해 멤버 변수의 순서와 데이터 유형을 조정한다.


포인터

포인터의 정의 - 실제 변수의 값이 저장되어 있는 메모리의 주솟값을 저장하는 변수로, 포인터 변수라고도 한다. 데이터를 간접 소유하게 되므로 한 번 이상의 접근으로 데이터를 조작할 수 있다.

int k = 23;
int *ptr = &k;

단순 포인터 - 단순 포인터는 포인터 변수에 데이터의 주소가 주어지고, 한 번의 접근으로 데이터를 조작할 수 있다. 포인터라고 하면 보통은 단순 포인터를 의미한다.

int k = 23;
int* ptr = &k;

printf("k = %d\n", k);	// k = 23
printf("&k = %p\n", &k);	// k = k의 주소
printf("ptr = %p\n", k);	// ptr = k의 주소
printf("*ptr = %d\n", *ptr);	// *ptr = 23

printf("&ptr = %p\n", &ptr);	// &ptr = ptr의 주소

이중 포인터 - 이중 포인터 변수는 단순 포인터 변수의 주소를 값으로 갖고, 두 번의 접근으로 실제 데이터에 접근할 수 있다.

int k = 23;
int *ptr = &k;	// 단순 포인터
int **pptr = &ptr;	// 이중 포인터

 

배열과 포인터 - 배열명은 배열의 시작 주소를 갖고 있으므로 일종의 포인터라고 할 수 있다.

*  배열명 - 배열의 시작 주소를 갖고 있으므로 포인터는 맞지만, 주소값을 변경할 수 없으므로 포인터 변수는 아니다.

int arr[5] = {5, 10, 15, 20, 25};
int* ptr = &arr[0];
printf("arr[0] = %d\n", arr[0]);	// arr의 0번째 데이터 5 츨력
printf("*ptr = %d\n", *ptr);	// ptr에 있는 arr의 0번째 주소의 값 5 출력

printf("arr[0] = %p\n", &arr[0]);	// arr[0] 주소 출력
printf("ptr = %p\n", ptr);	// ptr이 가진 arr[0]의 주소 출력

printf("arr[1] = %d\n", arr[1]);	// 10
printf("*(ptr+1) = %d\n", *(ptr+1));	// 10

printf("arr[2] = %d\n", arr[2]);	// 15
printf("*(ptr+2) = %d\n", *(ptr+2));	// 15
#include <stdio.h>
int main(void){
	int i, k=5, *p1=&k, **p2=&p1;
    int a[5] = {5, 10, 15, 20, 25};
    *(a+3)=**p2+100;	// a[3] = 5+100;
    p1 = 1;	// p1 = a[0];
    p1 += 2;	// p1 = a[2];
    **p2=30;	// a[2] = 30;
    for(i=0; i<5; i++) printf("%d	", a[i]);
    	// 5   10   30   105   25
    printf("\n");
    return 0;
}

구조체 포인터 - 구조체 변수 역시 포인터로 다룰 수 있는데, 포인터 변수가 구조체의 주소를 갖는다.

struct book book1 = {"0교시 문학시간", "이낭희", 14000, "나라말"};
structbook *ptr = &book1;

// 포인터로 구조체 배열 접근할 때 * 과 . 연산자 사용
// book1.value = 14000
// = (*ptr).value = 14000
// = ptr -> value = 14000
// 연결리스트 구현
#include<stdio.h>
void main(void){
	struct node{
    	char data;
        struct node* link;
    } a, b, c, *p;
    p = &a;
    
    a.data = 'A';
    a.link = &b;
    
    b.data = 'B';
    b.link = &c;
    
    c.data = 'C';
    c.link = NULL;
}

 

정적 할당과 동적 할당

메모리 영역(C프로그램) - 프로그램을 실행하면 운영체제는 실행에 필요한 메모리 공간을 할당한다. 메모리 공간 간은 프로그램을 실행할 때마다 운영체제에 의해 주기억 장치 RAM의 빈 공간에 할당된다. 

정적 할당 영역으로 컴파일할 때 점유할 메모리 크기가 결정됨.
실행코드, 전역변수, 지역변수
CODE
 - 함수 실행 코드
DATA
 - 초기화된 전역변수, static 변수 할당
 - 프로그램 시작과 동시에 할당 되고 프로그램이 종료되면 메모리에서 소멸
BSS(Blocked STated Symbol)
 - 초기화가 되지 않은 전역변수
STACK
 - 지역변수
 -> 함수 호출하면 점유 ( 지역변수, 매개변수 저장 )
 -> 함수 종료되면 소멸 ( 지역변수와 매개변수 소멸) 
- 연속 공간 사용
 -> 설정 크기 이상으로 요구되면 프로그램 실행 중단
실행 중에 함수를 사용하여 메모리를 할당 또는 해제하며, 기본 제공 크기 이상의 할당이 가능함. HEAP
 - 동적 할당 ( 필요할 때마다 동적으로 메모리를 할당하여 사용가능 )
 -> 할당함수 - malloc(), callock()
 -> 소멸 함수 -  free()
 - 불연속 공간 사용
 -> 설정 크기 이상으로 요구하여도 다른 사용할 수 있는 공간을 확보하여 할당 

*  정적 할당 - 프로그램 실행 전에 컴파일러에 의해 메모리 크기가 결정되는 방식

*  동적 할당 - 프로그램 실행 중에 필요한 만큼의 메모리를 동적으로 할당하는 방식

정적 메모리 할당

 - 프로그램에서 선언된 지역변수의 메모리 할당에 해당하며, 실행할 때 그 크기가 정해져 있고, STACK영역을 사용한다.

 - 사용할 메모리의 크기와 할당부터 해제까지의 저장 기간이 고정적이다.

 - 프로그램의 시작부터 종료까지 오랫동안 유지되므로 사용을 최소화하는 것이 좋다.

 // 만약 메모리가 stack영역의 기본 크기보다 크다면 stack영역의 기본 크기를 필요한 만큼 늘린다.

 // 만약 메모리가 너무 크고, 컴파일 시까지 그 크기를 결정하기 힘든 경우 동적으로 할당한다.

#include <stdio.h>
#define SIZE 1000000	// 1MB
// Visual Studio에서 STACK, HEAP영역의 기본 크기 = 1MB
int main(void){
	int a[SIZE] = { 10, };	// 배열 a는 4MB 크기를 요구함.
    // stack 영역에 할당된 크기보다 더 많은 크기를 요구 -> 프로그램이 정상적으로 실행되지 않음.
    // 해결 방법 = stack 영역을 늘리거나 동적할당으로 처리
    printf("%d\n", a[0]);
    return 0;
}

*  정적으로 할당된 변수 - 프로그램이 실행되는 동안 메모리에 상주하므로 빠른 액세스와 효율적인 사용이 가능하다. 하지만 변수의 크기가 컴파일 시점에 결정되므로, 실행 중에 크기를 동적으로 조정할 수 없는 제약이 있다.

동적 메모리 할당

 - 프로그램 실행 중에 필요한 만큼의 메모리를 HEAP영역에 할당하고 원하는 기간에 사용할 수 있으므로 메모리를 가장 효율적으로 사용하는 방법이다. 

 - stack영역에서 공간이 부족하면 프로그램 실행이 중지되지만, heap영역에서는 메모리 내 다른 곳에서 공간을 확보해 할당한다.

 - 메모리를 요청하고 할당받는 과정에서 시간이 지연되는 성능 저하 현상이 발생한다.

 - 개발자가 직접 메모리를 할당(malloc() 호출)하고, 해제(free() 호출) 해야 하므로 * 메모리 누수현상이 발생할 수 있다.

 // 일시적으로 메모리가 필요하거나 큰 기억공간이 필요한 경우 동적 메모리 할당이 효율적이다.

 // heap영역은 연속된 공간을 사용하지 않아도 되기 때문에 추가적으로 필요한 공간만큼 다른 장소에 메모리를 확보해 프로그램이 계속 실행되도록 한다.

#include <stdio.h>
#include <stdlib.h>
#define SIZE 1000000

int main(void){
	int* a = (int*)mallock(sizeof(int) * SIZE);
    a[0] = 10;
    printf("%d\n", a[0]);
    free(a);	// 동적 할당은 사용 후, 항상 할당 해제(소멸)해야 함.
    return 0;
}

*  동적으로 할당된 메모리 - 적절하게 관리하지 않으면 메모리 누수(Memory leak)와 같은 문제가 발생할 수 있다. 따라서 동적으로 할당된 메모리는 반드시 해제해야 한다.

*  메모리 누수 - 동적으로 할당된 메모리가 프로그램에서 더 이상 필요하지 않은데도 해제되지 않고 계속 유지되고 있는 상태를 말한다.

'전공' 카테고리의 다른 글

Java_컬렉션 제네릭  (7) 2024.10.08
파이썬_변수, 자료형 / 제어문, 반복문 / 함수  (2) 2024.07.10
자료구조_비선형 구조  (1) 2024.07.09
자료구조_선형 구조  (4) 2024.07.09
java  (0) 2023.07.06