2015년 8월 15일 토요일

C 언어 - 영향 범위

선언과 호출 순서, 영역


함수와 변수 선언의 위치에 따라 호출 할 수 있는 영역이 결정된다.

a = 10; // 컴파일 에러 (지정되지 않은 변수명 a)
int a;

위와 같이 값을 선언하는 부분과 참조하는 부분의 순서가 뒤바뀌면 컴파일 에러가 발생한다.

함수나 변수를 선언할 때는 용도에 따라 아래와 같이 세 영역 중 어디에 선언할지 선택을 해야 한다.
  • 프로젝트 전체에서 참조 (global)
  • 현재 파일에서만 참조 (static)
  • 특정 영역에서만 참조

영역

프로젝트 전체(global) 영역은 다른 소스 파일을 넘나 들며 공유되는 공간이다.
소스 파일 내 영역은 해당 변수가 선언되는 파일 내에서만 공유되는 공간이다.
특정 영역은 { } 로 감싸진 영역 내만 공유되는 공간이다.

C 프로젝트

프로그램이 하는 기능이 많아 질수록 코드는 늘어난다.
소스 파일 하나에 많은 코드를 넣으면 구분하기가 힘들어진다.
그래서 보통 관련도에 따라 소스 코드를 여러 파일로 분산 시킨다.


예제 프로젝트

  • util.h
  • util.c
  • main.c

util.h

#ifndef __UTIL_H__
#define __UTIL_H__

extern void hello(void);

#endif

util.c

#include <stdio.h> #include "util.h"

void hello(void) {
    printf("Hello\n");
}

main.c

#include "util.h"

int main(int argc, char * args[]) {
    hello();
    return 0;
}

컴파일:

$ gcc -o hello util.c main.c

실행:

./hello
Hello

헤더 파일 include

소스 파일 간 공유하고 싶은 변수나 함수 등이 있는 경우 헤더 파일에 변수 선언 또는 함수 선언문을 작성한다.

헤더 파일은 .h 확장자로 저장한다.

#include 문으로 헤더 파일을 참조할 수 있다.

#include "" 와 #include <> 탐색 순서 차이

  • #include "" 는 같은 위치의 header 파일을 먼저 탐색
  • #include <> 는 컴파일러에 -I 옵션에 지정한 경로를 먼저 탐색


위 예제에서 util.h 는 자신이 만든 헤더 파일이고 같은 경로에 위치 시켰으므로 "" 로 감싸 #include "util.h" 로 작성하였다.

stdio.h 는 표준 라이브러리로 아직 어디인지는 살펴보지 않았지만 현재 자신의 프로젝트 밖에 존재하므로 #include <stdio.h> 로 작성하였다.

헤더 중복 방지 처리


컴파일러는 같은 헤더 파일이 여러 번 참조되는지 검사하지 않고 #include 문을 만나면 무조건 읽어 들인다.

함수 선언문이나 변수 선언문이 중복되면 컴파일 에러가 발생한다.

이를 방지하기 위해 아래와 같이 전처리기를 사용한다.

#ifndef __UTIL_H__
#define __UTIL_H__

...

#endif

#ifndef ~ #endif 문

#ifndef 는 값이 define 되어 있는지 검사하여 define 되어 있지 않다면 #endif 문까지 진행한다.
아니면 #endif 문 다음으로 진행한다.

if not defined 의 줄임말

#ifndef __UTIL_H__ // __UTIL_H__ 가 아직 define 되지 않았으면 이 헤더 파일은 처음으로 읽힘
#define __UTIL_H__ // __UTIL_H__ 를 define 하여 다음 번 #ifndef __UTIL_H__ 검사에서 제외되도록 처리

extern void hello(); // 이 코드는 한번만 수행됨

#endif // #ifndef 문 끝

중복되지 않는 define 값

주의할 점은 define 값은 다른 파일과 중복이 되지 않도록 지정해야 한다.
이름을 짓는 일은 나름 스트레스 이기 때문에 보통 자신만의 규칙을 정하기도 한다.
일반적으로 파일명 변형하는 방법을 사용한다.
util.h -> __UTIL_H__ 또는 __UTIL_H

미리 자신만의 방식을 정해두는 게 좋다.

프로젝트 전체에서 참조 (global)

선언부와 구현부가 다른 파일들로 나뉜다.

선언부는 헤더 파일에 구현부는 소스 파일에 작성한다.

위 예제를 보면 선언문은 util.h 파일에 있고 구현부는 util.c 파일에 존재한다.

extern 함수 선언문:
extern void hello();

extern 함수 구현문:
void hello() {
    printf("Hello");
}

extern 은 컴파일러에게 해당 함수나 변수가 구현부 외 다른 소스 파일에서도 참조될 수 있게 global 영역에 변수를 선언하라는 의미이다.

extern 변수 선언문: 헤더 파일
extern int global_value;

extern 변수 선언문: 소스 파일
int global_value;

extern 변수는 소스 파일에 extern 을 제외한 선언문을 한번 더 써 줘야 한다.

현재 파일 내에서만 참조 (static)

소스 파일 내에서 선언: 다른 소스 파일에서 참조할 수 없음
static int a;


현재 영역에서만 참조

{
    int a;
}


복합 예제


util.h

#ifndef __UTIL_H__
#define __UTIL_H__

extern int global_number; // global variable

extern void global_hello(); // global function

#endif


util.c

#include <stdio.h>
#include "util.h"

int global_number = 0;

void global_hello() {
    printf("Global Hello\n");
}

main.c

#include <stdio.h>
#include "util.h"

static int static_number;
static void static_hello();

void static_hello() {
    printf("Static Hello\n");
    printf("Static Number: %d\n", static_number);
}

int main(int argc, char * args[]) {

    printf("Global Number: %d\n", global_number);

    global_hello(); // global function
    static_hello(); // static function

    {
        int local_number = 100;
        printf("Local Number: %d\n", local_number);
    }

    // local_number 는 여기서 사용할 수 없음
}


댓글 없음:

댓글 쓰기