함수 포인터
함수도 변수에 담아서 사용할 수 있다.
실행 시(런타임)에 어떤 함수를 가리키는지 결정되기 때문에 동적 바인딩 된다. 또한, 함수 포인터는 callback 함수를 구현할 수 있게 해 준다.
아래 코드의 main 함수에서 pFunc가 함수 포인터이며, add 함수를 pFunc라는 포인터 변수에 담아서 사용하고 있다.
int add(int a, int b) {
return a + b;
}
int main(void) {
int a = 10, b = 20, result = 0;
int (*pFunc)(int a, int b); // 함수 포인터 선언
pFunc = add; // 동적 바인딩
result = pFunc(a, b);
printf("a + b = %d", result);
return 0;
}
구조체
하나 이상의 서로 다른 종류의 변수들을 묶은 데이터 타입이다.
struct structName {
int member1;
char member2[10];
};
- struct 키워드: 구조체라는 데이터 타입을 의미
- structName: 구조체의 이름
- member: 구조체 멤버
연관된 변수들을 하나로 묶어서 관리하기 때문에 데이터 관리에 유용하며, 변수의 개수가 많아지면 구조체를 사용하는 것이 유리하다.
예를 들어, 학생의 정보(이름, 나이, 생년월일)을 저장한다고 할 때, 각 정보마다 변수를 선언하는 것보다 하나의 구조체에 변수들을 모아두는 것이 낫다.
또한, 구조체도 자료형이기 때문에 배열로 만들 수 있다.
아래 코드에서는 Student라는 구조체를 정의했다. main 함수에서는 구조체 배열을 생성하여 초기화하고, 각 구조체 멤버들을 (.) 연산자로 직접 접근하여 출력한다.
struct Student {
char name[10];
int age;
char birth[7];
};
int main(void) {
struct Student st[] = {
{ "김철수", 24, "010512" },
{ "이철수", 22, "031225" },
{ "최철수", 26, "990129" }
};
for (int i = 0; i < 3; i++) {
printf("이름: %s, 나이: %d, 생년월일: %s\n", st[i].name, st[i].age, st[i].birth);
}
return 0;
}
공용체
공용체도 사용자 정의 자료형이다.
위 그림과 같이 공용체는 메모리 공간을 공유한다는 점에서 구조체와 차이가 난다.
공용체를 사용하는 이유는 특정 공간을 효율적으로 관리하기 위함이다.
union unionName
{
char a;
int b;
double c;
};
- union 키워드: 공용체라는 데이터 타입을 의미
- unionName: 공용체의 이름
열거형
데이터들을 열거한 집합이다. 컴파일러는 열거형 멤버들을 정수형 상수로 취급한다.
첫 번째 멤버를 어떤 상수로 설정하면, 다음 멤버는 1씩 증가한다.
아래 코드로 예시를 들면, mon은 1, tue는 2, wed는 3 ... 이런 식이다.
enum Week {sun = 0, mon, tue, wed, thu, fri, sat};
동적 할당
동적으로 메모리를 할당하는 이유는 배열의 크기를 실행 시에 결정하기 위함이다. 동적으로 할당된 메모리는 힙에 저장되며, 힙은 큐 구조를 가진다.
동적 메모리 할당 함수의 원형은 다음과 같다.
void* malloc(size_t size);
- size: 할당할 메모리의 크기로, 바이트 단위로 입력한다.
- 메모리가 할당되면 메모리 주소값을 리턴하고, 메모리가 부족하면 NULL 포인터를 리턴한다.
malloc의 사용 예시는 다음과 같으며, 해당 메모리를 더 이상 사용하지 않으면 free()로 메모리를 해제해야 한다. 해제하지 않을 경우 메모리 누수가 발생한다. 참고로 malloc을 사용하려면 stdlib.h를 include 해야 한다.
int main(void) {
int* nums;
int num = 10;
nums = (int*)malloc(sizeof(int) * num);
if (nums == NULL) {
printf("메모리가 부족합니다.");
return 0;
}
for (int i = 0; i < num; i++) {
nums[i] = i;
}
for (int i = 0; i < num; i++) {
printf("num[%d] = %d\n", i, nums[i]);
}
free(nums); // 메모리 해제
return 0;
}
객체 지향
추상화
객체의 공통적인 특징을 하나의 개념으로 다루는 것을 추상화라고 한다.
예를 들어, 개와 고양이를 동물이라는 하나의 추상적인 개념으로 다룰 수 있다.
dog라는 객체와 cat이라는 객체를 따로 만들어서 관리할 수도 있지만, animal이라는 객체를 만들어 공통된 특징을 저장한 후 dog와 cat에서는 추가적인 부분만 정의해 주면 된다.
추상화를 사용하면 코드 중복을 줄일 수 있으며, 유지보수, 가독성을 높일 수 있다.
캡슐화
외부에서 내부를 볼 수 없도록 하는 것을 말한다. 그래서 외부 또는 내부에서 데이터를 조작할 수 있도록 인터페이스가 필요하다.
클래스는 데이터와 메서드로 구성되어 있다. 데이터는 은닉되어 있으며, 은닉된 데이터에 접근하기 위한 인터페이스를 멤버 함수(메서드)라고 한다.
멤버 함수를 사용해야만 데이터에 접근할 수 있기 때문에 안정성을 높일 수 있고, 데이터가 은닉되어 있기 때문에 다른 코드에 대한 의존성을 줄일 수 있으며, 유지보수성을 높일 수 있다.
배운 점
- 포인터를 사용하면 함수를 변수에 담을 수 있다.
- 원하는 변수들을 묶은 구조체라는 사용자 정의 데이터 타입이 있다.
- malloc을 사용하여 동적으로 메모리를 할당할 수 있고, 더 이상 쓰지 않는 메모리는 해제해줘야 메모리 누수가 발생하지 않는다.
- 객체 지향 프로그래밍의 특징인 추상화와 캡슐화를 통해 코드의 안정성, 가독성, 유지보수성을 높일 수 있다.
'데브코스' 카테고리의 다른 글
[11주차 - DAY3] 리터럴, 클래스 (0) | 2024.05.08 |
---|---|
[10주차 - DAY5] 클래스 (0) | 2024.05.03 |
[10주차 - DAY2] 포인터 (1) | 2024.04.30 |
[10주차 - DAY1] C언어 연산자, 분기문, 반복문 (0) | 2024.04.29 |
[10주차 주간 발표] 동적 라우팅과 다익스트라 알고리즘 (0) | 2024.04.27 |