포스트

2024.06.14 프로그래머스 - C언어로 프로그래밍 근간 알아보기 3

C언어로 프로그래밍 근간 알아보기 3

함수 포인터

  • 함수명 앞에 * 만 붙여주면 함수 포인터가 선언된다.
  • 함수 포인터도 포인터이므로 주소값을 저장한다.
1
2
// 자료형 (* 함수 포인터 이름) (인자 목록)
int (*func) (int a);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <stdio.h>

int Plus (int a, int b) {
    return a + b;
}

int Minus (int a, int b) {
    return a - b;
}

int main()
{
    // 함수 포인터 선언
    int (*fPtr)(int pa, int pb);
    
    fPtr = Plus; // Plus 함수의 주소값을 fPtr에 넣겠다!
    
    int a = 10;
    int b = 20;
    int sum = fPtr(a, b); // 포인터 함수로 함수 호출
    
    
    printf("%d\n", sum);
    printf("Hello World");

    return 0;
}

함수 포인터 사용 이유

  • 메모리의 크기 및 위치가 결정되는 시점은 컴파일 타임 또는 런타임 시점
    • 컴파일 타임에 결정되면 정적 바인딩
    • 런타임 시점에 결정되면 동적 바인딩 : 실행중에 메모리를 결정 한다.
  • Plugin 방식 을 사용하여, 매번 변경점이 생길 때마다 컴파일을 할 필요없이 구성이 가능하다.
  • 함수 포인터의 사용은 프로그램의 유연한 확장성을 제공한다.

구조체

  • 하나 이상의 서로 다른 종류의 변수들을 묶어서 새로운 데이터 타입을 정의하는 것
  • 연관된 변수들을 하나로 묶어서 관리함으로써 데이터 관리에 유용하다.
  • 데이터의 양이 많아지면 구조체가 유리하다.
1
2
3
4
5
6
7
8
9
10
11
12
struct student
{
	char name[10];
	int age;
	int height;
}
// struct 키워드는 `구조체` 를 의미
// student 는 내가 지정한 구조체 이름
// name, age, height 는 구조체의 멤버

// 구조체의 이름으로 정의했던 student로 이제 커스텀 구조체 자료형을 사용할 수 있다.

  • 구조체 사용 예시

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    
      #include <stdio.h>
      #include <string.h> // 문자열 관련 라이브러리 헤더 파일
        
      struct student {
          char name[10];
          int age;
          int height;
      };
        
      void main () {
          struct student st1;
          strcpy(st1.name, "테스트"); // 문자열 복사
          st1.age = 20;
          st1.height = 190;
        
            
          printf("이름 : %s, 나이 : %d, 키 : %d\n", st1.name, st1.age, st1.height);
            
      }
        
    

공용체

  • 공용체 또한 사용자 정의 자료형이다.
  • 공용체는 구조체와 다르게 메모리 공간을 공유 한다.
  • union 키워드를 사용하여 공용체를 선언 할 수 있다.

열거형

  • enumeration 의 약자로 enum 이라고 읽는디ㅏ.
  • 데이터들을 열거한 집합이다.
  • 컴퍼일러는 열거형 멤버들을 정수형 상수로 취급한다.
  • enum 키워드를 사용하여 정의한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include <stdio.h>

enum week {
    sun = 1,
    mon,
    tue,
    wed,
    thu,
    fri,
    sat,
};

void main () {
  int day;
  
  printf("요일을 선택하세요 : ");
  scanf("%d", &day);
  
  switch(day){
      case sun:
        printf("일요일\n");
        break;
      case mon:
        printf("월요일\n");
        break;
      case tue:
        printf("화요일\n");
        break;
      case wed:
        printf("수요일\n");
        break;
      case thu:
        printf("목요일\n");
        break;
      case fri:
        printf("금요일\n");
        break;
      case sat:
        printf("토요일\n");
        break;
      default :
        printf("잘못 입력하였습니다.");
        break;
  }
}

동적 메모리 할당

  • 코드 영역 : 실행할 명령어들이 순서대로 쌓이는 영역,
    • CPU가 이 영역에서 명령어들을 하나씩 가져가 처리한다.
  • 스택 영역 : Stack형 자료구조 (LIFO)
    • 나중에 들어간 것이 처음에 나온다.
    • 지역변수 및 매개변수 는 모두 스택 메모리를 사용

힙 영역

  • 컴퓨터 메모리의 일부가 할당 되었다가 회수되는 일들이 반복되어 일어난다.
  • 컴파일시가 아닌 실행시 사용자로부터 할당 메모리를 입력 받는다.
    • 동적 메모리 할당 이 일어나는 부분

동적 메모리 할당의 이유

  • 유동적인 메모리사용(변수 사용) 에 대해 유리하다.
    • 런타임 시점에서 할당해야할 변수들이 발생할 때
  • 동적 메모리 할당 함수의 원형

    1
    
      void *malloc*size_t size);
    
    • 전달인자 size는 바이트 단위로 입력
    • 메모리 할당이 되면 주소값을 리턴
    • 메모리 부족 시 NULL 포인터를 리턴
    • void * 의 의미 : 타입이 지정되지 않는 포인터
      • 동적으로 유동적인 메모리할당을 승낙? 하는 문
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <stdio.h>

int main () {
    int num;
    int *student;
    
    printf("학생 수를 입력하세요 :");
    scanf("%d", &num);
    
    student = (int*)malloc(sizeof(int) * num);
    
    if(student == NULL) {
        printf("메모리가 할당 되지 않았습니다.\n");
        return;
    } 
    
    printf("할당된 메모리 크기는 %d입니다.\n", sizeof(int)*num);
    free(student); // GC가 없기 때문에 수동으로 메모리를 무효 시키는 방법
    return;
}

객체 지향 프로그래밍

객체 지향 프로그래밍 방식 (OOP)

  • JS 는 객체를 사용하는 객체 기반 언어이다.
  • 객체를 생성하는 원리를 이해하면 객체를 더욱 잘 사용할 수 있다.
  • TS와 React기반에서 필요하다.
  • 사물을 프로그래밍화 시키겠다는 느낌쓰.

  • 기존에는 구조적 프로그래밍 방식을 사용했다.
    • 시퀀스가 한방향 (하향식)으로 를러가는 방식이다.
    • 기능적인 기본 단위는 함수

OOP 방식

  • 기본적인 기능 단위는 객체 이다.
  • 대표적인 예로 이벤트 기반의 모든 윈도우 프로그램
  • 유연한 변화 대처가 가능하다.

추상화

  • 추상 : 대상에서 특징 만을 뽑아낸것
  • 우리 주변의 사물 (생물) 들은 관념적이고 추상적인 것들이 많다.
    • 이러한 개념을 프로그래밍에 적용한것ㅠ ㅜ

캡슐화

  • 은닉한다, 숨긴다는 의미
  • 캡슐화 = 외부에서 내부를 볼 수 없게 만듦을 의미
  • 숨기기만 하는 것이 아닌, 외부에서 해당 데이터를 조작 가능한 인터페이스를 구축 후 숨김
  • 캡슐화의 대표적인 예 : 클래스 = 데이터 + Method

클래스

  • 사용자 정의 데이터 타입
  • 데이터와 메소드를 사용자인 내가 새로 정의한 데이터타입이다.
    • 추상적인 데이터 타입이라고 불리는 이유
    • 구조체와 비슷하다.
    • 멤버 변수와 멤버 함수로 구성된다.
  • 클래스는 다른 특별한 무언가가 아니라 그냥 데이터 타입 이다.
  • 사물의 특성을 정리하여 필드와 메소드로 표현하는 과정 → 추상화
  • 추상화된 결과를 하나의 클래스에 포함시키고 스스로 보호하게 하는것 → 캡슐화
1
2
3
4
5
6
7
class 클래스명
{
	잡근지정자 클래스 이름(){} // 생성자
	잡근지정자 ~클래스 이름(){} // 소멸자
	잡근지정자 데이터형 멤버변수(필드); // 변수 선언
	잡근지정자 데이터형 메소드(){...} // 메소드 선언
}

클래스의 접근 지정자

접근 지정자의미
public누구나 접근 가능하다.
protected상속 관계에 있을 때, 상속받은 자식 클래스에서 접근 가능, 그외는 접근 불가
internal같은 프로젝트 내의 모든 클래스가 접근 가능하다.
protected internalprotected 와 internal의 의미를 모두 포함
private내 클래스 내부에서만 접근 가능하고, 외부에서는 접근이 불가능

클래스 (객체) 선언

  • 클래스의 본질은 데이터 타입 이다.
  • 데이터 타입 지정자를 통해 변수를 선언할 수 있는 클래스로도 객체를 선언할 수 있다.
1
2
// 객체의 생성 (by Class named "Dog"
Dog a = new Dog();
  • C# 에서의 클래스 사용

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    
      using System;
        
      class Dog{
          private int eyes, nose, mouse, ears;
          public void bark(){
              Console.WriteLine("Bark!!!");
          }
      }
        
      class HelloWorld{
          static void Main() {
              Dog a = new Dog(); // private로 선언된 부분은 접근 할 수 없다.
              a.bark(); // "Bark!!"
              Console.WriteLine("Hello World");
          }
      }
    

클래스의 생성자

  • 모든 변수는 선언이 되면 초기화 해야 한다.의 개념을 인지해야 한다.
  • 객체 또한 본질적으로 변수 이므로 초기화가 필요하다.
  • 객체 생성시 초기화 전용 메소드를 제공하는데, 이것이 생성자(Contstuctor)
  • 생성자는 작성해 놓으면, 객체 생성시 자동으로 호출되어 수행 된다.

상속성

  • 현실 세계의 상속 개념과 유사하다. (부모 → 자식)
  • 이미 완성된 클래스를 다른 클래스에 상속할 수 있다.
  • JS의 경우 extends 키워드를 통해 부모 클래스의 상속을 받을 수 있다.
    • C의 경우 “클래스이름 : 부모 클래스” 로 상속을 진행한다.
  • protected 로 지정한 접근자는 자식 클래에서 접근 가능하다. (private는 안됨)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
using System;

class Dog{
    protected int eyes, nose, mouse, ears;
    public void bark(){
        Console.WriteLine("Bark!!!");
    }    
    public Dog() { // 생성과 동시에 초기화 진행
    
        eyes = 0;
        nose = 0;
        mouse = 0;
        ears = 0;
    }
}

class PudleDog : Dog {
    public PudleDog() { // 생성자
        base.eyes = 2; // base 키워드를 통해 부모 클래스의 멤버를 사용할 수 있다.
        Console.WriteLine("푸들의 눈 : {0}", eyes);
        
    }
}

class HelloWorld{
    static void Main() {
        Dog a = new PudleDog(); // private로 선언된 부분은 접근 할 수 없다.
        
        a.bark();
        Console.WriteLine("Hello World");
    }
}

다형성

  • 함수의 이름이 같더라도 전달인자의 타입이나 개수에 따라 구분된다
    • JS는 이름으로 구분된다..
  • 객체지향에서는 대표적으로 오버 로딩과 오버 라이딩 기법이 있다.

오버로딩

  • 과적하다, 적재하다 라는 의미를 가지고 있다.
  • 겉모습은 같지만, 내용이 다른경우
  • 이름이 같은 함수일지라도 전달인자 타입이나 개수가 다른 경우
  • 스타크래프트의 오버로드 느낌 ㅎㅎ
    • 저글링을 태운 오버로드와, 히드라를 태운 오버로드는 다른 유닛이다.

오버라이딩

  • 위로 올라탄다, 엎어 친다는 의미를 갖는다.
  • 기존의 것 위에 올라탄다. (덮어쓰기)
  • 상속 의 개념이 기반이 되어야 한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
using System;

class Dog{
    protected int eyes, nose, mouse, ears;
    virtual public void bark(){
        Console.WriteLine("Bark!!!");
    }

    public Dog() { // 생성과 동시에 초기화 진행
    
        eyes = 0;
        nose = 0;
        mouse = 0;
        ears = 0;
    }
}

class PudleDog : Dog {
    public PudleDog() { // 생성자
        base.eyes = 2; // base 키워드를 통해 부모 클래스의 멤버를 사용할 수 있다.
        Console.WriteLine("푸들의 눈 : {0}", eyes);
    }
    
    public override void bark() { // 오버라이딩 : 상속받은 클래스의 멤버 메서드를 덮어썼다.
       Console.WriteLine("왈왈"); 
    }
}

class HelloWorld{
    static void Main() {
        Dog dog = new Dog(); // private로 선언된 부분은 접근 할 수 없다.
        dog.bark(); // "Bark!!"
        
        PudleDog pudle = new PudleDog();
        pudle.bark(); // "왈왈"
        
        Console.WriteLine("Hello World");
    }
}

인터페이스

  • 메소드의 목록만을 가지고있는 명세서
    • 사용자 정의 타입 이다.
  • 메소드의 목록만 선언하고, 구현하지는 않는다.

  • 인터페이스 사용의 목적
    • 본체가 정의 되지 않은 추상 메소드 만 갖고 있다.
    • 인터페이스의 목적은 기존의 기능을 추가,수정보다는 동일한 개념의 기능을 새롭게 구현하는 기능
    • 공동작업시의 표준을 위해 사용
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
using System;

public interface IUnit {
    void Attack();
    void Move();
}

public class Zergling : IUnit {
    public void Attack() {
        // 해당 함수 구현
        Console.WriteLine("저글링 : 공격");
    }
    
    public void Move() {
    
        Console.WriteLine("저글링 : 이동");
    }
}

public class Dragoon : IUnit {
    public void Attack() {
        Console.WriteLine("드라군 : 공격");
    }
    
    public void Move() {
    
        Console.WriteLine("드라군 : 이동");
    }
}

class HelloWorld {
    static void Main() {    
        
        Zergling zerg = new Zergling();
        Dragoon dragoon = new Dragoon();
        
        zerg.Attack();
        zerg.Move();
        dragoon.Attack();
        dragoon.Move();
    }
}

람다

  • 익명 메소드
    • 메소드를 미리 정의하지 않고, 사용할 때 정의
    • 익명 메소드를 사용하면 코드가 간결해진다.
    • 별도의 메소드를 만들지 않으므로 코딩 오버헤드를 줄일 수 있다.
    • 내용 자체가 복잡하면 안된다.
    • 람다식에서 주로 사용된다.
  • 람다식
    • 기존 익명 메소드를 더욱 간결하게 만든 것
    • 코드를 짧고 간결하게 표현하는 것이 목적 (문법적 장점)

(x, y) ⇒ x + y; 의 문법으로 람다식을 사용한다. (C#)

1
2
3
4
5
6
7
8
9
10
11
using System;

class HelloWorld {
    delegate int CalcDele (int x);
    
    static void Main() {    
        CalcDele d = x => x + 1;
        Console.WriteLine(d(3));
      
    }
}
  • C#의 람다식을 보면, JS ES6에서의 화살표 함수와 상당히 유사한 것을 알 수 있다.
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.