전처리문 관련질문입니다.

하하의 이미지

아래 소스를 보시면 ifndef을 통하여 Makefile의 하나의 target에 대해 한번의

m1.h를 인클루트 하게끔 처리 하였습니다.

헌데 왜.. 에러가 발생하는 건지... ^^...

소스는 다음과 같습니다.

파일이름 m1.c -------------------------------------------------
#include <stdio.h>
#include "m1.h"

int main()
{


        koo();  

        m2();

}
파일이름 m1.c -------------------------------------------------


파일이름 m2.c--------------------------------------------------
#include <stdio.h>
#include "m1.h"

void m2()
{


        koo();


}
파일이름 m2.c--------------------------------------------------


파일이름 m1.h-------------------------------------------------
#ifndef _M1_
#define _M1_


        void koo () {


                printf("ifndef??\n");

        }


        void m2();

#endif
파일이름 m1.h-------------------------------------------------


파일이름 Makefile---------------------------------------------
CC= gcc
CFLAGS= -g

sources= m1.c
targets= test

OBJS =  m2.o 

.c.o:
        $(CC) $(CFLAGS) -c $<

all: $(targets)

$(targets):$(OBJS)
        $(CC) $(CFLAGS) -o $(targets) $(sources) $(OBJS)

clean:
        rm -f $(targets) $(objects) *.core
파일이름 Makefile---------------------------------------------

위 코드에 대한 에러는 다음과 같습니다...

gcc -g -o test m1.c m2.o 
m2.o: In function `koo':
/home/test/m1.h:5: multiple definition of `koo'
/tmp/ccWYKNAe.o:/home/test/m1.h:5: first defined here
collect2: ld returned 1 exit status
make: *** [test] 오류 1

중복 선언이라고 나옵니다. 그런데.. ifndef를 했습니다. ^^;;

무엇을 착각하고 있는지.. 알고 싶습니다..

kkojiband의 이미지

그냥 설명 드리는 것 보다 눈으로 직접 보시는게 확실히 이해가 가실껍니다...

gcc -E m1.c

gcc -E m2.c

이렇게 해보시면 m1.h 가 포함이 되어있는게 보이실껍니다 이게 바로 프리프로세싱 후의 코드입니다...

이번엔,

gcc -c m1.c

gcc -c m2.c

이렇게 각각 컴파일 한 후,

objdump -x m1.o

objdump -x m2.o

정확한 옵션이 잘 기억이 안나서 그냥 x로 했습니다 위와 같이 확인해보면 둘다에 koo 라는 심볼이 전역으로 선언되어있는게 보이실껍니다...

이 두개의 파일을 링크 시킨다면?? 당연히 그와 같은 에러가 나오겠죠...

이제 졸업이다...사랑하는 SKKULUG 후배들아 안녕~

하하의 이미지

음.. 그렇다면.. ifndef 문을.. 걸어주어 여러 파일에서

하나의 헤더파일에 대한 include 할때의 중복 피하는

방법에 대한 저의 이해는 틀린 것인 가요?

“바람에게도 길은 있다. 나는 비로소 나의 길을 가느니. 길은 언제나 어디에나 있다.”

akbar의 이미지

하하 wrote:
음.. 그렇다면.. ifndef 문을.. 걸어주어 여러 파일에서

하나의 헤더파일에 대한 include 할때의 중복 피하는

방법에 대한 저의 이해는 틀린 것인 가요?

예 틀립니다.

deisys의 이미지

#ifndef ... 으로 여러번의 인클루드는 막을 수 있습니다. 전처리 단계에서는 말이지요.

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

....

이렇게 m1.h 가 두번 인클루드되는건 막을 수있습니다. 하지만,

전처리단계에서의 처리이기 때문에 m1.h 를 인클루드하는 m1.c, m2.c 가 컴파일된 후에는 돌이킬수 없게 되는 것입니다. 이래서 선언만 헤더에 두고, 구현은 따로 저장해서 컴파일한 후 링크해서 쓰라고 하는 것입니다.. ;; 파일로 나눠지는 모듈의 구조를 수정할 필요가 있을 것 같은데요. 좋은 구조란 헷갈릴 여지가 없는 구조입니다.. ;;; - 어쨌든, 헤더파일에 구현을 넣는 것은 별로 좋지 않은 것 같네요. ( 템플릿 구현같은거 빼구요 ; )

... 뭐, 별로 좋은 방법은 아니지만 저 코드를 컴파일/실행해야하는 아주급한 일이 이다면... static void koo() 와 같이 앞에 static 을 붙여 함수의 범위를 파일로 제한해주면 컴파일과 실행이 되기는 됩니다..

하하의 이미지

음... 저도.. 위 코드가.. 적합하지 않다는 건.. 알 수 있지만..

그럼 왜. 위와 같은 코드가 궁금해 졌는지 부터 말씀드리면.. cgi 프로그램을 하던중.. cgi_library.h라는

헤더가 있는데.. 그 헤더 안에는 모든 function이 정의 구현 되어 들어 있습니다. cgi 특성상.. 개개의 페이지

마다 .. 연관성이 없어 하나의 페이지 프로그램 마다 각각 cgi_library.h를 include 하여.. 각각의 페이지 마

다 Makefile을 만들어 사용하고 있어.. 이 부분을 몽땅 모야 그러니깐 모든 페이지(cgi 파일)을 하나의

Makefile에서 돌리면 모든 cgi 페이지가 한번에 생성 되게끔 만들려고 보니.. 위와 같은 문제가 발생했습니다.

그냥 .. cgi_library.h의 function 들을 뽑아 하나의 모듈로 만들어 쓰면 되었지만.. 그 넘의.. 호기심+찜찜

함 으로 인해 이게 왜 그럴까 하고.. 위와 같은 코드를 만들어 확인하던중.. QnA까지 오게 되었습니다.

^^.... 또.. 위 deisys님의 글을 읽다.. 또 하나의 모르는 점이 있어 이글을 다시 올리게 되었습니다..

^^.......

바로

Quote:

static void koo() 와 같이 앞에 static 을 붙여 함수의 범위를 파일로 제한해주면 컴파일과 실행이 되기는 됩니다..

이부분 입니다..

static .... 정복하기 힘들 keyword입니다.... 관련 질문과 게시물을 여지것 봐오지만.. 또한 얘매한

keyword 입니다...

http://bbs.kldp.org/viewtopic.php?t=28565&highlight=static

http://bbs.kldp.org/viewtopic.php?t=27623&highlight=static

등등 읽어 보고 생각 해보면... 최초 하나의 파일에 .. include 될테고..

그 외.. 파일이.. 사용하려면.. scope를 벗어나 사용 할 수 없지 않을까? 생각 되어 집니다..

- - ;;;;;

kkojiband 님께서 .. 답변 주신 것과 같이.. static 을 줬을 때와.. 안줬을때를 objdump 해서

보면 static을 줬을때는 SYMBOL TABLE 에만 koo와 a 가 올라가 있고

static을 뺏을때(에러 발생)는 SYMBOL TABLE과 RELOCATION RECORD FOR

[.text] 영역 모두에 잡혀 있습니다..

그렇담.. 위의 심볼 테이블과 relocation record for[.text] 의 .. 차이점과.. 각각의 영역이 의미하는

바가 무엇인지.. 알려주실 분 계신가요.. - - ;;

그럼 즐거운 하루 되세요..... ^___^;;;;

“바람에게도 길은 있다. 나는 비로소 나의 길을 가느니. 길은 언제나 어디에나 있다.”

deisys의 이미지

Symbol table 은 "심볼의 정보를 가진" 테이블입니다. 함수가 선언되었다면 호출할 수 있으니, 구현이 아직 나타나지 않았다 해도 심볼 테이블에 올라가게 됩니다. 나중에 함수의 구현이 나타나면 그 함수의 주소(상대적이지만요)가 저장되며 호출할때 사용되지요. 함수의 구현부가 파일에 없다면 참조되는 주소를 비워두고 구현이 되어있는 다른 파일과 링크할때 채우게 되지요. 이때 relocation info. 를 사용합니다.

static 키워드가 붙은 함수는 "그 파일에서만" 사용하는 함수입니다. 따라서 relocation 정보 에는 나타날 필요가 없습니다. static 이 붙어있지 않다면 relocation을 고려해주어야 합니다.

static을 붙였을때 어떻게 되는지는 정확히 말하면 m1.c 와 m2.c 에서 부르는 같은 이름의 함수가 사실은 "다른 녀석" 이 됩니다. ... 각자 m1.c , m2.c 라는 범위 안에서 독립적으로 존재하는 함수가 되어버리는 것이지요. 그래서 안에 static int cnt = 0; ... 이런 코드가 들어가면 따로 놀게 됩니다. ;; m2.o 를 만들고 m1.h 를 고치고 m1.c 를 컴파일하면 m1.c, m2.c 에서 부르는 "같은 이름의" 함수는 코드까지 달라져버릴 수 있지요.. ;; 스테이트를 가지고 있는 함수의 경우라면 말 그대로 "컴파일만" 되고 동작은 이상해집니다....

... 내공이 부족해서 더 명쾌하게 설명하지는 못하겠네요. :'( - 사실 설명이 맞는건지도 헷갈리.... ;;

kkojiband의 이미지

심볼 테이블은 모든 심볼들, 전역 혹은 정적 변수나 함수들에 대한 정보들을 가지고 있습니다...

지역변수들은 심볼 테이블에는 존재하지 않습니다...

여기서 재밌는건, 함수 내에서 정적 변수로 선언이 된 것은 objdump 해보시면,

static int abcde;

이렇게 선언이된건 실제로 심볼 테이블에 추가될때는 abcde.0 이런식으로 추가됩니다...

다른 함수에서도 똑같은 이름의 정적 변수를 사용할수도있으니 이렇게 하는게 당연하겠죠?!

여기서 심볼 테이블이 가지고 있는 정보란 그 변수(함수)가 속해있는 섹션과 섹션에서의 오프셋, 크기 그리고 허용 범위(전역이냐 정적이냐) 등이 있습니다...

이렇게해서 링크 과정을 거쳐 여러 목적 코드들이 합쳐지면 각 섹션들이 합체(?)하고 재배치 과정이 일어나겠죠...

그리고 재배치란,

예를 들어,

int a;

int main( void )
{
a = 0;

return 0;
}

이런 코드가 있다면,

이게 완전한 실행 코드가 되기 전에는 a = 0; 부분을 어셈블 한 곳에는 실제 a 의 주소가 들어있는게 아니고 다른 정보(gcc 에선 0)가 들어있습니다...

그리고 .text 섹션에서 a=0 에서의 a 의 주소 부분의 오프셋이 재배치 정보란에 추가가 됩니다...

하...정말 설명 못하네요...-_-;

실제로 간단한 예제 파일 만들어서 한번 보세요...

전역 변수 선언해서 main 에서 전역 변수를 사용하고,

objdump 해서 코드를 살펴보면 전역 변수를 사용한 부분에서 전역 변수 주소가 들어가야할 4byte 에 0으로 채워져있을껍니다...

그리고 재배치 정보란을 보면,

.text 해놓고 위의 전역 변수를 사용한 곳의 오프셋 주소가 있고 옆에 전역 변수 이름이 있을껍니다...

이렇게해놓고 링킹 과정을 거쳐 모든 심볼들에 대해 주소가 확정이 되면 재배치 정보를 보고 예를 들어 위에서 해놓은,

.text 섹션의 오프셋 부분에다가 전역 변수의 확정 주소인 4 byte 를 쓰는겁니다...

설명이 엉성해서 영...--;

가장 확실한건 objdump 를 이용해서 gcc 가 어떤 식으로 심볼들을 다루는지 알아보세요...그게 가장 확실하게 알수있는 방법일듯합니다...

이제 졸업이다...사랑하는 SKKULUG 후배들아 안녕~

cdpark의 이미지

헤더 파일엔 전역변수의 "선언"과 함수의 원형만 넣으세요.

inline 함수도 아닌데 왜 헤더 파일에 함수 본체가 있나요? 물론 막는 방법도 있지만, 굳이 알려드릴 필요는 없어보입니다. 지금 상황은 그런 꽁수를 써야 하는 상황이 아니니깐요.

하하의 이미지

답변 주신 모든 분들께 .. 감사 드립니다 꾸벅.. ^^;

정말 많이 배우고 있습니다.. 다시 한번 감사 드립니다.

“바람에게도 길은 있다. 나는 비로소 나의 길을 가느니. 길은 언제나 어디에나 있다.”

댓글 달기

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
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.