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 internal | protected 와 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 라이센스를 따릅니다.