답변:
C에서 헤더 파일에 함수를 정의하면 해당 헤더 파일을 포함하는 컴파일 된 각 모듈에 해당 함수가 나타나고 함수에 대한 공용 기호가 내보내집니다. 따라서 additup 함수가 header.h에 정의되어 있고 foo.c 및 bar.c에 모두 header.h가 있으면 foo.o 및 bar.o에 additup의 사본이 모두 포함됩니다.
이 두 객체 파일을 함께 연결하면 링커에서 기호 additup이 두 번 이상 정의되어 허용되지 않음을 알 수 있습니다.
함수를 정적으로 선언하면 심볼이 내보내지지 않습니다. 객체 파일 foo.o와 bar.o에는 여전히 함수에 대한 별도의 코드 사본이 포함되어 있으며 사용할 수는 있지만 링커는 함수의 사본을 볼 수 없으므로 함수 사본을 볼 수 없습니다. 불평하지 않습니다. 물론 다른 모듈도이 기능을 볼 수 없습니다. 그리고 프로그램은 동일한 기능의 동일한 사본 두 개로 구성됩니다.
헤더 파일에서만 함수를 선언하고 정의하지 않은 다음 하나의 모듈에서만 정의하면 링커는 함수의 사본 하나를보고 프로그램의 모든 모듈에서이를보고 사용해. 그리고 컴파일 된 프로그램에는 함수의 사본 하나만 포함됩니다.
따라서 C의 헤더 파일에 함수 정의를 가질 수 있습니다. 스타일이 잘못되었거나 형식이 좋지 않으며 모든 것이 좋지 않습니다.
( "declare"는 본문이없는 함수 프로토 타입을 제공한다는 의미이고, "define"은 함수 본문의 실제 코드를 제공하는 것을 의미합니다. 이것은 표준 C 용어입니다.)
#ifndef HEADER_H
가 무엇 을 방지해야합니까?
C와 C ++는 이와 관련하여 거의 동일하게 동작합니다 inline
. 헤더에 함수를 가질 수 있습니다 . C ++에서 본문이 클래스 정의 안에있는 모든 메소드는 암시 적으로 inline
있습니다. C에서 동일한 작업을 수행하려면 함수를 선언하십시오 static inline
.
static inline
"... 함수를 사용하는 각 번역 단위에 여러 개의 함수 사본이 계속 남아 있습니다. static
inline
함수 가 아닌 C ++에서는 사본이 하나만 있습니다. 실제로 C의 헤더에 구현을하려면 1) 구현을 inline
(예 :)로 표시 inline void func(){do_something();}
하고 2) 실제로이 기능은 특정 번역 단위 (예 :)에 있다고 말해야합니다 void func();
.
헤더 파일의 개념에는 약간의 설명이 필요합니다.
컴파일러의 명령 행에 파일을 제공하거나 '#include'를 수행하십시오. 대부분의 컴파일러는 확장자 c, C, cpp, c ++ 등을 가진 명령 파일을 소스 파일로 받아들입니다. 그러나 일반적으로 소스 파일에 대한 임의의 확장명을 사용할 수있는 명령 줄 옵션이 포함되어 있습니다.
일반적으로 명령 행에 제공된 파일은 '소스'이며 포함 된 파일은 '헤더'입니다.
전 처리기 단계는 실제로 모든 것을 취해 모든 것이 하나의 큰 파일처럼 보이도록 만듭니다. 헤더 나 소스에있는 것은 실제로이 시점에서 관련이 없습니다. 일반적으로이 단계의 출력을 표시 할 수있는 컴파일러 옵션이 있습니다.
따라서 컴파일러 명령 행에 제공된 각 파일에 대해 큰 파일이 컴파일러에 제공됩니다. 여기에는 메모리를 차지하거나 다른 파일에서 참조 할 심볼을 만드는 코드 / 데이터가있을 수 있습니다. 이제 이들 각각은 '객체'이미지를 생성합니다. 링커는 서로 연결된 두 개 이상의 객체 파일에서 동일한 심볼이 발견되면 '중복 심볼'을 제공 할 수 있습니다. 아마도 이것이 이유입니다. 객체 파일에 심볼을 생성 할 수있는 헤더 파일에 코드를 넣지 않는 것이 좋습니다.
'인라인'은 일반적으로 인라인됩니다. 그러나 디버깅 할 때는 인라인되지 않을 수 있습니다. 그렇다면 링커는 왜 다중 정의 오류를 제공하지 않습니까? 단순 ... 이들은 '약한'기호이며, 모든 개체의 약한 기호에 대한 모든 데이터 / 코드가 동일한 크기와 내용이라면 링크 된 것이 다른 개체의 복사본 하나와 복사본을 유지합니다. 작동합니다.
C ++ 표준 인용문
C ++ 17 N4659 표준 초안 10.1.6 "인라인 지정자는"그 방법은 암시 적으로 인라인 말합니다 :
4 클래스 정의 내에 정의 된 함수는 인라인 함수입니다.
더 나아가서 인라인 메소드 는 모든 번역 단위에서 정의 될 수있을 뿐만 아니라 정의 되어야한다는 것을 알 수 있습니다.
6 인라인 함수 또는 변수는 그것이 사용되는 모든 번역 단위에 정의되어야하며 모든 경우에 정확히 동일한 정의를 가져야한다 (6.2).
이것은 12.2.1 "멤버 함수"의 노트에 명시 적으로 언급되어 있습니다.
1 멤버 함수는 클래스 정의에서 정의 될 수 있으며 (11.4),이 경우 인라인 멤버 함수입니다 (10.1.6) [...]
3 [참고 : 프로그램에서 인라인이 아닌 멤버 함수에 대한 정의는 최대 하나만있을 수 있습니다. 프로그램에 둘 이상의 인라인 멤버 함수 정의가있을 수 있습니다. 6.2 및 10.1.6을 참조하십시오. — 끝 참고]
GCC 8.3 구현
main.cpp
struct MyClass {
void myMethod() {}
};
int main() {
MyClass().myMethod();
}
컴파일 및 심볼보기 :
g++ -c main.cpp
nm -C main.o
산출:
U _GLOBAL_OFFSET_TABLE_
0000000000000000 W MyClass::myMethod()
U __stack_chk_fail
0000000000000000 T main
그러면 ELF 객체 파일 man nm
에서 MyClass::myMethod
심볼이 약한 것으로 표시되어 여러 객체 파일에 나타날 수 있음을 알 수 있습니다.
"W" "w"기호는 약한 개체 기호로 특별히 태그가 지정되지 않은 약한 기호입니다. 약한 정의 된 기호가 일반 정의 된 기호와 연결되면 일반 정의 된 기호가 오류없이 사용됩니다. 정의되지 않은 약한 심볼이 연결되고 심볼이 정의되지 않은 경우 심볼 값은 오류없이 시스템 별 방식으로 결정됩니다. 일부 시스템에서 대문자는 기본값이 지정되었음을 나타냅니다.
아마도 같은 이유로 Java의 클래스 정의 안에 전체 메소드 구현을 넣어야합니다.
구불 구불 한 괄호와 같은 키워드가 많이있어 비슷해 보이지만 언어가 다릅니다.