gcc에서 #include와 함수 선언에 관한 개념이 안 잡히는 군요.

RedPain의 이미지

int main()
{
    printf( "test\n" );
    return 0;
}

이라는 #include <stdio.h>가 빠진 test.c라는 파일을 만든 후

# gcc -o test test.c
라고 컴파일 하면 컴파일도 잘되고 실행도 잘 됩니다.

int main()
{
    func();
    return 0;
}

라는 test1.c라는 파일과
#include <stdio.h>

void func()
{
    printf( "func\n" );
}

라는 test2.c라는 파일을 만든 후

# gcc -c test1.c
# gcc -c test2.c
# gcc -o test test1.o test2.o
를 하면 또한 컴파일도 잘 되고 실행도 잘 됩니다.
여기서 의문이 드는 것이 이런 식으로만 한다면 헤더파일이라는 것이 필요가 없지 않나요?

#ifndef TEST2_H
#define TEST2_H

void func();

#endif

라는 test2.h가 필요한 줄 알았는 데 그게 아닌 것 같군요.
헤더파일에 함수 선언만 해두는 것이 아니지만 다른 것도 이와같이 헤더파일 없이도 쉽게 소스파일을 나누어서 컴파일할 수 있을 듯하군요.
그리고 헤더파일을 어떻게들 사용하시는 지도 궁금합니다.
blackmir의 이미지

gcc -Wall 로 컴파일을 하면 Warning이 뜹니다.

[blackmir@doit]~> gcc -Wall a.c 
a.c: In function `main':
a.c:4: warning: implicit declaration of function `printf'
a.c:5: warning: control reaches end of non-void function
[blackmir@doit]~> ./a.out 
hello?[blackmir@doit]~> 

흠.. gcc가 내부적으로 뭔가 하는게 아닐까요?

아니면 compile시에 gcc가 일단 printf에 대한 선언을 해보고 link시에 있는지 없는지 확인하는 것도 같습니다.

PS. 아 답변이 많이 부족하군요.. :twisted: 켁.

ssay의 이미지

RedPain wrote:

int main()
{
    printf( "test\n" );
    return 0;
}

이라는 #include <stdio.h>가 빠진 test.c라는 파일을 만든 후

# gcc -o test test.c
라고 컴파일 하면 컴파일도 잘되고 실행도 잘 됩니다.

int main()
{
    func();
    return 0;
}

라는 test1.c라는 파일과
#include <stdio.h>

void func()
{
    printf( "func\n" );
}

라는 test2.c라는 파일을 만든 후

# gcc -c test1.c
# gcc -c test2.c
# gcc -o test test1.o test2.o
를 하면 또한 컴파일도 잘 되고 실행도 잘 됩니다.


-> 필요합니다. 이 같은 경우에는 printf에 대한 정의를 test2.o에서 가져오는 것입니다. test.c의 경우 printf의 정의가 GCC에서 내부적으로 미리 가져오는 헤더에 있는 경우라 생각합니다. 특수한 경우라는 거지요.
하지만 그렇지 않는 경우에는 반드시 헤더를 통해서 알려주어야 합니다.
Quote:

여기서 의문이 드는 것이 이런 식으로만 한다면 헤더파일이라는 것이 필요가 없지 않나요?
#ifndef TEST2_H
#define TEST2_H

void func();

#endif

라는 test2.h가 필요한 줄 알았는 데 그게 아닌 것 같군요.

-> 이 부분은 중복선언 방지를 위한 프로그래밍의 한 방법입니다. 나누어 컴파일하는 프로그래밍 방식에서는 꼭 필요한 방법이지요.
Quote:

헤더파일에 함수 선언만 해두는 것이 아니지만 다른 것도 이와같이 헤더파일 없이도 쉽게 소스파일을 나누어서 컴파일할 수 있을 듯하군요.
그리고 헤더파일을 어떻게들 사용하시는 지도 궁금합니다.

헤더파일은 각 소스 파일에 있는 함수및 변수들의 참조를 위한 정보를 포함하는 파일입니다. 소스파일을 나누어 컴파일하는 경우라면 다른 소스파일에 있는 함수및 변수를 적어도 하나쯤은 사용할 것입니다. 그렇기에
#ifndef TEST2_H
#define TEST2_H

함수 목록 및 변수,상수 목록

#endif

으로 헤더를 작성하고 각 소스파일에서 이 헤더파일을 포함하게 하는 것입니다.

적절한 답이 되는지 잘 모르겠지만 기초적인 내용은 포함했다고 생각합니다.
잘 모르시겠다면 다시 질문해 주세요.

arimae의 이미지

gcc 에서는 기본적으로 표준 라이브러리는 자동적으로 링크를 합니다.
따라서 printf 같은 표준 라이브러리는 헤더 파일이 없어도 알아서 링크를 합니다.
단지 헤더 파일이 없을 경우 return 값을 알 수 없으므로 이것을 int 로 가정합니다.

int main() 
{ 
    printf( "test\n" ); 
    return 0; 
} 

위의 경우 printf는 return 값을 알수 없으므로 int로 가정합니다.
위의 경우 원래 printf의 return 값이 int 이므로 별 문제 없지만 return 값이 int 가 아닌 경우 문제가 발생할 수 있습니다.

그리고 헤더 파일을 제공할 경우 인자에 대한 type checking 을 하 수 있습니다.

예를 들어

int main() 
{ 
    printf( 'c' ); 
    return 0; 
} 

위의 코드의 경우 헤더 파일이 없으므로 컴파일러가 printf의 인자 형을 알지 못하여 아무 에러 없이 컴파일이 됩니다.

하지만,

#include <stdio.h>

int main() 
{ 
    printf( 'c' ); 
    return 0; 
} 

[arimae@ts arimae]$ gcc -c test5.c
test5.c: In function `main':
test5.c:5: warning: passing arg 1 of `printf' makes pointer from integer without a cast

위의 경우 stdio.h 를 포함함으로써 인자의 형태를 정확히 알고 있으므로 위와 같이 warning을 발생시킵니다

printf 에 헤더 파일을 포함하지 않았을때 에러 없이 컴파일 되는 것은 gcc가 표준 라이브러리를 자동적으로 링크하여 printf 함수에 대한 정보를 얻을 수 있기 때문에 에러가 발생하지 않는 것입니다.

printf 가 아닌 자체 제작한 함수를 이용해 테스트 해보도록 하겠습니다..

test5.c

int main(void)
{
    func1();
    return 0;
}

test6.c

int func1(void)
{
    printf("here\n");
    return 0;
}

위의 경우 test5.c 에서 test6.c 에 있는 func1 을 호출합니다.
이 경우 test5.c 어떤 include 도 하지 않았습니다. (위에서 printf 를 호출하면서 stdio.h 를 include 하지 않은것 처럼)

이것을 각자 컴파일 해보고.. 링크를 해보면..

[arimae@ts arimae]$ gcc -c test5.c
[arimae@ts arimae]$ gcc -c test6.c 
[arimae@ts arimae]$ gcc -o test test5.o test6.o
[arimae@ts arimae]$ ./test
here
[arimae@ts arimae]$ 

위와 같이 아무런 에러도 나지 않고 정상적으로 컴파일이 되며 실행도 정상적으로 됩니다.

이렇듯 include 파일은 어떤 함수의 존재를 찾기 위한 필수가 아닌 강력한 타입 체킹을 위한 한 조건이라고 할 수 있습니다.

함수를 호출할때 다른 파일에서 그 함수에 대한 정보를 찾을 수 있다면 (위의 test5.c test6.c 에서처럼) include 로 해당 함수에 대한 정보를 제공하지 않아도 컴파일러에서 알아서 찾아서 링크를 해줍니다.

하지만 맨 위에서 실험을 했듯이, 정확한 타입 체킹을 위해서라면 반드시 해당 함수에 대한 선언을 헤더 파일로 제공하여 include 해줘야 할 것입니다.

p.s. math.h 에 있는 sin을 이용해서 테스트를 하려니 built-in function 이라고 하면서 경고를 내는군요. sin은 gcc 내부에서 이미 type information을 가지고 있는것이 아닐까 추측해봅니다.

Dream, Passion and Challenge..

김충길의 이미지

헤더 파일에는 함수의 프로토타입, 상수, 전역변수등을 선언하는 역할을
합니다. 물론 데이타 타입을 정의하기도 하고요.

예를 든시 printf 함수 사용예는 단순하기 때문에 문제가 되지 않지만
구조체를 파라메터로 받거나 하는 것들은 반드시 사용전에 선언이
필요합니다. 그래서 파라메터 값이 제대로 넘겨집니다.

컴파일러가 선언이 되지 않은 함수를 만나면 일단 리턴타입을 디폴트로
int로 가정하고 파라메터들의 타입은 해당 변수나 상수의 그 타입으로
결정짓습니다. 그리고 실제 함수가 정의된 부분에 가서(이경우 같은 소스파일의)
뒤에 위치해 있다는 가정이죠) 리턴타입이나 파라메터 갯수나 타입이 틀린경우
에러로 여깁니다.

헤더파일을 사용하는 이유중의 하나는 함수를 제대로 호출했는가도 체크해주는
역할을 해줍니다. 포인터 타입을 사용해야 곳에 정수형 타입을 사용한 경우
처럼 말이죠.

일반적으로 함수를 정의하고 선언하는 데 있어서 일단 함수를 사용전에
그 함수의 원형(프로토타입)을 선언(헤더파일을 인클루드하면 되죠)하여 사용을 체크하게 하는것이 졍형입니다.

C언어는 타입체크가 심하지 않지만 C++은 체크가 심합니다. c++에서는
헤더는 웬만해서는 include해줘야 할겁니다.

screen + vim + ctags 좋아요~

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.