반복된 typedefs - C에서는 유효하지 않지만 C++에서는 유효합니다.
다음 코드가 C에서 컴플라이언스 경고를 트리거하는 이유를 표준 참조하고 싶습니다(테스트 완료).gcc -pedantic; "typedef refinition"(typedef 재정의)이지만 C++에서는 괜찮습니다.g++ -pedantic):
typedef struct Foo Foo;
typedef struct Foo Foo;
int main() { return 0; }
정의하지 못하는 이유는 무엇입니까?typedefC에서 반복적으로?
(이는 C프로젝트의 헤더 구조에 실질적인 영향을 미칩니다.
C++로 컴파일 되는 이유는 무엇입니까?
왜냐하면 C++ 규격은 그렇게 명시되어 있기 때문이다.
레퍼런스:
C++03 Standard 7.1.3 typedef 지정자
§7.1.3.2:
특정 비클래스 범위에서 typedef 지정자를 사용하여 해당 범위에서 선언된 모든 유형의 이름을 재정의하여 이미 참조된 유형을 참조할 수 있습니다.
[예:
typedef structures {/*...*/ }초
typeef int I;
typeef int I;
typedef I;
: 엔드 예시]
C에서 컴파일 할 수 없는 이유는 무엇입니까?
typedef이름에는 링크가 없으며 C99 표준에서는 링크 사양이 없는 식별자는 동일한 범위와 동일한 이름 공간에 여러 선언을 가질 수 없습니다.
레퍼런스:
C99 표준: 제6.2.2조 식별자의 연계
§ 6.2.2/6 상태:
다음 식별자: 객체 또는 기능 이외의 것으로 선언된 식별자, 함수 파라미터로 선언된 식별자, 스토리지 클래스 지정자 없이 선언된 객체의 블록 범위 식별자.
추가 § 6.7/3 상태:
식별자에 연결이 없는 경우, 6.7.2.3에 명시된 태그를 제외하고 동일한 범위와 동일한 이름 공간에 식별자(선언자 또는 형식 지정자)의 선언이 하나 이상 없어야 한다.
표준 C는 현재 ISO/IEC 9989:2011
2011년 C 표준은 ISO에 의해 2011-12-19년 월요일에 공표되었다(더 정확히 말하면, 19일에 위원회 웹사이트에 추가되었다; 2011-12-08년에 표준이 '오래 전'으로 공표되었을 수 있다).WG14 Web 사이트의 발표를 참조해 주세요.안타깝게도 ISO의 PDF는 338 CHF이고 ANSI 387 USD입니다.
- INCITS/ISO/IEC 9899:2012 (C2011)용 PDF는 ANSI에서 30 USD로 입수할 수 있습니다.
- INCITS/ISO/IEC 14882:2012 (C++2011)용 PDF는 ANSI에서 30 USD로 입수할 수 있습니다.
주요 답변
질문은 "C에서 반복되는 typedef가 허용됩니까?"입니다.정답은 "No - not the ISO/IEC 9899:1999 or 9899:1990 표준"입니다.그 이유는 아마도 과거일 것입니다.원래 C 컴파일러는 이를 허용하지 않았기 때문에 원래의 표준자(C 컴파일러에서 이미 사용 가능한 것을 표준화하도록 요구받음)가 그 동작을 표준화했습니다.
C99 표준 프로그램이 typedef를 반복하는 위치에 대해서는 Als의 답변을 참조하십시오.C11 표준은 § 6.7 3의 규칙을 다음과 같이 변경하였다.
3 식별자에 연계가 없는 경우, (선언자 또는 형식 지정자에) 동일한 범위와 동일한 이름 공간에 식별자의 선언이 하나 이상 있어서는 안 된다. 단, 다음과 같은 경우는 제외한다.
- typedef 이름이 현재와 동일한 유형을 나타내도록 재정의할 수 있습니다. 단, 그 유형이 가변적으로 수정된 유형이 아닌 경우,
- 태그는 6.7.2.3에 명시된 대로 다시 신고할 수 있습니다.
그래서 C11에는 반복적인 typedef라는 명시적인 명령이 있습니다.C11 준거 C 컴파일러의 이용 가능 여부를 확인합니다.
아직 C99 이전 버전을 사용하고 있는 사용자의 경우 후속 질문은 아마도 "어떻게 하면 반복된 typedef에서 문제가 발생하지 않도록 할 수 있을까요?"입니다.
여러 소스 파일에 필요한 각 유형을 정의하는 단일 헤더가 있다는 규칙을 따르고(단, 이러한 유형을 정의하는 헤더는 여러 개일 수 있습니다.단, 각 개별 유형은 하나의 헤더에만 있습니다), 해당 유형이 필요할 때마다 해당 헤더가 사용되면 충돌은 발생하지 않습니다.
또한 유형에 대한 포인터만 필요하고 실제 구조를 할당하거나 구성원에 액세스할 필요가 없는 경우에도 불완전한 구조 선언을 사용할 수 있습니다(유형 없음).다시, 불완전한 유형을 선언하는 헤더에 대한 규칙을 설정하고 유형이 필요한 경우 해당 헤더를 사용합니다.
C의 외부 변수란?을 참조하십시오. 변수에 대해 설명하지만 유형은 다소 비슷하게 취급할 수 있습니다.
코멘트로부터의 질문
특정 포함을 금지하는 별도의 전처리의 복잡성 때문에 나는 "불완전한 구조 선언"이 매우 필요하다.그럼 앞으로 나온 선언들이 다시 헤더로 입력되면 제가 입력하지 말라는 건가요?
대체로 그런가봐요.저는 이 문제를 실제로 해결할 필요가 없었습니다(단, 작업 중인 시스템의 일부에 대해 매우 걱정해야 하는 부분이 있지만). 이것은 약간 잠정적이지만, 작동해야 한다고 생각합니다.
일반적으로 헤더는 라이브러리(하나 이상의 소스 파일)에 의해 제공되는 외부 서비스를 라이브러리의 사용자가 컴파일할 수 있도록 충분히 상세하게 기술한다.특히 소스 파일이 여러 개 있는 경우에는 예를 들어 완전한 유형을 정의하는 내부 헤더가 있을 수 있습니다.
모든 헤더는 (a) 자급자족 및 (b) 아이돌포텐트입니다.즉, (a) 헤더를 포함할 수 있고 필요한 다른 모든 헤더가 자동으로 포함될 수 있으며 (b) 컴파일러의 분노를 사지 않고 헤더를 여러 번 포함할 수 있습니다.후자는 보통 헤더 가드를 사용하여 실현되지만 일부는 이를 선호합니다.#pragma once- 하지만 그것은 휴대할 수 없습니다.
따라서 다음과 같은 퍼블릭헤더를 가질 수 있습니다.
public.h
#ifndef PUBLIC_H_INCLUDED
#define PUBLIC_H_INCLUDED
#include <stddef.h> // size_t
typedef struct mine mine;
typedef struct that that;
extern size_t polymath(const mine *x, const that *y, int z);
#endif /* PUBLIC_H_INCLUDED */
아직까지는 그다지 논란이 되지 않았습니다(이 라이브러리가 제공하는 인터페이스가 매우 불완전하다고 합법적으로 의심할 수 있지만).
private.h
#ifndef PRIVATE_H_INCLUDED
#define PRIVATE_H_INCLUDED
#include "public.h" // Get forward definitions for mine and that types
struct mine { ... };
struct that { ... };
extern mine *m_constructor(int i);
...
#endif /* PRIVATE_H_INCLUDED */
다시 말씀드리지만, 별로 논란이 되진 않습니다.그public.h헤더를 먼저 나열해야 합니다.이것에 의해, 자기 평가의 자동 체크가 가능하게 됩니다.
소비자코드
필요한 모든 코드polymath()서비스 기입:
#include "public.h"
서비스 이용에 필요한 정보는 여기까지입니다.
공급자 코드
라이브러리의 모든 코드를 정의한다.polymath()서비스 기입:
#include "private.h"
그 후에는 모든 것이 정상적으로 기능합니다.
기타 프로바이더
다른 라이브러리가 있는 경우(call it)multimath()를 사용하는 경우,polymath()서비스, 그 코드에는 다음 코드가 포함되게 됩니다.public.h다른 소비자와 마찬가지로요.이 경우,polymath()서비스는 외부 인터페이스의 일부입니다.multimath(), 그 다음에multimath.h퍼블릭 헤더는 다음을 포함합니다.public.h(죄송합니다.이쪽에서 용어를 바꿨습니다).이 경우,multimath()서비스에서는 모든 것을 완전히 숨깁니다.polymath()서비스, 그 다음에multimath.h헤더에 포함되지 않음public.h단,multimath()개인 헤더 또는 개별 소스 파일이 필요한 경우polymath()필요에 따라 서비스를 제공할 수 있습니다.
올바른 헤더를 어디에나 포함시키는 규율을 엄수하는 한, 이중 화질의 문제는 발생하지 않습니다.
그 후 헤더 중 하나에 2개의 정의 그룹이 포함되어 있는 것을 알게 되면 1개는 경합 없이 사용할 수 있으며, 1개는 새로운 헤더(및 거기에 선언된 서비스)와 경합할 수 있습니다.그러면 원래 헤더를 2개의 서브헤더로 분할해야 합니다.각 소제목은 여기에 자세히 설명되어 있는 규칙을 개별적으로 따릅니다.원래 헤더는 2개의 개별 파일을 포함하는 헤더 가드와 행으로 사소한 것이 됩니다.종속성이 변경되더라도 기존 작업 코드는 모두 변경되지 않은 상태로 유지됩니다(의존할 수 있는 추가 파일).새로운 코드에는 기존의 헤더와 경합하는 새로운 헤더를 사용하면서 적절한 서브헤더를 포함할 수 있습니다.
물론 단순히 양립할 수 없는 두 개의 헤더를 가질 수도 있습니다.예를 들어, (잘못 설계된) 헤더가 존재하여 다른 버전의 것을 선언하는 경우FILE구조(버전부터)<stdio.h>), Hosed 입니다.코드에 잘못 설계된 헤더가 포함되어 있을 수 있습니다.<stdio.h>둘 다 아니에요.이 경우 설계가 잘못된 헤더는 새 이름을 사용하도록 수정해야 합니다(아마도).File, 하지만 아마 다른 것이 있을 것이다.기업 인수 후 다음과 같은 일반적인 데이터 구조를 사용하여 두 제품의 코드를 하나의 제품으로 병합해야 하는 경우 보다 현실적으로 이 문제가 발생할 수 있습니다.DB_Connection데이터베이스 접속에 사용합니다. C++ 가 namespace하나 또는 둘 다 코드의 이름 변경 연습이 필요합니다.
7.1.3/3 및 /4 때문에 C++로 실행할 수 있습니다.
C99에서는 6.7.7에서는 이와 동등한 특수한 케이스가 없기 때문에 C99에서는 할 수 없습니다.따라서 typedef 이름을 다시 선언하는 것은 다른 ID를 다시 선언하는 것과 동일한 규칙을 따릅니다.구체적으로는 6.2.2/6(typedef에는 링크가 없습니다) 및 6.7/3(링크 없는 식별자는 동일한 범위에서 한 번만 선언할 수 있습니다).
마세요.typedefC99는 C++의 decl-specificator이다.문법이 다르기 때문에 C++의 저자들은 타이피디프를 "다른 종류의 선언"으로 만드는 데 더 많은 노력을 기울이기로 결정했고, 그래서 그들을 위한 특별한 규칙에 더 많은 시간과 텍스트를 기꺼이 소비했을지도 모른다는 의심이 든다.그 외에는 C99 작가들의 동기가 무엇이었는지 모르겠다.
[편집: C1x에 대한 요하네스의 답변을 참조하십시오.저는 전혀 이해하지 못하기 때문에 C를 C99라고 하는 의미로 사용하는 것을 그만두어야 할 것 같습니다.왜냐하면 그들이 비준해 공표해도 눈치채지 못할지도 모르기 때문입니다.'C'는 'C99'를 의미하지만 실제로는 'C99'를 의미하지만 MSVC를 지원해야 한다면 'C89'를 의미합니다.
[다시 편집: 실제로 공개되어 C11이 되었습니다.으으으으악!
많은 사람들이 기준을 언급하며 답했지만, 아무도 "왜 여기서 C와 C++의 기준이 다른가"라고 말하지 않았습니다.음, C++에서 반복되는 typedef를 허용하는 이유는 C++가 구조와 클래스를 유형으로 암시적으로 선언하기 때문이라고 생각합니다.따라서 C++에서는 다음 사항이 합법적입니다.
struct foo { int a; int b; };
foo f;
C에는 다음과 같이 적어야 합니다.
struct foo { int a; int b; };
typedef struct foo foo;
foo f;
구조체를 유형으로 선언하는 그런 C 코드가 많이 있습니다.이러한 코드가 C++로 이행되면 C++ 언어에 의해 암묵적인 typedef가 추가되기 때문에 typedef는 중복됩니다.따라서 프로그래머들이 더 이상 필요하지 않은 typedef를 제거하는 번거로움을 피하기 위해 처음부터 C++로 typedef를 복제할 수 있도록 했습니다.
다른 사람들이 말했듯이, 시간이 있는 사람들은 C에서 동일한 typedef를 반복하는 것도 유용할 수 있다는 것을 깨달았다.적어도 해롭지는 않을 거야.그래서 이 C++ 기능이 C11로 "백포트"된 것입니다.
c사양에는 이것이 무효인 이유를 나타내는 것은 없습니다.스펙은 그것을 명확히 하기에는 부적절한 장소입니다.FWIW는 C1x에서 허용됩니다(마지막 질문 중 하나에서 받은 답변에 따르면).
이 c1x 기능은 매크로를 typedef로 변환하는 것을 지원한다고 생각합니다(동일할 경우 반복할 수 있습니다).
언급URL : https://stackoverflow.com/questions/8594954/repeated-typedefs-invalid-in-c-but-valid-in-c
'programing' 카테고리의 다른 글
| 잭슨을 사용하여 Java 객체를 JSON으로 변환 (0) | 2022.07.30 |
|---|---|
| Vue.js에서 어레이를 필터링하고 V-for를 루프하는 방법 (0) | 2022.07.30 |
| vdso와 vsyscall이 뭐죠? (0) | 2022.07.30 |
| printf 문자열, 가변 길이 항목 (0) | 2022.07.30 |
| Vue.js: 컴포넌트 인스턴스가 마운트된 시기를 알 수 있는 방법이 있습니까? (0) | 2022.07.30 |