programing

C 헤더 문제: #include 및 "정의되지 않은 참조"

oldcodes 2023. 8. 17. 21:52
반응형

C 헤더 문제: #include 및 "정의되지 않은 참조"

세 개의 파일을 가지고 있습니다main.c,hello_world.c,그리고.hello_world.h어떤 이유에서인지 간에 그들이 잘 정리되지 않은 것 같고, 왜 그런지 정말 모르겠어요...

여기 제 소스 파일들이 있습니다.첫 번째 hello_world.c:

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

int hello_world(void) {
  printf("Hello, Stack Overflow!\n");
  return 0;
}

그러면 hello_world.h, 단순:

int hello_world(void);

그리고 마지막으로 main.c:

#include "hello_world.h"

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

GCC에 입력하면 다음과 같습니다.

cc main.c -o main/tmp/ccSRLvFl.o: 'main' 기능에서:main.c:(.text+0x5):'hello_world'에 대한 정의되지 않은 참조collect2: ld가 1개의 종료 상태를 반환했습니다.make: *** [main] 오류 1
gcc main.c hello_world.c -o main

또한 항상 헤더 가드를 사용합니다.

#ifndef HELLO_WORLD_H
#define HELLO_WORLD_H

/* header file contents go here */

#endif /* HELLO_WORLD_H */

컴파일에 hello_world.c 파일을 포함하지 않습니다.사용:

gcc hello_world.c main.c  -o main

hello_world.c에 대해 링크하고 있지 않습니다.

이를 위한 쉬운 방법은 다음 컴파일 명령을 실행하는 것입니다.

cc -o main main.c hello_world.c

더 복잡한 프로젝트에서는 빌드 스크립트를 사용하거나 컴파일 및 연결 명령을 구분하는 파일을 만드는 경우가 많지만, 작은 프로젝트에서는 위의 명령(두 단계를 모두 결합)을 사용하면 됩니다.

두 번째 .c 파일인 hello_world.c에서 컴파일한 개체 파일을 main.o 파일과 연결해야 합니다.

사용해 보십시오.

cc -c main.c
cc -c hello_world.c
cc *.o -o hello_world

지금은 번역 단위의 개념에 대해 배울 수 있는 좋은 시간입니다.

컴파일러는 한 번에 하나의 번역 단위만 처리합니다.그것은 다른 가능한 번역 단위에 대해 아무것도 알지 못할 것이고, 모든 번역 단위를 함께 넣는 것이 링커의 일입니다.

프로그램을 빌드하는 데 사용하는 명령:

gcc main.c -o out

두 개의 번역 단위 중 하나인 번역 단위를 컴파일하고 연결을 시도합니다.main.c 단위역음의 번역 .hello_world.c전혀 사용되지 않습니다.

프런트 엔드 프로그램의 소스 파일을 모두 전달해야 합니다.gcc를 모두 하는 것은 두가구연결니다합고축이하지를▁to다니.

gcc main.c hello_world.c -o out

네, hello_world.c. 링크하는 것을 잊은 것 같습니다.

저는 사용할 것입니다.gcc hello_world.c main.c -o main파일 수가 적으면 이 방법을 사용할 수 있지만 큰 프로젝트에서는 파일 만들기 또는 일부 컴파일 스크립트를 사용하는 것이 좋습니다.

(저는 C 프로그램의 구성 요소를 간단히 살펴본 다음 gcc 호출 뒤에 일반적으로 숨겨져 있는 빌드 단계를 조사합니다.)

C 및 C++와 같은 기존 컴파일 언어는 일반적으로 하나의 "객체 파일"로 하나씩 "컴파일"되는 소스 파일로 구성됩니다.모든 포함 지침이 처리된 후 각 원본 파일은 하나의 "번역 단위"입니다.(따라서 번역 단위는 일반적으로 둘 이상의 파일로 구성되며, 동일한 포함 파일이 일반적으로 둘 이상의 번역 단위에서 발생합니다. 파일과 번역 단위는 엄밀하게 말하자면 n:m 관계입니다.그러나 실용적으로 "번역 단위"는 C 파일이라고 말할 수 있습니다.)

단일 소스 파일을 객체 파일로 컴파일하려면 다음을 통과합니다.-c컴파일러 플래그:

gcc -c myfile.c

이렇게 하면 생성됩니다.myfile.o 아마도 니면아마도아도▁or.myfile.obj같은 디렉토리에 있습니다.

개체 파일에는 컴퓨터 코드와 데이터(및 잠재적으로 디버그 정보)가 포함되어 있지만 여기서는 이를 무시합니다.기계 코드에는 함수가 포함되어 있으며 데이터는 변수의 형태로 제공됩니다.개체 파일의 함수와 변수 모두 "기호"라고 하는 이름을 가집니다.컴파일러는 일반적으로 프로그램에서 변수와 함수 이름을 언더스코어 등을 추가하여 변환하며, C++에서 생성된 ("맹글된") 이름은 형식에 대한 정보와 함수에 대한 매개 변수를 포함합니다.

일부 기호(예: 전역 변수의 이름 및 일반 함수)는 다른 개체 파일에서 사용할 수 있으며 "내보내기"됩니다.

기호는 약간 단순화하면 주소 별칭으로 간주될 수 있습니다.함수의 경우 이름은 점프의 대상 주소에 대한 별칭이고 변수의 경우 이름은 프로그램이 읽을 수 있고 쓸 수 있는 메모리 위치의 주소에 대한 별칭입니다.

help에는 help.c .herpC의 함수에는 기본적으로 "외부 링크"가 있으며, 다른 번역 단위에서 사용할 수 있습니다.이름("기호")을 내보냅니다.

현대 C에서 다른 번역 단위에 정의된 이름을 사용하는 소스 파일은 이름을 선언해야 합니다.이것은 컴파일러에게 그것으로 무엇을 해야 하는지, 그리고 그것이 소스 코드에서 구문적으로 사용될 수 있는 방법(예: 함수 호출, 변수 할당, 배열 색인)을 알려줍니다.컴파일러는 이 "상징 주소"에서 읽거나 "상징 주소"로 점프하는 코드를 생성합니다. 모든 심볼 주소를 "실제" 메모리 위치로 대체하여 점프와 메모리 액세스가 원하는 위치에 도달하도록 하는 것이 링커의 일입니다.

이를 사용하는 파일의 이름(함수, 변수) 선언은 다음과 같이 "수동"일 수 있습니다.void herp();처음 사용하기 전에 파일에 직접 나타납니다.더 할 수 있는 됩니다.helper.h의 "합니다.#include- ing it.여기에는 마법이 없습니다. 포함 지시문은 포함 파일 텍스트를 파일에 직접 작성된 것처럼 삽입하기만 하면 됩니다.정확히 아무런 차이가 없습니다.특히 헤더 파일을 포함하더라도 링커가 해당 소스 파일과 링크하도록 지시하지 않습니다.그 이유는 간단합니다.개체 파일로 컴파일하는 동안 해당 지식이 지워지기 때문에 포함된 파일에 대해 링커가 알지 못합니다.

이것은 당신의 경우에 그것을 의미합니다.help.c. , 는 "링크"입니다. 당신의 경우 컴파일의 코드는main.c.

이루어지는지에 대한 더 . 왜냐하면 이 절차는 인 C 단계를 통합할 때문입니다.gcc -o myprog help.c main.c 데 을 수행합니다.myprog.

우리가 "컴파일러"라고 말할 때, 예를 들어 다음을 참조합니다.gcc우리는 일반적으로 명령 줄에서 명령과 파일을 가져와 소스에서 실행 가능한 프로그램을 생성하는 것과 같이 원하는 결과를 얻기 위해 필요한 단계를 수행하는 "실행 드라이버"를 의미합니다.gcc의 실제 컴파일러는 다음과 같습니다.cc1는 조립 하는데, 파일은 반드시 .as개체 파일에 저장합니다.원본 파일이 컴파일된 후 gcc는 적절한 옵션을 사용하여 링커를 호출하여 실행 파일을 생성합니다.

다음은 단계를 자세히 설명하는 샘플 세션입니다.

$ ls
Makefile  help.c  help.h  main.c

$ /lib/gcc/x86_64-pc-cygwin/7.4.0/cc1 main.c
 main
Analyzing compilation unit
Performing interprocedural optimizations
 <*free_lang_data> <visibility> <build_ssa_passes> <opt_local_passes> <targetclone> <free-inline-summary> <emutls> <whole-program> <inline>Assembling functions:
 <materialize-all-clones> <simdclone> main
Execution times (seconds)
 phase setup             :   0.00 ( 0%) usr   0.00 ( 0%) sys   0.00 (22%) wall    1184 kB (86%) ggc
 TOTAL                 :   0.00             0.00             0.01               1374 kB

$ ls
Makefile  help.c  help.h  main.c  main.s

$ /lib/gcc/x86_64-pc-cygwin/7.4.0/cc1 help.c
 herp
Analyzing compilation unit
Performing interprocedural optimizations
 <*free_lang_data> <visibility> <build_ssa_passes> <opt_local_passes> <targetclone> <free-inline-summary> <emutls> <whole-program> <inline>Assembling functions:
 <materialize-all-clones> <simdclone> herp
Execution times (seconds)
 phase setup             :   0.01 (100%) usr   0.00 ( 0%) sys   0.00 (33%) wall    1184 kB (86%) ggc
 TOTAL                 :   0.01             0.00             0.01               1370 kB

$ ls
Makefile  help.c  help.h  help.s  main.c  main.s

mainhelp두파일이 , 메인으로 객체 할 수 .s◦ 도움s라는 두 개의 어셈블리 파일이 있습니다. 이 파일은 어셈블리어를 사용하여 객체 파일로 어셈블리할 수 있습니다.as하지만 간단히 살펴봅시다.help.s:

$ cat help.s

        .file   "help.c"
        .text
        .globl  some_variable
        .data
        .align 4
some_variable:
        .long   1
        .text
        .globl  herp
        .def    herp;   .scl    2;      .type   32;     .endef
        .seh_proc       herp
herp:
        pushq   %rbp
        .seh_pushreg    %rbp
        movq    %rsp, %rbp
        .seh_setframe   %rbp, 0
        .seh_endprologue
        nop
        popq    %rbp
        ret
        .seh_endproc
        .ident  "GCC: (GNU) 7.4.0"

조립자에 대해 아무것도 모르는 경우에도 기호를 명확하게 식별할 수 있습니다.some_variable그리고.herp어셈블리 레이블입니다.

아 예, 도움말에 변수 정의를 추가한 것을 잊었습니다.c:

$ cat help.c

#include "help.h"
int some_variable = 1;
void herp() {}

을 어셈블리어로 조립할 수 있습니다.as:

$ as main.s -o main.o

$ ls
Makefile  help.c  help.h  help.s  main.c  main.o  main.s

$ as help.s -o help.o

$ ls
Makefile  help.c  help.h  help.o  help.s  main.c  main.o  main.s

이제 두 개의 객체 파일이 있습니다.유틸리티("이름 망글링")를 사용하여 내보냈거나 필요한 기호("외부")를 확인할 수 있습니다.

$ nm --extern-only help.o

0000000000000000 T herp
0000000000000000 D some_variable

$ nm --extern-only main.o
                 U __main
                 U herp

는 코드를 하는 " "는 " (정의되지 "T" 코텍드포 "나스트는" 섹정다니을션냅 "D" 데는섹 "U" 않음지타니냅다나되의를함하이는에타터호음가을션기이있고는을the▁"▁"d타정나냅니u않다▁undefined▁is"▁(▁"음"▁"",▁fortt;지는▁indicates▁the되▁thattext▁"▁section▁symbolun",는__maingcc/또는 cygwin quirk입니다.)

문제의 원인은 다음과 같습니다. 정의되지 않은 기호를 정의하는 개체 파일과 main.o를 쌍으로 구성하지 않으면 링커는 이름을 "해결"할 수 없고 점프를 생성할 수 없습니다.점프 목적지가 없습니다.

이제 두 개체 파일을 실행 파일에 연결할 수 있습니다.Cygwin은 우리에게 Cygwin.dll에 대한 링크를 요구합니다. 상황이 좋지 않아서 죄송합니다.

$ ld main.o help.o /bin/cygwin1.dll -o main

$ ls
Makefile  help.c  help.h  help.o  help.s  main*  main.c  main.o  main.s

이상입니다.프로그램이 제대로 실행되지 않는다는 것을 덧붙여야 합니다.그것은 끝나지 않고 Ctrl-C에 반응하지 않습니다. gcc가 우리에게 제공하는 몇 가지 Gnu 또는 Windows 빌드 복잡성을 놓칠 수 있습니다.

아, 파일을 만드세요.파일을 대상 정의 및 대상종속성으로 구성: A라인

main: help.o main.o

두 .o 파일에 따라 대상 "메인"을 지정합니다.일반적으로 파일 만들기에는 대상을 생성하는 방법을 지정하는 규칙도 포함됩니다.그러나 Make에는 규칙이 내장되어 있습니다. 컴파일러를 호출하여 .c 파일에서 .o 파일을 생성하는 것을 알고 있으며(그리고 자동으로 이 종속성을 고려합니다), 대상이 .o 파일 중 하나와 이름이 같으면 o 파일을 링크하여 대상에 따라 생성하는 것을 알고 있습니다.

따라서 다음과 같은 규칙이 필요하지 않습니다.우리는 단순히 암묵적인 의존성을 정의합니다.프로젝트의 전체 Makefile은 다음과 같이 요약됩니다.

$ cat Makefile

CC=gcc
main: help.o main.o
help.o: help.h
main.o: help.h

CC=gcc사용할 C 컴파일러를 지정합니다.CC는 C 컴파일러를 지정하는 기본 제공 변수입니다(CXX는 C++ 컴파일러를 지정합니다(예: g++).

어디 보자꾸나:

$ make

gcc    -c -o main.o main.c
gcc    -c -o help.o help.c
gcc   main.o help.o   -o main
$ ls
Makefile  help.c  help.h  help.o  main.c  main.exe*  main.o

종속성이 작동합니까?

$ make
make: 'main' is up to date.

$ touch main.c

$ make
gcc    -c -o main.o main.c
gcc   main.o help.o   -o main

$ touch help.h

$ make
gcc    -c -o main.o main.c
gcc    -c -o help.o help.c
gcc   main.o help.o   -o main

좋아 보입니다. 단일 소스 파일을 터치한 후에는 해당 파일만 컴파일하지만 두 파일이 종속된 헤더를 터치하면 둘 다 컴파일됩니다.어떤 경우에도 연결이 필요합니다.

컴파일러에게 프로젝트에 두 개의 소스 파일이 포함되어 있다고 말해야 합니다.

gcc -o out main.c hello_world.c

또한 귀하의 수준에서는 IDE(예: Eclipse CDT)를 사용하여 빌드가 아닌 프로그래밍에 집중할 것을 제안합니다.나중에 여러분은 복잡한 프로젝트를 만드는 방법을 배울 것입니다.하지만 이제 프로그래밍을 배우는 것뿐입니다.

언급URL : https://stackoverflow.com/questions/10357117/c-header-issue-include-and-undefined-reference

반응형