구조체
구조체 정의와 변수 사용
#include <stdio.h>
#include <string.h>
struct employee {
char name[25];
int salary;
char dept;
} emp3; // 구조체 정의 시 전역 변수 emp3 선언
int main() {
struct employee emp1, emp2;
strcpy(emp1.name, "Kim");
emp1.salary = 50000;
emp1.dept = 'D';
emp2 = emp1; // 동일 타입 구조체 복사
strcpy(emp2.name, "Park");
emp3 = emp1;
strcpy(emp3.name, "Lee");
printf("직원1: %s, %d, %c\n", emp1.name, emp1.salary, emp1.dept);
printf("직원2: %s, %d, %c\n", emp2.name, emp2.salary, emp2.dept);
printf("직원3: %s, %d, %c\n", emp3.name, emp3.salary, emp3.dept);
return 0;
}
구조체 변수 초기화
#include <stdio.h>
#include <string.h>
struct employee {
char name[25];
int salary;
char dept;
} emp2 = {"Choi", 60000, 'M'}; // 구조체 정의와 동시에 초기화
int main() {
struct employee emp1 = {"Jung", 45000, 'E'}; // 지역 변수 초기화
printf("직원1: %s, %d, %c\n", emp1.name, emp1.salary, emp1.dept);
printf("직원2: %s, %d, %c\n", emp2.name, emp2.salary, emp2.dept);
return 0;
}
구조체 멤버로 다른 구조체 사용
#include <stdio.h>
#include <string.h>
struct hire_date {
int year;
int month;
int day;
};
struct employee {
char name[25];
struct hire_date start; // 구조체를 멤버로 포함
char dept;
} emp1 = {"Yoon", {2020, 3, 15}, 'S'};
int main() {
struct employee emp2;
emp2 = emp1;
emp2.start.year = 2022;
printf("원본: %s, %d-%d-%d, %c\n",
emp1.name, emp1.start.year, emp1.start.month, emp1.start.day, emp1.dept);
printf("복사: %s, %d-%d-%d, %c\n",
emp2.name, emp2.start.year, emp2.start.month, emp2.start.day, emp2.dept);
return 0;
}
무명 구조체
#include <stdio.h>
#include <string.h>
struct employee {
char name[25];
struct { // 이름이 없는 내부 구조체
int year;
int month;
int day;
} start;
char dept;
} emp1 = {"Seo", {2018, 7, 1}, 'T'};
int main() {
printf("직원: %s, 입사일: %d-%d-%d, 부서: %c\n",
emp1.name, emp1.start.year, emp1.start.month, emp1.start.day, emp1.dept);
return 0;
}
구조체 배열
#include <stdio.h>
#include <string.h>
struct employee {
char name[25];
int salary;
char dept;
};
int main() {
struct employee team[3] = {
{"Kim", 50000, 'D'},
{"Park", 60000, 'M'},
{"Lee", 45000, 'E'}
};
for (int i = 0; i < 3; ++i) {
printf("%s, %d, %c\n", team[i].name, team[i].salary, team[i].dept);
}
return 0;
}
구조체 포인터
#include <stdio.h>
#include <string.h>
struct employee {
char name[25];
int salary;
char dept;
};
int main() {
struct employee team[3] = {
{"Kim", 50000, 'D'},
{"Park", 60000, 'M'},
{"Lee", 45000, 'E'}
};
struct employee *ptr;
ptr = team;
for (int i = 0; i < 3; ++i) {
// 화살표 연산자와 역참조로 멤버 접근
printf("%s, %d, %c\n", (*ptr).name, ptr->salary, ptr->dept);
ptr++;
}
return 0;
}
공용체
공용체(union)는 서로 다른 데이터 타입이 같은 메모리 공간을 공유하는 구조체와 유사한 데이터 타입이다. 가장 큰 멤버의 크기만큼 메모리를 할당받으며, 한 번에 하나의 멤버만 유효한 값을 가진다.
#include <stdio.h>
#include <string.h>
union data_packet {
char buffer[20];
int code;
char flag;
};
int main() {
union data_packet packet;
strcpy(packet.buffer, "init");
packet.code = 42;
packet.flag = 'X';
printf("packet.buffer: %s\n", packet.buffer); // 'X' 문자로 덮어써짐
printf("packet.code: %d\n", packet.code); // 'X'의 ASCII 값으로 변경
printf("packet.flag: %c\n", packet.flag); // 최종 할당된 값 'X'
printf("공용체 크기: %lu\n", sizeof(union data_packet)); // 20
return 0;
}
typedef 사용
typedef는 기존 데이터 타입에 새로운 이름을 부여하여 코드를 간결하게 만든다.
#include <stdio.h>
#include <string.h>
typedef struct employee {
char name[25];
int salary;
char dept;
} Employee, *EmployeePtr; // 구조체 별칭과 포인터 별칭
int main() {
Employee emp; // struct employee emp와 동일
strcpy(emp.name, "Kim");
emp.salary = 50000;
emp.dept = 'D';
EmployeePtr p = &emp; // struct employee *p와 동일
printf("이름: %s\n", p->name);
printf("급여: %d\n", p->salary);
return 0;
}
#include <stdio.h>
#include <string.h>
struct employee {
char name[25];
int salary;
char dept;
};
typedef struct employee Employee; // 별도로 typedef 적용
int main() {
Employee emp;
strcpy(emp.name, "Park");
emp.salary = 60000;
emp.dept = 'M';
return 0;
}
메모리 관리
C 언어는 네 가지 메모리 영역을 사용한다:
- 코드 영역: 실행할 명령어가 저장
- 전역/정적 변수 영역: 프로그램 시작 시 할당, 종료 시 해제
- 스택 영역: 함수 호출 시 생성되는 지역 변수 저장, 함수 종료 시 자동 해제
- 힙 영역: 동적 메모리 할당에 사용, malloc/free로 관리
동적 메모리 관리: malloc/free
malloc은 힙에서 연속된 메모리를 할당하고, free는 할당된 메모리를 해제한다.
#include <stdio.h>
#include <stdlib.h>
char* read_name() {
char *buffer;
buffer = (char*)malloc(10 * sizeof(char)); // 힙에 10바이트 할당
if (buffer == NULL) {
printf("메모리 할당 실패!\n");
exit(1);
}
scanf("%s", buffer);
return buffer;
}
int main() {
char *name;
name = read_name();
printf("입력된 이름: %s\n", name);
free(name); // 할당된 힙 메모리 해제
name = NULL; // 댕글링 포인터 방지
return 0;
}
댕글링 포인터 (Wild Pointer)
초기화되지 않았거나, 해제된 메모리를 가리키는 포인터는 심각한 오류를 유발할 수 있다.
#include <stdio.h>
#include <string.h>
int main() {
char *p; // 초기화되지 않은 포인터
strcpy(p, "hello"); // 예측 불가능한 메모리에 쓰기 시도 -> 오류
char *q;
*q = 'A'; // 마찬가지로 위험한 접근
return 0;
}
#include <stdio.h>
#include <string.h>
int main() {
char source[] = "wel";
char *ptr;
ptr = source;
strcpy(ptr, "welcome"); // source 배열 크기(4바이트)를 초과하는 쓰기 -> 버퍼 오버플로우
return 0;
}