[데브코스] WIL 10

devlog·2024년 5월 7일
0

풀뎁코

목록 보기
10/14
post-thumbnail

☑️ 타입스크립트

📍 Sprint 3 FE Typescript
인터프리터 언어인 자바스크립트와는 달리 타입스크립트는 컴파일 언어이다. 이러한 이유로 타입스크립트를 학습 전 C언어로 중요 개념을 학습하는 한 주!

프로그래밍 개념

  • 설계 → 원시코드 작성 → 컴파일 → 링크
    • 최근데는 컴파일과 링크를 빌드 하나로 합쳐서 실행

컴파일 기반 언어의 엄격한 문법

  • 문장 끝 세미 콜론
  • 컴파일러는 세미콜론 단위로 문장을 해석하기 때문
    • js는 컴파일 언어가 아닌 인터프리터

✅ 변수와 자료형

변수

  • 변경이 가능한 수, 어떤 값이 들어갈 지 예측할 수 없음
  • 수많은 데이터의 주소 값을 모두 기억할 수 없기 때문에 사용

메모리 영역

  • 코드 영역 → 실행 명령어 저장
  • 스택 영역 → 지역 변수 및 매개변수 저장
  • 힙 영역 → 프로그래머가 직접 할당
  • 데이터 영역 → 전역 변수, static 변수 저장
  • 지역 변수 및 매개변수는 모두 스택 메모리를 사용

원시 타입 재할당

  • 가비지 컬렉터
    • 더 이상 참조되지 않는 데이터는 가비지 컬렉터에 의해 적절한 시점에 메모리에서 해제
int a = 10;
int b = 20;

// 1) 
int a = 20;

// 2)
int b = 30 ;

// 0x100 --- 10
// 0x101 ---  20

// 1) 재할당 하면 20을 저장하는 주소 값으로 교체 
// 0x100 ↘️ 10
// 0x101 ---  20
 
// 2) 기존에 없는 값은 새로운 메모리를 확보하여 저장하고 b가 가르키는 주소값 대체
// 0x100 -x->  10
// 0x101  ↘️   20
// 0x110       30

const 상수

  • 변하지 않는 수
  • 선언과 동시에 초기화해야 함
  • 메모리 공간에 수가 존재하지만 그 값을 변경할 수 없음

scanf 사용자로 부터 데이터 입력

scanf("서식 문자열", &변수);

✅ 기본 문법

if 문

  • 수행 조건에 관계 연산자 사용
if( 수행 조건 ){
  수행하고 싶은 일
}

if ~ else 문

if( 수행 조건 ){
  수행 조건 만족 시 수행
}else{
  수행 조건 불만족 시 수행
}

if ~ else if ~ else 문

if( 수행 조건 1 ){
  수행 조건 만족 시 수행
}else if( 수행 조건 2 ){
  수행 조건 만족 시 수행
}{
  수행 조건 1, 2 불만족 시 수행
}

while 문

  • 반복 조건을 만족하는 동안 반복 문장을 수행
  • 반복 조건을 만족하지 않으면 while문을 빠져나감
while( 반복 조건 ) {
 반복 문장
 }

이중 while 문

while( 조건 1 ){
  while( 조건 2 ){
  ....
    }
  }

무한 루프

  • 반복 수행이 무한하게 일어나는 것
  • 무한 루프 안에서 어느 시전에 break이나 return으로 빠져나올 수 있게 설계

for 문

  • while문과 다르게 변수의 초기화 연산과 증감 연산의 기능을 제공
  • 반복하고자 하는 내용을 수행 후 증감문 수행
for(초기문; 조건문; 증감문){
  반복하고자 하는 내용
}

break 문

  • 반복 문 내부에서 특정 조건이 되어 break를 만나면 while문을 빠져나가게 된다.
while(1){

  if(a=b) break; // a와 b가 같으면 while문을 빠져나감
  
}

continue 문

  • 반복 문 내부에서 특정 조건이 되어 continue를 만나면 이하의 수행은 무시하고, 다시 반복의 시작점으로 이동한다.
while(1){

  if(a=b) continue; // a와 b가 같으면 아래의 ... 내용을 무시하고 다시 while문 시작으로 돌아감
     ...
}

✅ 함수

사용 이유

  • 코드의 가독성 향상
  • 코드의 유지 보수 및 확장 용이

함수의 종류

  • 표준 함수
    • 언어에서 기본적으로 제공해주는 함수
    • 함수를 라이브러리화 시켜서 편리하게 사용할 수 있음
      • e.g. printf
  • 사용자 정의 함수
    • 사용자가 자신이 원하는 기능을 직접 만듦

함수의 기본 형태

자료형 함수이름 (인수 목록){
   함수의 내용
 }
  • 데이터타입
    • 함수가 리턴하는 값의 타입
    • void 타입
      • 결과값을 리턴하지 않는 함수

        void print(){ // 리턴할 값이 없는 함수 e.g. 출력만 하는 함수
          printf("출력")
          }
  • 함수 이름
    • 함수 기능과 밀접한 이름으로 작명
  • 인수 목록
    • 함수에 필요한 값을 전달
  • 함수의 내용
    - 중괄호 사이의 영역 안에서 작성

✅ 변수의 범위

지역 변수

  • 코드에서 지역의 의미
  • 같은 이름이라도 다른 영역의 함수에 속해 있으면 독립된 다른 함수
  • 함수 호출 순서와 소멸 순서는 반대 (FILO)
    • 함수가 끝나면 지역 변수도 소멸
  • 함수의 매개변수 또한 스택 메모리에 할당되는 지역 변수

전역 변수

  • 함수 밖에 선언된 변수
  • 모든 함수에서 접근 가능
  • 코드 관리 면을 고려해서 불가피한 경우에만 사용
  • 프로그램이 시작하자마자 메모리 상에 올라가서 프로그램이 종료될 때 메모리 상에서 소멸
  • 스택 영역이 아닌 데이터 영역에서 관리

static 변수

  • 지역 변수와 같이 중괄호 영역에서 선언되지만, 중괄호를 벗어나도 메모리 상에 고정되어 소멸하지 않음
  • 전역 변수와 같이 데이터 영역에서 관리
void func(){
  static int val = 0; // static으로 선언하지 않으면 매번 초기화
  val++;
  printf("%d\n", val);
}
int main(){
  int i = 0;
  while(i < 3){
   func();
   i++;
  } 
}

// 1
// 2
// 3

메모리 생명 주기

  • 전역 변수 → 프로그램 시작 ~ 프로그램 끝
  • static 변수 → 함수 안 변수 선언 시점 ~ 프로그램 끝
  • 지역 변수 → 함수 안 변수 선언 시점 ~ 함수 끝

✅ 배열

  • 같은 속성을 가진 것들을 나열해 놓은 것
  • 여러 개의 변수를 간단하게 선언할 수 있음
int a[100];

배열 선언 구조

int arr[5];
// 배열의 타입, 배열 이름, 배열 길이

int arr[5] = {1, 2, 3, 4, 5};
// 배열의 초기화
  • 배열의 길이를 생략하고 선언하는 것이 일반적

배열의 복사

  • 같은 타입의 배열끼리 복사 가능
  • 배열은 상수이므로 대입 연산자로 복사 불가능
    • 배열의 요소끼리 복사

문자열 변수

char str[12] = "Hello World";
  • 문자열 끝에는 null문자가 반드시 추가됨
    - 배열의 공간이 남았을 때 추가되는 쓰레기 값과 문자를 구분하지 못하기 때문에 끝을 인식하기 위해 null을 표시

✅ 포인터

  • 포인터 변수의 줄임말로 메모리의 주소값을 저장하고 있는 변수
  • 어떤 특정 메모리 주소를 가리키거나 향하고 있음
  • 원격 데스크와 비유하여 생각

포인트 변수

  • int *pA 앞에 * 기호를 붙여 사용
  • int *pA = &a; 주소 값만 저장하므로 일반 변수에 저장할 수 없음
int main(){
 int a = 5;
 int *pA = &a;
 
 printf("%p\n", &a); // b의 주소값 출력
 printf("%p\n", pA); // b의 주소값 출력
 printf("%d\n", *pA); // PA가 가르키고 있는 값 출력 = 5
}

배열 이름의 의미

  • 배열 이름은 배열의 첫 번째 요소의 주소값을 갖음
  • arr = &arr[0] (= 값이 아닌 주소)
  • 배열의 이름은 주소값을 갖는 포인터이지만 상수이므로 주소값을 저장할 수 없음
    • arr = temp; 와 같은 대입은 오류를 일으킴

값에 의한 복사

int a = 1;
int b = a; 
// 값을 전달
// a와 b 둘 다 값이 존재

참조에 의한 복사

void func(int *pArr){
...
}

int main(){
 int arr[] = {1, 2, 3};
 func(arr); // 주소값을 전달
}

// 배열의 주소값을 포인터로 넘겨서 참조 사용 가능

함수 포인터

  • 함수명 앞에 *를 붙여주면 함수 포인터가 됨
  • 함수 포인터도 포인터이므로 주소값을 저장
int (*func)(int a);

// 자료형 (*함수 포인터 이름)(인자 목록)
int Plus(int a, intb){
  return a -b;
}

int main(){
  int a = 10;
  int b = 5;
  
  int (*fptr)(int a, int b);
  
  fPtr = Plus;  // 함수의 주소값을 넘김
  
  int result = fPtr(a,b); // 포인터가 간접 호출
  printf("%d\n", result);
  
  return 0;
}

함수 포인터 사용 이유

  • 프로그램의 유연한 확장성을 제공
    - 코드의 규격을 맞추어 놓고 함수 포인터를 전달해서 동적으로 기능 추가

✅ 구조체

  • 하나 이상의 서로 다른 종류의 변수들을 묶어서 새로운 데이터 타입을 정의하는 것
  • 연관된 변수들을 묶어서 관리함으로써 데이터 관리에 유용
  • 데이터 양이 많아지면 구조체가 관리하기 유리
  • e.g. 학생 정보 관리 시스템 - 이름, 나이 성별
    • 위 정보를 따로 관리한다면 어떠한 연관성이 있는지 알기 어려움 → 데이터 분산
struct  student{ // 내가 정의한 구조체 student라는 새로운 데이터 타입
	char name[10];
	int age;
	int height; 
}

구조체 멤버 접근

st1.name, st1.age, ...
// [구조체 변수명].[구조체 멤버]
// 멤버 접근 시 사용하는 .을 직접 접근이라고 함
struct  student{ 
	int age;
	int height; 
} st1;

int main(){
  st1.age = 10;
  st1.height = 130;
}

✅ 공용체 union

  • 사용자 정의 자료형
union student{ // 내가 정의한 구조체 student라는 새로운 데이터 타입
	char name[10];
	int age;
	int height; 
};
  • 구조체와의 차이점 → 메모리 공간 공유
  • 가장 큰 크기의 메모리에 맞춰 공유

✅ 열거형 enum

  • 데이터를 열거한 집합
  • 컴파일러가 열거형 멤버들은 정수형 상수로 취급
  • 연속된 데이터를 정의할 때 주로 사용
enum Week{ // 내가 정의한 구조체 student라는 새로운 데이터 타입
	sun = 0, // 첫 번째를 0으로 설정하면 이후 멤버들은 1씩 증가
	mon,
	tue,
	...
};

✅ 메모리 영역

  • 코드 영역
    • 실행할 명령어들이 순서대로 쌓이며 CPU가 이 영역에서 명령어를 하나씩 처리
  • 스택 영역
    • 스택이란 모든 원소들의 삽입, 삭제를 한쪽 방향에서만 수행하는 선형 자료 구조
    • 지역 변수, 매개변수는 모두 스택 메모리 사용
  • 힙 영역
    • 큐 구조, 컴퓨터 메모리의 일부가 할당되었다가 회수되는 일들의 반복
    • 컴파일이 아닌 실행시 사용자로부터 할당 메모리를 입력 받음 = 동적 할당
  • 데이터 영역
    • 전역 변수와 static 변수가 저장되는 메모리 영역
    • 프로그램 종료 시 소멸

동적 메모리 사용 이유

// 일반 변수 선언 -> 정적
int a; 

int user[30]; 
  • 위와 같이 정적 할당을 할 경우에는 컴파일 타임에 메모리 실행이 이루어지기 때문에 user와 같이 사용자 수가 바뀔 수 있는 상황에서의 사용은 적합하지 않음
  • 변수가 유동적일 때에는 실행시 결정
void* malloc(size_t size); // 동적 메모리 할당 함수의 원형

// void* 타입이 지정되지 않은 포인터
  • 전달 인자 size를 bite 단위로 입력
  • 메모리 할당이 되면 메모리 주소값 리턴

동적 메모리 할당 및 해제

int main(){
  int num;
  int *student;
  
  printf("학생의 수를 입력하세요.");
  scanf("%d", &num);
  
  student = (int*)malloc(sizeof(int) * num);
  
  printf("할당된 메모리 크기 : %d", sizeof(int)*num);
  
  free(student); // 사용한 메모리 해제 -> 해제 안하면 메모리 누수
}

참고

						(출처) https://pridiot.tistory.com/244
  • 힙 메모리는 stack 메모리의 포인터를 총해서 사용 가능
    • 단독으로 사용 불가능

✅ 객체 지향 프로그래밍

  • 기능 단위는 객체이므로 유연한 구조를 가지고 있음
  • 중간에 설계, 기능을 수정 및 반영 가능

추상화

  • 대상에서 공통적인 특징만을 뽑아낸 것
    • e.g. 음식 - 피자, 치킨, 탕수육

캡슐화

  • 클래스 = 데이터 + 메소드 → 사용자 정의 타입
  • 은닉, 숨긴다는 뜻
  • 외부에서 내부의 내용을 볼 수없게 한다는 의미
  • 외부에서 데이터를 조작할 인터페이스 필요
    • 인터페이스 = 성곽으로 둘러쌓여있는 성의 문

클래스

  • 사용자 정의 데이터 타입
    • 본질이 데이터타입인 것을 잊지 않기
    • e.g. int a; 처럼 Student a; (데이터 타입이니까!)
  • 멤버 변수와 멤버 함수로 구성

클래스의 구성

  • 사물의 특성을 정리해서 필드(변수)와 메소드로 표현하는 과정 → 추상화
  • 추상화된 결과를 하나의 클래스에 포함시키고 스스로 보호하는 것 → 캡슐화

클래스의 선언 형식

  • 생성자, 멤버 변수, 메소드 등으로 구성
class 클래스 이름{ // 클래스 선언
 접근 지정자 클래스 이름 () { }         // 생성자
 접근 지정자 ~클래스 이름 () { }        // 소멸자
 접근 지정자 데이터형 멤버 변수(필드);   // 변수 선언
 접근 지정자 데이터형 메소드() { };     // 메소드 선언
}

접근 지정자

접근 지정자의미
public누구나 접근 가능
protected상속 관계에 있을 때 상속 받은 자식 클래스에서만 접근 가능
private나의 클래스 내부에서만 접근 가능, 외부에서는 절대 접근 불가능

객체 생성

Dog a = new Dog(); // 객체 a (원래는 주소를 가지고 있는 포인터 변수)
class Dog{
  private int eyes, nose, mouse, ears; // 다른 곳에서 접근 할 수 없게 캡슐화
  public void bark(){Console.WriteLine("멍멍");}
}
class HelloWorld{
  static void Main(){
    Dog a = new Dog(); 
    a.bark();
  }
}

생성자 constructor

  • 객체도 본질적으로 변수이므로 선언되면 초기화해야함
  • 생성자는 객체 생성 초기화 전용 메소드
  • 객체 생성시 자동으로 호출되는 메소드
  • 클래스의 이름과 같아야 함
  • return 타입이 없음
class Dog{
  private int eyes, nose, mouse, ears; // 다른 곳에서 접근 할 수 없게 캡슐화
  public void bark(){Console.WriteLine("멍멍");}

 public Dog(){
   eyes = 0;
   nose = 0;
   mouse = 0;
   ears = 0;
 }
}

상속성

  • 이미 완성된 클래스를 다른 클래스에 상속할 수 있음
class Dog{
  protected int eyes, nose, mouse, ears; // 자식들이 사용할 수 있게 protected
  public void bark(){Console.WriteLine("멍멍");}

 public Dog(){
   eyes = 0;
   nose = 0;
   mouse = 0;
   ears = 0;
 }
}

class Poodle : Dog{
public Poodle(){
  base.eyes = 1;
  Console.WriteLine("Poodle eyes : {0}", eyes);
}
}

class Hello{
  static void Main(){
    Dog a = new Dog(); 
    a.bark();
    Poodle pd = new Poodle();
  }
}

다형성

  • 함수 이름이 같더라도 전달 인자(매개변수)의 타입이나 개수에 따라 구분됨
    • 오버로딩(overloading), 오버라이딩(overriding)

오버로딩

  • 함수 이름은 똑같지만 전달 인자 타입이나 개수가 둘 중 하나라도 다른 경우
int Plus(int a, int b){ }
int Plus(int a, int b, int c){ }
char Plus(char a, char b){ }
int Plus(double a, double b){ }

오버라이딩

  • 상속의 개념이 기반이 되어 기존의 것을 덮어버린다는 개념
class Dog{
  protected int eyes, nose, mouse, ears; // 자식들이 사용할 수 있게 protected
  virtual public void bark(){Console.WriteLine("멍멍");}

 public Dog(){
   eyes = 0;
   nose = 0;
   mouse = 0;
   ears = 0;
 }
}

class Poodle : Dog{
public Poodle(){
  base.eyes = 1;
  }
  public override void bark(){Console.WriteLine("왈왈");} // 오버라이딩
}
class Jindo : Dog{
public Poodle(){
  base.eyes = 1;
  }
  public override void bark(){Console.WriteLine("컹컹");} // 오버라이딩
}

class Hello{
  static void Main(){
    Dog a = new Dog(); 
    a.bark(); // 멍멍
    Poodle pd = new Poodle();
    pd.bark(); // 왈왈
    
    dog = new Jindo();
    dog.bark(); // 컹컹
  }
}

✅ 인터페이스

  • 메소드의 목록만을 가지고 있는 명세, 사용자 정의 타입
  • 메소드의 목록만 선언하고 구현은 하지 않음
  • 인터페이스를 상속받는 자식 클래스에서는 메소드를 반드시 오버라이딩해서 사용해야함
접근 지정자 interface 이름 : 기반 인터페이스(필수x){} // 인터페이스 선언 형태

접근 지정자 class 자식 클래스 이름 : 인터페이스{} // 인터페이스를 상속받는 클래스 형태
  • 기존의 기능을 추가하거나 수정의 개념보다 동일한 개념의 기능을 새롭게 구현하는 기능

  • 공동 작업 시 표준을 정하는 역할 (설계의 역할)

  • 추상 클래스 상속 → 기능 확장 목적

  • 인터페이스 상속 → 기능 명세(목록만 만듦)

public interface Feature{
  void Afeat();
  void Bfeat();
}

public class First : Feature{
  public void Afeat(){Console.WriteLine("첫번째 기능 A");}
  public void Bfeat(){Console.WriteLine("첫번째 기능 B");}
}

public class Second : Feature{
  public void Afeat(){Console.WriteLine("두번째 기능 A");}
  public void Bfeat(){Console.WriteLine("두번째 기능 B");}
}

class Hello{
  static void Main(){
  First f = new First();
  f.Afeat(); // 첫번째 기능 A
  f.Bfeat(); // 첫번째 기능 B
  Second s = new Second();
  s.Afeat(); // 두번째 기능 A
  s.Bfeat(); // 두번째 기능 B
  }
}

메모리 관리

  • 객체 지향 언어는 가비지 컬렉터가 메모리를 자동 관리
  • 백그라운드에서 더 이상 사용되지 않는 메모리를 찾아 회수

익명 메소드

  • 익명 메소드를 사용하면 코드를 간결해지고, 별도의 메소드를 만들지 않으므로 코딩 오버헤드를 줄일 수 있음
  • 람다식에서 사용
    - 람다식 - 기존 익명 메소드를 더욱 간결하게 만듦

람다식 표현

(인수) => 표현식 또는 명령문

Delegate(int x, int y) {return x+y;}
(x, y) => x+y; // 람다식 표현

0개의 댓글