컴파일 시 헤더파일 중복으로 에러가 나는 경우우

bakdorazi의 이미지

c프로그램을 짜다 보면 종종 #include nex_port.h 를 했는데도 컴파일 시 nex_port.h에 정의된 함수를 찾을 수 없다는 에러를 종종 보기도 합니다.
그때는 #include nex_port.c 를 해주면 거의 모든 에러가 클리어 되던데요...
그런 걸 남용하다가 아래와 같은 문제가 생겼네요...
아래는 software.c 라는 소스입니다.
아래는 software.c 가 참조하고 있는 헤더파일들입니다.
여기에서 몇 개의 헤더들은 위에서 말한 바와 같이 헤더를 인클루드 했는데도 헤더에 정의 된 함수를 찾을 수 없다는 에러 때문에 본체가 정의된 *.c 소스까지 include 되어 있습니다.
예를 들자면 nex_image.c 나 nex_config.c 같은 것들인데요..

software.c

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <stddef.h>
#include <sys/types.h>
#include <unistd.h>
#include <dirent.h>
 
#include <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-includes.h>
#include <net-snmp/agent/net-snmp-agent-includes.h>
#include <semaphore.h>
#include "common.h"
#include "nex_common.h"
#include "nex_config.h"
#include "nex_image.h"
#include "software.h"
#include "nex_pme.h"
#include "nex_image.c"
#include "nex_config.c"

여기서 참조된 nex_config.c 를 한 번 보겠습니다.

nex_config.c

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <time.h>
#include <netinet/in.h>
 
#include "common.h"
#include "iocmd.h"
 
#include "nex_common.h"
#include "nex_config.h"
#include "nex_system.h"
#include "nex_system.c"
#include "nex_interface.h"
#include "nex_password.h"
#include "nex_port.h"
#include "nex_pme.h"
#include "nex_qos.h"
#include "nex_pme.c"
#include "nex_port.c"
#include "nex_efmoam.c"
#include "nex_igmp.c"
#include "nex_vlan.c"
#include "nex_http.h"
#include "nex_snmp.h"
#include "nex_image.h"
#include "nex_vlan.h"
#include "nex_filter.h"
#include "nex_dtag.h"
#include "nex_efmoam.h"
#include "nex_sntp.h"
#include "nex_log.h"
#include "nex_tm.h"
#include "nex_module.h"
#include "nex_igmp.h"
#include "nex_cpe.c"
#include "bdFlashCommon.h"
#include "crc32.h"

nex_config.c 역시도 함수 본체가 정의된 *.c가 많이 포함되어 있습니다.
nex_port.c, nex_vlan.c, nex_cpe.c 등등이 있겠는데요.
여기서 nex_port.c 를 한 번 보도록 하겠습니다.

nex_port.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
 
#include "common.h"
#include "iocmd.h"
 
#include "nex_common.h"
#include "nex_system.h"
#include "nex_port.h"
#include "nex_config.h"
#include "nex_pme.h"
#include "nex_qos.h"
#include "nex_vlan.h"
#include "nex_common.c"
#include "nex_module.h"
#include "nex_module.c"

여기에도 역시 함수 본체가 정의된 *.c 파일이 많이 있습니다.

문제는 맨 위의 software.c를 컴파일 하면 아래와 같은 에러가 나온다는 겁니다.

running: ppc_82xx-gcc  -g -O2 -Dlinux  -I. -I/home/test/install/usr/include -o software netsnmptmp.18014.c  -pthread software.c  -L/home/test/install/usr/lib -lnetsnmpmibs -lnetsnmpagent -lnetsnmphelpers -lnetsnmp -ldl -lcrypto -lm    
In file included from nex_port.c:21,
                 from nex_config.c:25,
                 from software.c:25:
nex_common.c:25: error: redefinition of 'nexGetFromFile'
nex_common.c:25: error: previous definition of 'nexGetFromFile' was here
nex_common.c:49: error: redefinition of 'nexSetToFile'
nex_common.c:49: error: previous definition of 'nexSetToFile' was here
nex_common.c:85: error: redefinition of 'nexGetFromFileConf'
nex_common.c:85: error: previous definition of 'nexGetFromFileConf' was here
nex_common.c:109: error: redefinition of 'nexSetToFileConf'
nex_common.c:109: error: previous definition of 'nexSetToFileConf' was here
nex_common.c:146: error: redefinition of 'nexAddToFileConf'
nex_common.c:146: error: previous definition of 'nexAddToFileConf' was here
nex_common.c:163: error: redefinition of 'nexRunCmd'
nex_common.c:163: error: previous definition of 'nexRunCmd' was here
nex_common.c:242: error: redefinition of 'nexRunCmdBack'
nex_common.c:242: error: previous definition of 'nexRunCmdBack' was here
nex_common.c:283: error: redefinition of 'nexExecCmd'
nex_common.c:283: error: previous definition of 'nexExecCmd' was here
nex_common.c:294: error: redefinition of 'nex_xrealloc'
nex_common.c:294: error: previous definition of 'nex_xrealloc' was here
nex_common.c:304: error: redefinition of 'find_pid_by_name'
nex_common.c:304: error: previous definition of 'find_pid_by_name' was here
nex_common.c:372: error: redefinition of 'nexGetPid'
nex_common.c:372: error: previous definition of 'nexGetPid' was here
nex_common.c:383: error: redefinition of 'find_pid'
nex_common.c:383: error: previous definition of 'find_pid' was here
nex_common.c:396: error: redefinition of 'isnumber'
nex_common.c:396: error: previous definition of 'isnumber' was here
nex_common.c:406: error: redefinition of 'cksum16'
nex_common.c:406: error: previous definition of 'cksum16' was here
nex_common.c:418: error: redefinition of 'nexcmp'
nex_common.c:418: error: previous definition of 'nexcmp' was here
nex_common.c:437: error: redefinition of 'nexncmp'
nex_common.c:437: error: previous definition of 'nexncmp' was here
In file included from nex_port.c:23,
                 from nex_config.c:25,
                 from software.c:25:
nex_module.c:27: error: redefinition of 'nexFindValue'
nex_module.c:27: error: previous definition of 'nexFindValue' was here
nex_module.c:59: error: redefinition of 'nexGetModuleState'
nex_module.c:59: error: previous definition of 'nexGetModuleState' was here
nex_module.c:95: error: redefinition of 'nexSendModuleCommand'
nex_module.c:95: error: previous definition of 'nexSendModuleCommand' was here
nex_module.c:173: error: redefinition of 'nexGetModuleBoardType'
nex_module.c:173: error: previous definition of 'nexGetModuleBoardType' was here
nex_module.c:183: error: redefinition of 'nexSetModuleBoardType'
nex_module.c:183: error: previous definition of 'nexSetModuleBoardType' was here
nex_module.c:198: error: redefinition of 'nexGetModuleInfo'
nex_module.c:198: error: previous definition of 'nexGetModuleInfo' was here
nex_module.c:228: error: redefinition of 'nexSetModuleDeviceType'
nex_module.c:228: error: previous definition of 'nexSetModuleDeviceType' was here
nex_module.c:252: error: redefinition of 'nexSetModuleDeviceMode'
nex_module.c:252: error: previous definition of 'nexSetModuleDeviceMode' was here
nex_module.c:276: error: redefinition of 'nexSetModuleOperMode'
nex_module.c:276: error: previous definition of 'nexSetModuleOperMode' was here
nex_module.c:300: error: redefinition of 'nexSetModuleDesc'
nex_module.c:300: error: previous definition of 'nexSetModuleDesc' was here
nex_module.c:321: error: redefinition of 'nexGetConfigModule'
nex_module.c:321: error: previous definition of 'nexGetConfigModule' was here
nex_module.c:331: error: redefinition of 'nexBuildConfigModule'
nex_module.c:331: error: previous definition of 'nexBuildConfigModule' was here
removing the tmporary code file: netsnmptmp.18014.c

에러의 요지는 함수들이 중복 선언되었다는 것입니다.
그렇다고 위의 소스들에서 중복되는 부분의 헤더를 지울수는 없습니다. 그들은 다른 소스에서도 참조하고 있고
인클루드된된 헤더를 지우면 "함수를 찾을 수 없다"는 에러가 반드시 나오기 때문이죠...

제가 길게 설명했는데.. 문제의 요지가 잘 전달되었을 지 의아하군요...

부디 이런 문제를 격으신 분이 있거나 해결책을 알고 계신 분이 계시면 도움을 좀 부탁드립니다.

그럼 오늘도 좋은 하루 보내세요.

semmal의 이미지

애초에 include로 c파일을 부른 것 자체가 잘못입니다.

정의된 함수가 header에 선언되지 않았다면, header를 고치면 될 일이고,

만약 어떤 이유로 header를 고쳐서는 안된다면, 따로 header를 만들거나, 그 함수를 사용하는 모듈에서 선언해서 쓰면 될 일입니다.
------------------------------
How many legs does a dog have?

------------------------------
How many legs does a dog have?

bakdorazi의 이미지

해더에는 분명히 함수가 선언되어 있어요. 근데 컴파일 하면 함수를 못찼겠다고 에러가 나온다는 겁니다.
그래서 어쩔 수 없이 c 파일을 인클루드 시켜봤는데 에러가 클리어되더라는 거죠...

이번 에러가 난 건에 대해서는 말씀하신 대로 필요한 함수를 "사용하는 모듈"로 카피해서 사용할 수도 있는데
그럴 경우 그 함수가 참조하는 다른 함수들을 사용하기 위해서 또 다른 헤더들을 인크루드 시켜야 되고
해더 인클루드로 해결되면 되는데 함수를 정의한 함수의 해더를 인클루드 시켰는데도 그 함수 못찾겠다고 에러가 나면
어쩔 수 없이 c 파일을 incldue 시킬 수 밖에 없다는 겁니다...

예를 들면

1. nex_port.h 에 port_init가 정의되어 있습니다.
2. software.c 에서 include nex_port 해서 port_init를 쓰려고 합니다.
3. software를 컴파일하면 port_init가 정의되지 않은 함수라고 나옵니다.

경우1
1. software.c 에 #include nex_port.c 를 해줍니다.
2. 에러가 클리어 됩니다.

경우2
1. software.c에 port_init함수를 선언하고 그 함수가 사용하고 있는 다른 함수들을 사용하기 위해서 다른 해더들을 인클루드 합니다.
2. software.c에 port_init 함수를 nex_port에서 복사해서 붙입니다.
3. 컴파일 합니다.
4. port_init가 참조하는 함수들을 사용하기 위해서 인클루드한 헤더에서 에러가 나옵니다.
5. 살펴보니 인클루드 한 헤더에 이미 정의되어 있는 함수들인데 또 정의가 안되었다고 에러를 뿜습니다.
6. 다시 그 헤더의 c 함수를 인클루드 합니다.
7. 컴파일 하니 빛의 속도로 화면이 넘어가면서 에러가 나옵니다.

경우2는 님이 말씀하신 것을 토대로 한 번 해본 겁니다.
그런데도... 역시나 문제가 있네요...
에고... 제가 뭔가 잘못한 건가요?
다른 방법이 있으시면 말씀 좀 해주세요...

semmal의 이미지

일단 함수를 왜 못찾는지부터 알아야겠죠.
이 이유를 찾지 않고 그저 에러를 없애려고만 하면 근본적인 문제가 해결이 안되잖아요.
static으로 정의한 건지, 또다른 이유가 있는지, header에서 define이 꼬이는지 다 따져봐야하는거 아닌가요?
정석을 쓰지않고, 쉽게 돌아갈 방법을 찾으니 나중에 답이 안나올 수 밖에 없지요.

그리고 port_init함수를 복사하는 건 제가 말한적 없습니다. 정의가 아니고 선언입니다. definition이 아니라 declaration요. 개인적인 생각으로, 중복된 정의는 include로 c를 불러오는 것 보다 더 나쁜(?)짓입니다.
그냥 선언만 제대로 하면 되는데 복사는 해서도 안되고 할 필요도 없지요.

경 3
1. 왜 port_init 함수를 못찾는지 알아봅니다.
2. port_init 함수가 제대로 선언(또는 정의)되도록 수정합니다.
3. 서로 다른 라이브러리가 같은 이름을 쓰거나 define 구문때문에 충돌이 나는 등, 피치 못할 사정이 있다면, nex_port.h를 분리하고(또는 분리하거나) software.c를 분리합니다.
4. 구현 문제로 차마 분리하지 못할 경우라면, wrapper.h/c 를 만들어서 한번 더 싸서 분리해줍니다.

에러 메시지를 없애는게 급선무가 아니고, 에러가 왜 나는지 찾는게 급선무입니다.
------------------------------
How many legs does a dog have?

------------------------------
How many legs does a dog have?

ymir의 이미지

include 해서 사용하는 소스코드는 그 자체로 paste 된 것과 동일하게 사용되기 때문에..
굳이 헤더를 include 할 필요는 없습니다.

위 오류는 모두 중복선언과 관련되 내용인데..

header 를 여러 헤더에서 include 시에 중복 선언 등의 에러가 나는 걸 방지하려면..
#ifndef _A_HEADER_H__
#define _A_HEADER_H__
.. contents of a.h
#endif /* _A_HEADER_H__ */

와 같이, #ifdef 로 감싸 주면 됩니다.

그리고 예시에서 undefined symbol 이 뜨는 경우라면...
port_init 의 implementation 이 있는 object file 을 찾지 못했을 경우일겁니다.
예를 들면 port_init 의 구현이 nex_port.c 에 있는데..
얘를 포함하여 빌드하지 않고, 다른 소스에서 단지 header 만 include 한 경우라는 뜻입니다.
컴파일은 되지만 linker 가 port_init 을 못찾겠다고 툴툴댈겁니다.

코드를 싹 뜯어내서, 헤더와 코드를 분리하고...
include 대신 모듈 컴파일을 하는것이 좋을 것 같네요.

되면 한다! / feel no sorrow, feel no pain, feel no hurt, there's nothing gained.. only love will then remain.. 『 Mizz 』

되면 한다! / feel no sorrow, feel no pain, feel no hurt, there's nothing gained.. only love will then remain.. 『 Mizz 』

익명 사용자의 이미지

감사

나그네나그네의 이미지

비슷한 경우로 C++에서 class를 못 찾는 에러가 있습니다.

예를 들어,
---- A.h ----

#ifndef A_H
#define A_H

#include"B.h"
class A{
B b;
}
#endif

---- B.h ----

#ifndef B_H
#define B_H

#include"A.h"
class B{
A a;
}
#endif

위와 같은 상황이 있을 때, B.h에서 class A가 없다는 컴파일 에러가 발생하는 경우가 있습니다.

이 때, B.h를

#ifndef B_H
#define B_H

#include"A.h"
class A;
class B{
A a;
}
#endif

와 같이 고치면, class A; 구문이 A.h 의 A class를 찾아가기에 컴파일 타임과 런타임 둘 다 정상적으로 작동합니다.

댓글 달기

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