[10주차 Day4] 프론트엔드 기초 - 프로그래밍의 기본 원리 & 객체지향에 대한 이해

반 히·2024년 5월 2일

데브코스

목록 보기
22/58
post-thumbnail

💡 함수 포인터


📌 함수 포인터의 사용

  • 함수명 앞에 *만 붙여주면 함수 포인터가 선언된다

    자료형 (*함수 포인터 이름) (인자 목록)
    int (*func) (int a);

  • 함수 포인터도 포인터이므로 주소값을 저장한다.
    • 특정 함수의 주소값을 저장하는 포인터 변수.
    • 함수의 주소값만 저장하므로 함수 포인터라고 부름
#include <stdio.h>
int Plus(int a, int b) {
    return a + b;
}
int Minus(int a, int b) {
    return a - b;
}
int main()
{
    int a = 20;
    int b = 10;
    int select = 0;
    
    int (*fPtr)(int pa, int pb);
    
    while(1) {
        printf("Select : ");
        scanf("%d", &select);
        
        switch(select) {
            case 1:
                fPtr = Plus;
                break;
            case 2:
                fPtr = Minus;
                break;
            case 3:
                return 0;
            default :
                break;
        }
        int result = fPtr(a, b);
        printf("result = %d\n", result);
    }
    return 0;
}

📌 함수 포인터의 사용 이유

  • 직접 함수를 호출하면 되지 왜 복잡하게 함수 포인터를 사용하나?
    • 메모리의 크기 및 위치가 결정되는 시점은 컴파일 타임 또는 런타임 시점이다.
    • 컴파일 타임 시점에 결정이 되면 정적 바인딩, 런타임 시점에 결정이 되면 동적 바인딩 되었다고 한다.
      • 자바스크립트에서 var a = 10 → 동적 바인딩 (즉흥)
      • int a = 10 → 정적 바인딩 (계획)
  • visual studio code에서 사용되는 extension들은 플러그인 방식으로 동작한다.
    • 새로운 기능을 추가하게 되면 매번 다시 컴파일 해야 하는 불편함이 있으나 플러그인 방식을 사용하면 그럴 필요가 없다.
  • 함수 포인터의 사용은 프로그램의 유연한 확장성을 제공한다.
    • 코드의 규격을 맞춰놓고, 함수 포인터를 전달하여 동적으로 기능을 추가함.

💡 구조체, 공용체, 열거형


📌 구조체

  • 하나 이상의 서로 다른 종류의 변수들을 묶어서 새로운 데이터 타입을 정의하는 것.
  • 추상적임

🔗 구조체를 사용하는 이유

  • 연관된 변수들을 하나로 묶어서 관리함으로써 데이터 관리에 유용하다.
  • 데이터 양(변수의 개수)이 많아지면 구조체가 유리하다.
  • 예를 들어, 학생 정보 관리 시스템이라고 가정하자.
    • 이름, 나이, 성별 등의 정보들은 모두 변수로 선언.
    • 각각의 변수를 별도로 관리하면 연관성을 알 수 없다.
    • 학생 수가 10명 이내로 적으면 커버가 되지만, 100명, 200명으로 늘어나면 변수의 관리가 힘들어진다. ⇒ 구조체가 대안
    • 학생 한 명을 그룹으로 지정하여 이름, 나이, 성별 등의 정보들을 그룹으로 묶는다.

🔗 구조체 정의하기

  • 구조체의 기본 형태 (학생 정보)
struct student
{
    char name[10];
    int age;
    int height;
}

struct 키워드는 구조체라는 데이터 타입을 의미
student는 내가 만든 구조체의 이름
name, age, height는 구조체 멤버
구조체 student는 사용자인 내가 정의한 새로운 데이터 타입이다.
student a;를 통해 데이터 타입이 studenta라는 변수 선언 가능

  • 선언된 구조체 변수의 형태

🔗 구조체 멤버에 접근하기

  • 구조체 변수를 통해 구조체 멤버의 값을 참조해야 한다.

    [구조체 변수명].[구조체 멤버]
    ex) st1.name, st1.age, st1.height

  • 멤버에 접근 시 .(점 - 직접 접근 연산자)을 사용하는데, 이를 직접 접근이라고 한다.
  • 실습 예제
#include <stdio.h>
#include <string.h>

struct student {
    char name[10];
    int age;
    int height;
} st1;
int main()
{
    
    strcpy(st1.name, "한비랑");
    st1.age = 23;
    st1.height = 162;
    printf("이름 = %s, 나이 = %d, 키 = %d\n", st1.name, st1.age, st1.height);

    return 0;
}

🔗 평가 문제

  • 우리가 우체국에서 물건을 어디론가 보낼 때, 물건의 종류, 무게, 높이 등의 정보를 입력한다. 우리는 이러한 정보들을 갖고 있는 물건에 대해 구조체를 만들어보자. 구조체 이름은 object라고 하고, 구조체 멤버로는 이름(name), 높이(height), 무게(weight)를 갖는다. 구조체 변수를 하나 선언하고, 구조체 변수를 통해 각각의 멤버에 값을 대입한다. 대입하는 값은 사용자로부터 입력 받으며, 입력이 끝나면 대입한 멤버의 값을 화면에 출력하도록 프로그램을 작성하라.
    출력 예)
    물건의 이름 : 책
    물건의 높이(cm) : 30
    물건의 무게(kg) : 2
    보낼 물건의 정보 : 책, 30cm, 2kg
#include <stdio.h>
#include <string.h>
struct object {
    char name[20];
    int height;
    int weight;
};
int main()
{
    struct object obj;
    char name[20];
    int h, w;
    
    printf("물건의 이름 : ");
    scanf("%s", name);
    printf("물건의 높이(cm) : ");
    scanf("%d", &h);
    printf("물건의 무게(kg) : ");
    scanf("%d", &w);
    
    strcpy(obj.name, name);
    obj.height = h;
    obj.weight = w;
    
    printf("\n보낼 물건의 정보 : %s, %dcm, %dkg", obj.name, obj.height, obj.weight);
    
    return 0;
}

📌 공용체

  • 공용체도 사용자 정의 자료형이다.
  • 구조체와의 차이점은 메모리 공간을 공유한다는 점이다.
    • 메모리가 큰 변수의 메모리를 기준으로 해서 공용으로 사용이 됨.
union unTemp
{
    char a;
    int b;
    double c;
} un;

📌 열거형

  • 열거형은 enumeration의 약자로 enum(이넘)이라고 읽는다.
  • 데이터들을 열거한 집합이다.
    • 구조체나 공용체는 서로 다른 타입의 연관된 데이터들을 모아둠
    • 열거형은 연속된 데이터들(연속적인 데이터들)을 주로 정의할 때 사용
  • 컴파일러는 열거형 멤버들을 정수형 상수로 취급한다.
  • 키워드는 enum을 사용하여 정의한다.
enum Week
{
    sun = 0,
    mon,
    tue,
    wed,
    thu,
    fri,
    sat
};
  • 열거형의 멤버들은 각 요일을 나타낸다.
  • 첫 번째 멤버 sun을 0으로 설정하면 다음 멤버 mon은 1씩 증가한다.
#include <stdio.h>
enum Week
{
    sun = 1,
    mon,
    tue,
    wed,
    thu,
    fri,
    sat
};

int main()
{
    int day;
    
    printf("요일을 선택하세요 : ");
    scanf("%d", &day);
    
    switch(day) {
        case sun :
            printf("Sunday\n");
            break;
        case mon :
            printf("Monday\n");
            break;
        case tue :
            printf("Tuesday\n");
            break;
        case wed :
            printf("Wednesday\n");
            break;
        case thu :
            printf("Thursday\n");
            break;
        case fri :
            printf("Friday\n");
            break;
        case sat :
            printf("Saturday\n");
            break;
        default :
            printf("잘못 입력하셨습니다.\n");
            break;
    }
    return 0;
}

💡 메모리 구조


📌 메모리 영역

  • 메모리는 크게 코드영역, 스택영역, 힙영역, 데이터 영역 총 4가지로 구분
    • 코드 영역 : 실행 명령어들 저장
      • 실행할 명령어들이 순서대로 쌓인다.
      • CPU가 이 영역에서 명령어들을 하나씩 가져다 처리한다.
    • 스택 영역 : 지역 변수 및 매개 변수 저장
      • 스택이란 모든 원소들의 삽입, 삭제를 한쪽 방향에서만 수행하도록 하는 선형 자료구조이다.
      • 이를 후입 선출 방식 (LIFO)이라고 한다.
      • 나중에 들어온 녀석이 먼저 빠져나간다는 뜻이다.
      • push : 집어넣음 / pop : 맨 위의 것을 뺌.
      • 우리가 지금껏 사용한 지역 변수 및 매개 변수 등은 모두 스택 메모리를 사용
    • 힙 영역 : 프로그래머가 직접 할당 (큐 메모리라고도 말함)
      • 힙은 컴퓨터 메모리의 일부가 할당되었다가 회수되는 일들의 반복
      • 힙은 컴파일 시가 아니라 실행 시에 사용자로부터 할당 메모리를 입력 받음. (동적 메모리)
    • 데이터 할당 : 전역변수, static 변수 저장
      • 전역 변수나 static 변수가 저장되는 메모리 영역.
      • 이 메모리는 프로그램 종료 시 소멸

💡 동적 메모리 할당


📌 동적으로 메모리를 할당하는 이유

  • 일반 변수 선언 : 메모리 할당은 컴파일 타임에 이루어진다. (정적 메모리)

    int a;
    double b;

  • 전교생이 10명인 학교의 학생 수를 배열로 선언한다면?

    int student[10];

  • 학생 수가 늘어나게 되면?

    int student[100];

  • 이는 미봉책에 불과하다. 학생 수가 어떻게 변할 지 모른다.
  • 결국 학생 수는 유동적이므로 고정하지 말고 실행 시 결정하자. (런타임에 결정)
int num;
fputs("학생 수를 입력하세요 : ", stdout);
scanf("%d", &num);
int student[num];
  • 문제점
    • scanf는 런타임에 실행
    • int student[num]은 컴파일타임에 실행
    • 런타임에 입력 받은 변수를 컴파일타임에 대입하는 형태는 시점 논리에 맞지 않는다.
  • 해결 방법
    • 실행 중에 학생 수를 알아야 하는 경우, 동적 메모리 할당 기법을 통해 문제 해결이 가능하다.

📌 동적 메모리 할당 및 해제

🔗 동적 메모리 할당 함수의 원형

void* malloc(size_t size);
  • 전달인자 size는 바이트 단위로 입력한다.
  • 메모리 할당이 되면 메모리의 주소값을 리턴한다.
  • 메모리 부족 시 NULL 포인터를 리턴한다.
  • void *의 의미 : 타입이 지정되지 않는 포인터. (난 당신이 원하는 메모리 크기만큼 하당해줄테니, 메모리는 당신이 원하는 형태로 정해서 사용하세요.)
int main()
{
    int num;
    int *student;
    
    printf("학생 수를 입력하세요 : ");
    scanf("%d", &num);
    
    student = (int *)malloc(sizeof(int) * num);
    if (student == NULL) {
        printf("메모리가 할당되지 않았습니다.\n");
        return 0;
    }
    printf("할당된 메모리 크기는 %d입니다.\n", sizeof(int)*num);
    
    free(student);
    
    return 0;
}
  • 사용이 끝나면 free를 통해 메모리를 해제해주어야만 한다. 그렇지 않으면 메모리 누수로 시스템에 떠돌아다니는 메모리가 됨. (치명적인 오류 발생의 원인이 될 수 있음)
    • 그러나 자바스크립트와 같은 프레임워크 기반의 언어들은 가비지 컬렉터가 자동으로 사용하지 않는 메모리들을 해제해줌.
  • 앞으로 객체 기반에서 new 연산자를 이용한 객체 생성의 메모리 구조는 모두 위 형태를 기반한다.
  • 메모리 할당이 일어나면 반드시 해제를 해주어야 함.

📁 객체 지향에 대한 이해

💡OT


📌 왜 객체지향 철학을 이해해야 하는가

  • 자바스크립트는 객체를 사용하는 객체 기반 언어이다.
  • 객체를 생성하는 원리를 이해하면 객체를 더욱 잘 사용할 수 있다
    • 객체 지향에 대한 원리를 알게 되면 자바스크립트의 동작 원리와 타입스크립트를 이해하는데 굉장히 많은 도움이 됨.
  • 타입스크립트와 리액트 기반에서 필요하다.
    • 타입스크립트 코드는 자바스크립트 언어를 기반으로 함.
    • 리액트는 타입스크립트 기반

💡객체 지향 철학의 이해


📌 객체 지향이란

  • 객체 : 영문으로 Object, '사물'을 나타내는 추상적인 개념
  • 지향 : 영문으로 Oriented, '~를 향한다'는 의미

📌 구조적 프로그래밍과 객체 지향 프로그래밍

  • 구조적 프로그래밍 방식
    • 순차적, 하향식, 폭포수 방식이라고 한다.
    • 기능적인 기본 단위는 함수이다.
  • 객제지향 프로그래밍 방식
    • 기능 단위는 객체이다. ⇒ 유연함
    • 대표적인 예가 이벤트 기반의 모든 윈도우 프로그램이다.

📌 추상화

  • 추상 : 대상에서 특징만을 뽑아낸 것
    • 우리 일상의 사물들은 관념적이고 추상적인 것들이 많다.
  • 플라톤의 이데아(Idea)는 추상화 개념과 일맥 상통한다.

📌 캡슐화

  • 은닉하다, 숨긴다는 의미
  • 캡슐화한다는 것은 외부에서 그 내부를 볼 수 없게 한다는 의미
  • 마냥 숨기기만 한다면 데이터는 무용지물임
  • 외부로부터 데이터를 조작할 인터페이스가 필요

    클래스 = 데이터 + 메소드
    데이터는 은닉된 데이터임 (= 프로퍼티 = 필드 = 속성 = 멤버 변수)
    메소드는 클래스 내에서만 정의된 함수임. (= 멤버 함수 = 인터페이스)

0개의 댓글