헤더파일이란
헤더파일
C언어에서 함수는 먼저 함수의 원형에 대해 선언이 되어 있어야한다.
표준함수 역시 선언이 되어야하는데, 이런 표준 함수의 원형 및 관련 정보가 들어간 파일을 헤더파일이라한다.
헤더파일은 끝에 .h 확장자가 붙으며, 선언시에는 앞에 #include를 사용해야 적용된다.
리눅스 기준으로 헤더파일은 /usr/include 디렉터리 밑에 있다.
헤더파일 VS 라이브러리
그렇다면 라이브러리처럼 또는 Python 처럼 모듈과 같은 것이 아니냐? 할 수 있지만, 엄연히 차이가 있다.
구분 | 헤더파일 | 라이브러리 |
---|---|---|
구성 | 사람이 알아들을 수 있는 C언어 문법으로 작성 | 기계어로 번역된 바이너리 |
역할 | 함수선언, 매크로 정의, 데이터 구조 선언 함수와 변수의 선언부만 포함, 구현부는 없음. |
함수 구현부, 동작코드가 포함된 파일 프로그램 실행 시 링크되어 동작을 수행 |
형식 | .h파일 | 정적 : .lib 또는 .a파일 동적 : .dll파일, .so 파일 |
사용목적 | 코드의 재사용성과 가독성을 높이기 위해 사용 | 코드 중복을 줄이고 효율적으로 재사용하기 위해 사용 |
정적라이브러리 VS 동적라이브러리
구분 | 정적라이브러리 | 동적라이브러리 |
---|---|---|
컴파일 시 동작 | 라이브러리 전체가 실행파일에 복사 | 라이브러리에 대한 참조만 실행파일에 포함 |
실행 시 동작 | 실행파일이 독립적으로 동작(라이브러리 파일 필요X) | 라이브러리를 실행 시점에 로드(라이브러리 파일 필요) |
파일 크기 | 파일이 크다 | 파일이 작다 |
메모리 사용 | 각 실행파일이 라이브러리 복사본을 포함 | 여러 프로그램이 라이브러리를 공유 |
업데이트 용이성 | 어려움(수정 시 다시 컴파일) | 쉽다 |
실행속도 | 빠름(실행 시점에 로드시간이 필요X) | 약간 느림 |
운영체제 의존성 | 낮음 | 운영체제에 따라 경로 설정 등이 필요 |
헤더파일 작성 방법
헤더파일 작성은 크게 어렵지 않다.
앞서 설명한 것처럼 헤더파일은 프로그램에 필요한 함수 선언, 매크로 정의, 데이터 구조 선언 등의 정의, 선언을 해놓은 파일이기 때문에 보통의 코딩을 할때, 선언부만 따로 만든 파일이라 생각하면 된다.
헤더파일 구조
#ifndef HEADER_FILE_NAME // 헤더 가드 시작
#define HEADER_FILE_NAME // 중복해서 헤더를 포함하지 않게 막아주는 역할
.
.
.
#endif // 헤더 가드 종료
- 조건부 컴파일 : 조건에 따라 필요한 문장만 골라서 컴파일하는 것.
- ifndef(if not define) : 만약 정의(선언) 되어있지 않다면 → #ifndef ~ #endif 사이 코드를 컴파일해라
- 만약, 이미 정의가 되어있으면 #define을 넘김 → 즉, 중복해서 헤더를 선언했을 경우 그냥 넘겨서 컴파일에러를 안 일으킨다.
코드 분석 : main.c
//main.c
#include "hacker.h"
int main(){
hacker* qwertyou = new_hacker();
printf("%s's age is %d\n",qwertyou->name, qwertyou->age);
printf("%s's level is %d\n",qwertyou->name, qwertyou->level);
printf("\n\n-----qwertyou's skill list-----\n");
for(int i=0;i<qwertyou->skill_num;i++){
printf("%s's skill%d : %s\n",qwertyou->name, i, qwertyou->skill_list[i]);
}
}
hacker* qwertyou = new_hacker();
- hacker의 자료형 qwertyou라는 이름의 새로운 구조체를 생성
printf("%s's age is %d\n",qwertyou->name, qwertyou->age); printf("%s's level is %d\n",qwertyou->name, qwertyou->level);
- qwertyou의 멤버변수를 출력한다.
-
for(int i=0;i<qwertyou->skill_num;i++){ printf("%s's skill%d : %s\n",qwertyou->name, i, qwertyou->skill_list[i]);
- 동일하게 for 반복문을 써서 qwertyou의 멤버변수를 출력한다.
코드 분석 : hacker.h
//hacker.h
#pragma once
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
typedef struct hacker{ // hacker이라는 이름의 구조체 생성
char name[50]; // 멤버변수 name 배열 생성
unsigned int age; // 멤버변수 age 생성
char** skill_list; // 이중 포인터 skill_list 생성
int skill_num; // 멤버변수 정수형 skill_num 생성
int level; // 멤버변수 정수형 level 생성
}hacker; // alias(별칭)를 지정 : hacker
int input_name(hacker*); // input_name() 함수 선언
int input_age(hacker*); // input_age() 함수 선언
int input_new_skill(hacker*);// input_new_skill() 함수 선언
hacker* new_hacker() { //hacker 객체 생성
hacker* new_ptr = (hacker*)malloc(sizeof(hacker)); //구조체 메모리를 할당(malloc)
memset(new_ptr->name, '\0', sizeof(new_ptr->name)); //이름 초기화
new_ptr->skill_num = 0; //skill_num 0으로 초기화
new_ptr->level = 1; // level 1로 초기화
new_ptr->skill_list = (char**)malloc(sizeof(char*)); //skill_list 동적으로 메모리 할당(malloc)
input_name(new_ptr); // 이름 입력을 받음
input_age(new_ptr); // 나이 입력을 받음
input_new_skill(new_ptr); // 첫번째 스킬을 입력받음
return new_ptr;
}
int input_name(hacker* ptr){
write(1,"Input the name : ",17); // write함수로 표준출력을 사용해서 이름을 입력하라는 메세지 출력
return scanf("%s",ptr->name); // 이름을 입력받아 ptr->name에 저장
}
int input_age(hacker* ptr){
printf("Input the age : ");
return scanf("%u",&ptr->age); // 나이를 입력받아 ptr->age에 저장
}
void increase_size(char***); // increase_size 함수 선언
int input_new_skill(hacker* ptr){
write(1, "Input new Skill : ", 18); // write함수로 표준출력을 이용해 새로운 스킬을 입력하라는 메세지 출력
char buf[50] = {0,}; // buf배열을 0으로 초기화
scanf("%s",buf); // 입력 받아서 buf에 저장
char* tmp = (char*)malloc(strlen(buf)+1); // buf의 크기+1한 사이즈의 동적 메모리를 할당. => strcpy는 \0까지 추가하기 때문에
strcpy(tmp,buf); // buf 내용을 복사해서 tmp에 넣음
ptr->skill_num++; // ptr->skill_num 변수 증가
increase_size(&(ptr->skill_list)); // increase_size 함수 실행
ptr->skill_list[ptr->skill_num-1] = tmp; // tmp변수에 저장된 새로운 스킬을 skill_list 마지막 위치에 저장
return ptr->skill_num;
}
void increase_size(char*** skill_list) {
if (*skill_list == NULL) { // skill_list가 Null이면
*skill_list = (char**)malloc(sizeof(char*)); // 크기 1인 배열을 할당
(*skill_list)[0] = NULL; // skill_list 첫 배열에 Null 삽입
return;
}
int org_size = sizeof(*skill_list) / sizeof(char*); // skill_list 배열의 사이즈 계산
char** new_ptr = (char**)malloc(sizeof(char*) * (org_size + 1)); // skill_list 배열 사이즈에 +1을 한 것을 new_ptr로 동적 메모리할당
for (int i = 0; i < org_size; i++) {
new_ptr[i] = (*skill_list)[i]; // for 반복문을 통해 new_ptr에 기존 skill_list 내용을 복사
}
free(*skill_list); // skill_list를 메모리 해제
*skill_list = new_ptr; // skill_list에 new_ptr 넣기
}
정리하면 다음과 같다.
new_hacker()
- 새로운
hacker
객체를 동적으로 생성하는 함수.skill_list
는 동적으로 메모리 할당(malloc
), 초기 크기는 1.input_name
: 이름을 입력받음.input_age
: 나이를 입력받음.input_new_skill
: 첫 번째 스킬을 입력받음.
- 세 가지 입력 함수:
- 구조체 메모리를 할당 (
malloc
)한 후, 이름을 초기화(memset
), 스킬 개수는 0, 레벨은 1로 초기화.
- 새로운
input_new_skill
- 새로운 스킬을 입력받아
skill_list
에 추가하는 함수.
- 입력받은 문자열을
buf
에 저장. - 동적 메모리 할당을 통해 입력받은 문자열의 복사본(
tmp
) 생성. - 스킬 개수(
skill_num
)를 증가시킨 뒤,increase_size
를 호출해skill_list
의 크기를 확장. - 새로운 스킬을 리스트의 마지막 위치에 추가.
- 새로운 스킬을 입력받아
increase_size()
- 스킬 리스트의 크기를 확장하는 함수.
- 초기 리스트가
NULL
인 경우 크기가 1인 배열을 할당. - 기존 리스트 크기(
org_size
)를 계산. - 새 크기로 메모리를 재할당한 배열 생성.
- 기존 배열의 내용을 새 배열로 복사한 뒤, 기존 배열을 해제하고 새 배열로 교체.
끝.
'STUDY' 카테고리의 다른 글
[1주차 TIL] KnockOn Bootcamp Pre.Rev : 스택 & 큐 (0) | 2024.12.09 |
---|---|
[1주차 TIL] KnockOn Bootcamp Pre.Rev : 연결리스트 (0) | 2024.12.08 |
리눅스 네트워크 및 프로세스 관련 명령어 (6) | 2024.11.14 |
vim 명령어 정리 (2) | 2024.11.14 |
[AI] 특성공학과 규제 (0) | 2022.11.06 |