문제

SO에 대한 외부/내부 연결에 대한 기존 질문을 읽었습니다.제 질문은 다릅니다. 동일한 변수에 대해 여러 정의가 있고 다른 번역 단위에 외부 연결이 있는 경우 어떻게 됩니까? C 그리고 C++?

예를 들어:

/*file1.c*/

typedef struct foo {
    int a;
    int b;
    int c;
} foo;

foo xyz;


/*file2.c*/

typedef struct abc {
    double x;
} foo;

foo xyz;

Dev-C++와 C 프로그램을 사용하면 위 프로그램이 완벽하게 컴파일되고 링크됩니다.반면 C++ 프로그램으로 컴파일하면 다중 재정의 오류가 발생합니다.왜 C에서 작동해야 하며 C++와의 차이점은 무엇입니까?이 동작은 정의되지 않았으며 컴파일러에 따라 달라지나요?이 코드는 얼마나 "나쁜" 코드이며 리팩토링하려면 어떻게 해야 합니까(이렇게 작성된 오래된 코드를 많이 본 적이 있습니다)?

도움이 되었습니까?

해결책

C와 C ++는 "하나의 정의 규칙"을 가지고 있으며, 이는 각 객체가 모든 프로그램에서 한 번만 정의 될 수 있습니다. 이 규칙의 위반 원인 정의되지 않은 행동 이는 컴파일시 진단 메시지를 보거나 보지 않을 수 있음을 의미합니다.

파일 범위에서 다음 선언 사이에는 언어 차이가 있지만 예제의 문제에 직접 문제가되지는 않습니다.

int a;

C에서 이것은 잠정적 인 정의입니다. 동일한 변환 단위의 다른 잠정적 정의로 합병하여 단일 정의를 형성 할 수 있습니다. C ++에서는 항상 정의입니다 (사용해야합니다. extern 객체를 정의하지 않고 선언하려면) 및 동일한 번역 장치에서 동일한 객체의 후속 정의는 오류입니다.

예에서는 두 번역 단위 모두 (충돌) 정의가 있습니다. xyz 임시 정의에서.

다른 팁

이는 C++의 이름 변경으로 인해 발생합니다.에서 위키피디아:

첫 번째 C ++ 컴파일러는 C 소스 코드로 번역기로 구현되었으며, 이는 C 컴파일러에 의해 개체 코드로 컴파일됩니다.이로 인해 기호 이름은 C 식별자 규칙을 준수해야했습니다.나중에도 기계 코드 또는 어셈블리를 직접 생산 한 컴파일러의 출현으로 시스템의 링커는 일반적으로 C ++ 기호를 지원하지 않았으며 여전히 망설이 필요했습니다.

에 관해서 호환성:

컴파일러 공급 업체에게 더 큰 자유를 제공하기 위해 C ++ 표준위원회는 이름 관리, 예외 처리 및 기타 구현 별 기능의 구현을 지시하지 않기로 결정했습니다.이 결정의 단점은 다른 컴파일러가 생성 한 객체 코드가 호환되지 않을 것으로 예상됩니다.그러나 해당 플랫폼의 컴파일러를 표준화하려는 특정 기계 또는 운영 체제에 대한 타사 표준이 있습니다 (예 : C ++ ABI [18]).일부 컴파일러는 이러한 항목에 대한 보조 표준을 채택합니다.

에서http://www.cs.indiana.edu/~welu/notes/node36.html다음 예가 제공됩니다.


예를 들어 아래 C 코드의 경우

int foo(double*);
double bar(int, double*);

int foo (double* d) 
{
    return 1;
}

double bar (int i, double* d) 
{
    return 0.9;
}

해당 기호 테이블은 다음과 같습니다. dump -t)

[4]  0x18        44       2     1   0   0x2 bar
[5]  0x0         24       2     1   0   0x2 foo

동일한 파일에 대해 g++에서 컴파일하면 기호 테이블은 다음과 같습니다.

[4]  0x0         24       2     1   0   0x2 _Z3fooPd
[5]  0x18        44       2     1   0   0x2 _Z3bariPd

_Z3bariPd 이름이 bar이고 첫 번째 인수가 정수이고 두 번째 인수가 double에 대한 포인터인 함수를 의미합니다.


C ++는 기호를 두 번 이상 정의 할 수 없습니다. C 링커가 무엇을하고 있는지 확실하지 않으면, 단순히 두 정의를 동일한 기호에 매핑 할 수있는 좋은 추측은 물론 심각한 오류를 유발할 수 있습니다.

포팅을 위해 개별 C-Files의 내용을 익명 네임 스페이스에 넣으려고 노력할 것입니다. 기호는 기호를 다르게, 파일과 로컬로 만들므로 다른 곳에서 동일한 이름으로 충돌하지 않습니다.

C 프로그램은 이것을 허용하고 메모리를 노조처럼 조금 취급합니다. 그것은 실행되지만 당신이 기대했던 것을 줄 수는 없습니다.

C ++ 프로그램 (유형이 강한)은 문제를 올바르게 감지하고 문제를 해결하도록 요청합니다. 당신이 정말로 당신이 노조를 원한다면, 그것을 하나로 선언하십시오. 두 개의 별개의 객체를 원한다면 범위를 제한하십시오.

당신은 찾았습니다 하나의 정의 규칙. 분명히 귀하의 프로그램에는 버그가 있습니다

  • 이름이 하나만있을 수 있습니다 foo 프로그램이 연결되면.
  • 일부 소스 파일에 모든 헤더 파일이 포함 된 경우 두 가지 정의가 표시됩니다. foo.

C ++ 컴파일러는 "Mangling"으로 인해 #1을 얻을 수 있습니다. 링크 된 프로그램의 변수 이름은 선택한 프로그램과 다를 수 있습니다. 이 경우 필요하지는 않지만 컴파일러가 문제를 감지 한 방식 일 것입니다. 그러나 #2는 남아 있으므로 그렇게 할 수 없습니다.

안전 메커니즘을 정말로 물리 치고 싶다면 다음과 같이 망설을 비활성화 할 수 있습니다.

extern "C" struct abc foo;

… 기타 파일…

extern "C" struct foo foo;

extern "C" 링커에 C ABI 규칙을 사용하도록 지시합니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top