티스토리 뷰
이전 글: 9. 파일 입출력에서 계속
메모리의 구성
- 코드 영역: 실행할 프로그램의 코드가 저장되는 메모리 공간, CPU는 코드 영역에 저장된 명령문들을 하나씩 가져가서 실행한다.
- 데이터 영역: 전역변수와 static으로 선언되는 static 변수가 할당. 프로그램 종료 시까지 남는다.
- 스택 영역: 지역변수와 매개변수가 할당된다. 함수가 종료되면 소멸된다.
- 힙 영역: 프로그래머가 원하는 시점에 변수를 할당하고 소멸하도록 지원하는 공간
malloc 과 free
생성과 소멸 시기가 지역변수나 전역변수와 다른 유형의 변수는,
malloc 과 free 함수로 힙 영역에 할당하고 소멸할 수 있다.
먼저, stdlib.h를 추가해준다.
#include <stdlib.h>
void * malloc(size_t size); // 힙 영역으로 메모리 공간 할당
--> 성공 시 할당된 메모리 주소값, 실패 시 NULL 반환
void free(void * ptr); // 힙 영역에서 메모리 공간 해제
// 아래와 같이 사용한다.
int * ptr1 = (int *)malloc(sizeof(int));
*ptr1 = 20;
malloc이 호출된 개수만큼 free도 호출해줘야 한다.
calloc
void * calloc(size_t elt_count, size_t elt_size); // 블럭 개수만큼 반환
--> 성공 시 할당된 메모리 주소값, 실패 시 NULL 반환
malloc으로 구현 시 아래와 동일
char * ptr2 = (char *)malloc(30, 4);
// 4바이트 크기의 블럭 30개를 힙 영역에 할당해주세요.
realloc
void * realloc(void * ptr, size_t size);
--> 성공 시 새로 할당된 메모리 주소 값, 실패 시 NULL 반환
"ptr이 가리키는 메모리의 크기를 size 크기로 조절해줘"
(아마도, 동적 배열에 사용될 것 같다)
선행처리기(Preprocessor)와 매크로(Macro)
선행처리 = 컴파일 이전의 처리를 의미
소스파일 --> 선행처리기 --> 선행처리를 거친 소스파일 --> 컴파일러 --> 오브젝트 파일 --> 링커 --> 실행파일
선행처리는 대부분 단순 치환의 형태를 띠며, #로 시작한다.
#define PI 3.14
"소스코드에서 PI를 만나면 바로 3.14로 치환하라."
(소스코드가 직접 바뀐다고 생각하면 된다)
#include <stdio.h>
"stdio.h의 파일의 내용을 여기다 옮겨놓아라."
#define: Object-like macro
#define PI 3.14
-------- --- -------
지시자 매크로 매크로 몸체(또는 대체 리스트)
위와 같은 PI를 '오브젝트와 유사한 매크로' 또는 '매크로 상수'라고 한다.
매크로는 대문자로 작성한다.
#define: Function-like macro
매크로는 매개변수가 존재하는 형태로도 정의할 수 있다.
'함수와 유사한 매크로', '매크로 함수'라고도 한다.
#define SQUARE(X) X*X
-------- -------------- ------
이런 패턴 등장 시 이렇게 바꿔라
이렇게 선행처리기에 의해서 변환되는 과정 자체를 가리켜 '매크로 확장(macro expansion)'이라 한다.
!! 주의
선행처리기는 컴파일러 이전에 변경되므로, 매크로 전체를 괄호로 묶어주는 것이 중요하다.
#define SQUARE(X) ((X) * (X))
두 줄로 정의하고자 할 때에는 이스케이프 문자(\)를 사용한다.
또 하나, 먼저 정의된 매크로도 사용가능하다.
#define CIRCLE(R) ( 2 * PI * ( R ) )
매크로의 장단점
장점:
+ 일반 함수에 비해 실행 속도가 빠르다.
스택 메모리 할당 및 인자 전달 등을 거치지 않기 때문이다.
+ 자료형에 따라 별도로 함수를 정의하지 않아도 된다.
자료형에 관계없이 문자 그대로 치환되기 때문이다.
단점:
- 정의하기가 까다롭다.
- 디버깅이 어렵다.
따라서 주로 이런 함수들을 매크로로 정의한다.
- 작은 크기의 함수
- 호출 빈도가 높은 함수
조건부 컴파일
특정 조건에 따라 소스코드의 일부를 삽입하거나 삭제하기 위한 용도
#if FOO
// 매크로 FOO가 참이라면
#endif
#ifdef FOO
// 매크로 FOO가 정의되었다면
#endif
#ifndef FOO
// 매크로 FOO가 정의되지 않았다면
#endif
// 이 매크로는 주로 헤더파일의 중복을 막기 위해 사용된다.
#if FOO == 1
…
#elif FOO == 2
…
#else
…
#endif
매크로 매개변수의 문자 치환
#define STRING_JOB(A, B) #A "'s job is " #B
문자열 내에서 매개변수 치환을 위해선 # 연산자를 사용해야 한다.
#A 는 "A"와 같은 형태로 치환된다.
문자열은 연속해서 나란히 선언하면 하나의 문자열로 간주된다.
예) char * str = "FOO" "BAR" --> "FOOBAR"와 동일
#define CONCAT(A, B, C) A ## B ## C
매개변수의 단순 연결에는 ## 연산자를 사용한다.
CONCAT(10, 2, 030) --> 102030
파일의 분할과 헤더파일 디자인
extern int num; // int형 변수 num이 외부에 선언되어 있다.
extern void increment(void); // 함수가 외부에 정의되어 있다.
void increment(void); // 함수의 경우 생략 가능하다.
static 키워드
static 지역변수 : 변수를 데이터 영역 메모리 공간에 정의한다.
static 전역변수 : 이 변수의 접근 범위는 파일 내부로 제한한다.
즉, 외부 파일에서 접근할 수 없다.
static 함수 : static 전역변수와 동일하다.
#include 지시자
#include "header1.h" // 이 문장 위치에 header1.h 파일의 내용을 가져다 놓으세요.
--> 이 문장을 포함하는 소스 파일 기준에서 상대경로로 찾는다.
#include <stdlib.h> // C가 제공하는 표준 헤더파일에서 찾는다.
헤더파일!
헤더파일에는 중복 정의될 수 있는 내용을 넣는다.
- 매크로 : 매크로의 명령문도 파일 단위로만 유효하기 때문에 헤더 파일에 삽입하는 것이 편리하다.
- 함수 선언 : 외부에서 가져다 쓸 용도의 함수 선언
- 구조체 정의
헤더 파일의 중복 삽입 해결법
구조체의 선언 및 정의 시 헤더파일에 넣는 것은 좋지만, 중복되면 컴파일 에러가 난다.
중복 방지를 위해 조건부 컴파일을 활용한다.
헤더파일: stperson.h
#ifndef __STPERSON_H__
#define __STPERSON_H__
typedef struct
{
int age;
char sex;
char name[20];
} PERSON;
#endif
반응형
댓글
공지사항