C ++에서 'struct'와 'typedef struct'의 차이점은 무엇입니까?


답변:


1202

C ++에서는 미묘한 차이 만 있습니다. C와의 차이로 차이를 만듭니다.

C 언어 표준 ( C89 §3.1.2.3 , C99 §6.2.3C11 §6.2.3 )은 태그 식별자 ( struct/ union/ enum) 및 일반 식별자 ( typedef및 기타 식별자)를 포함하여 다양한 범주의 식별자에 대해 별도의 네임 스페이스를 요구합니다. .

방금 말한 경우 :

struct Foo { ... };
Foo x;

Foo태그 네임 스페이스에만 정의되어 있으므로 컴파일러 오류가 발생 합니다.

다음과 같이 선언해야합니다.

struct Foo x;

를 참조하려고 Foo할 때마다 항상로 호출해야합니다 struct Foo. 이것은 짜증나게하기 때문에 다음을 추가 할 수 있습니다 typedef.

struct Foo { ... };
typedef struct Foo Foo;

이제 struct Foo(태그 네임 스페이스에서) 일반 Foo(일반 식별자 네임 스페이스에서)은 모두 동일한 것을 참조 Foo하며 struct키워드 없이 유형의 객체를 자유롭게 선언 할 수 있습니다 .


구성 :

typedef struct Foo { ... } Foo;

선언의 약어 일뿐 typedef입니다.


드디어,

typedef struct { ... } Foo;

익명 구조를 선언하고 typedef이를 만듭니다 . 따라서이 구문을 사용하면 태그 네임 스페이스에 이름이없고 typedef 네임 스페이스에 이름 만 있습니다. 이것은 또한 앞으로 선언 될 수 없다는 것을 의미합니다. 정방향 선언을하려면 태그 네임 스페이스에 이름을 지정해야합니다 .


C ++ 에서 이름이 같은 이름을 가진 다른 선언에 의해 숨겨지지 않는 한 모든 struct/ union/ enum/ class선언은 암시 적으로 선언 된 것처럼 작동 typedef합니다. 자세한 내용은 Michael Burr의 답변 을 참조하십시오.


53
당신이 말하는 것은 사실이지만, AFAIK, 'typedef struct {...} Foo;' 명명되지 않은 구조체의 별칭을 만듭니다.
dirkgently 2016 년

25
잘 잡는다. "typedef struct Foo {...} Foo;"사이에는 미묘한 차이가있다 및 "typedef struct {...} Foo;".
Adam Rosenfield

8
C에서 struct 태그, union 태그 및 열거 태그는 위에서 주장한 것처럼 2를 사용하여 (struct 및 union) 대신 하나의 네임 스페이스를 공유합니다. typedef 이름에 대해 참조 된 네임 스페이스는 실제로 별개입니다. 즉, 'union x {...};'를 모두 가질 수는 없습니다. 그리고 'struct x {...};' 단일 범위에서.
Jonathan Leffler

10
문제가 아닌 typedef와는 별도로, 문제의 두 코드 조각 간의 또 다른 차이점은 Foo는 첫 번째 예제에서는 생성자를 정의 할 수 있지만 두 번째 예제에서는 생성자를 정의 할 수 없다는 것입니다 (익명 클래스는 생성자 또는 소멸자를 정의 할 수 없으므로) .
Steve Jessop

1
@Lazer : 거기 있는 미묘한 차이지만, 아담 수단은 타입 정의없이 변수를 선언하는 '유형 VAR'를 사용할 수있는 (그 말에 간다).
Fred Nurk

226

에서 이 DDJ 문서 , 단 삭스는 당신이 당신의 구조체 typedef에하지 않으면 버그를 통해 크리프 수있는 하나 개의 작은 영역을 설명 (및 클래스를!)

원한다면 C ++이 모든 태그 이름에 대해 typedef를 생성한다고 상상할 수 있습니다.

typedef class string string;

불행히도 이것은 완전히 정확한 것은 아닙니다. 나는 그것이 그렇게 간단하기를 원하지만 그렇지 않습니다. C ++은 C와의 비 호환성을 도입하지 않으면 구조체, 공용체 또는 열거 형에 대해 이러한 typedef를 생성 할 수 없습니다.

예를 들어, C 프로그램이 status라는 이름의 함수와 구조체를 선언한다고 가정합니다.

int status(); struct status;

다시 말하지만, 이것은 나쁜 습관이지만 C입니다.이 프로그램에서 상태 자체는 기능을 나타냅니다. 구조체 상태는 유형을 나타냅니다.

C ++가 태그에 대해 typedef를 자동으로 생성 한 경우이 프로그램을 C ++로 컴파일하면 컴파일러는 다음을 생성합니다.

typedef struct status status;

불행히도이 유형 이름은 함수 이름과 충돌하고 프로그램은 컴파일되지 않습니다. 그렇기 때문에 C ++은 단순히 각 태그에 대한 typedef를 생성 할 수 없습니다.

C ++에서 태그는 typedef 이름과 동일하게 작동하지만 프로그램이 태그와 이름 및 범위가 동일한 객체, 함수 또는 열거자를 선언 할 수 있습니다. 이 경우 객체, 함수 또는 열거 자 이름이 태그 이름을 숨 깁니다. 프로그램은 태그 이름 앞에 키워드 class, struct, union 또는 enum (적절한 경우)을 사용하여 태그 이름 만 참조 할 수 있습니다. 이러한 키워드 중 하나와 태그로 구성되는 유형 이름은 정교한 유형 지정자입니다. 예를 들어, struct status 및 enum month는 정교한 유형 지정자입니다.

따라서 다음을 모두 포함하는 C 프로그램 :

int status(); struct status;

C ++로 컴파일 할 때 동일하게 동작합니다. 이름 상태만으로도 기능을 나타냅니다. 프로그램은 정교한 형식 지정자 구조체 상태를 사용해야 만 형식을 참조 할 수 있습니다.

그렇다면 어떻게 버그가 프로그램에 영향을 줄 수 있습니까? Listing 1 의 프로그램을 고려하자 . 이 프로그램은 기본 생성자와 foo 객체를 char const *로 변환하는 변환 연산자로 클래스 foo를 정의합니다. 표현식

p = foo();

main에서 foo 객체를 생성하고 변환 연산자를 적용해야합니다. 후속 출력문

cout << p << '\n';

클래스 foo를 표시해야하지만 그렇지 않습니다. 함수 foo를 표시합니다.

이 놀라운 결과는 프로그램이 Listing 2에 표시된 헤더 lib.h를 포함하기 때문에 발생한다 . 이 헤더는 foo라는 함수를 정의합니다. 함수 이름 foo는 클래스 이름 foo를 숨기므로 main에서 foo에 대한 참조는 클래스가 아니라 함수를 참조합니다. main은 다음과 같이 정교한 형식 지정자를 사용해야 만 클래스를 참조 할 수 있습니다.

p = class foo();

프로그램 전체에서 이러한 혼동을 피하는 방법은 클래스 이름 foo에 다음 typedef를 추가하는 것입니다.

typedef class foo foo;

클래스 정의 직전 또는 직후. 이 typedef는 형식 이름 foo와 함수 이름 foo (라이브러리에서) 사이에 컴파일 타임 오류를 유발하는 충돌을 일으 킵니다.

물론 이러한 typedef를 실제로 작성하는 사람은 아무도 없습니다. 많은 훈련이 필요합니다. Listing 1 의 오류와 같은 오류의 발생률은 매우 작기 때문에 많은 사람들이이 문제를 무시하지는 않는다. 그러나 소프트웨어 오류로 인해 신체 부상을 입을 수있는 경우 오류 가능성에 관계없이 typedef를 작성해야합니다.

왜 누군가가 클래스와 같은 범위에서 함수 또는 객체 이름을 가진 클래스 이름을 숨기고 싶어하는지 상상할 수 없습니다. C의 숨기기 규칙은 실수였으며 C ++의 클래스로 확장되어서는 안됩니다. 실제로 실수를 바로 잡을 수는 있지만 불필요한 프로그래밍 분야와 노력이 필요합니다.


11
"class foo ()"를 시도했는데 실패한 경우 : ISO C ++에서 "class foo ()"는 잘못된 구문입니다 (표준화 전에 '97이 작성되었습니다). "typedef class foo foo;"를 넣을 수 있습니다. 메인에 "foo ();"라고 말할 수 있습니다. (따라서 typedef-name은 함수 이름보다 어휘 적으로 가깝습니다). 구문 상 T ()에서 T는 단순 유형 지정자 여야합니다. 정교한 형식 지정자는 허용되지 않습니다. 물론 이것은 좋은 대답입니다.
Johannes Schaub-litb

Listing 1Listing 2링크가 끊어집니다. 보세요
Prasoon Saurav

3
클래스와 함수에 다른 명명 규칙을 사용하는 경우 추가적인 typedef를 추가하지 않고도 명명 충돌을 피할 수 있습니다.
zstewart

64

한 가지 더 중요한 차이점은 다음과 같습니다. typedefs 전달할 수 없습니다. 그래서에 대한 typedef옵션을 수행해야합니다 #include파일은을 포함하는 typedef모든 것을 의미 #include당신의이야 .h또한 직접 필요 여부 등 여부를 해당 파일을 포함합니다. 더 큰 프로젝트에서 빌드 시간에 영향을 줄 수 있습니다.

이 없으면 파일 의 맨 typedef앞에 포워드 선언을 추가하고 파일 의 구조체 정의 만 추가 할 수 있습니다 .struct Foo;.h#include.cpp


구조체 정의를 노출하면 빌드 시간에 영향을 미치는 이유는 무엇입니까? 컴파일러가 Foo * nextFoo 와 같은 것을 볼 때 컴파일러가 정의를 알 수 있도록 typedef 옵션을 제공하지 않아도 추가 검사를 수행합니까? ?
Rich

3
실제로 추가 검사가 아니며 컴파일러가 처리 해야하는 코드가 더 많습니다. 포함 체인 어딘가에 해당 typedef가 발생하는 모든 cpp 파일에 대해 typedef를 컴파일합니다. 더 큰 프로젝트에서 typedef를 포함하는 .h 파일은 미리 컴파일 된 헤더가 많은 도움이 되더라도 쉽게 수백 번 컴파일 될 수 있습니다. 순방향 선언을 사용하여 벗어날 수 있다면 전체 구조체 사양을 포함하는 .h를 포함하는 것이 실제로 관심있는 코드로 제한하는 것이 더 쉬우므로 해당하는 포함 파일이 덜 컴파일됩니다.
Joe

@ 이전 댓글 작성자 (게시물 소유자 제외)에게 문의하십시오. 나는 답장을 거의 놓쳤다. 그러나 정보 주셔서 감사합니다.
Rich

C11에서는 더 이상 사실이 아니며, 같은 이름으로 같은 구조체를 여러 번 typedef 할 수 있습니다.
michaelmeyer

32

차이가 있지만, 미묘한는. 이런 식으로보십시오 : struct Foo새로운 유형을 소개합니다. 두 번째는 명명되지 않은 struct유형에 대해 Foo (새 유형이 아님)라는 별칭을 만듭니다 .

7.1.3 typedef 지정자

1 [...]

typedef 지정자로 선언 된 이름은 typedef-name이됩니다. 선언의 범위 내에서 typedef-name은 구문 상 키워드와 동일하며 8 절에 설명 된 방식으로 식별자와 연관된 유형의 이름을 지정합니다. 따라서 typedef-name은 다른 유형의 동의어입니다. typedef-name 클래스 선언 (9.1) 또는 열거 선언과 같은 방식으로 새 유형을 도입 하지 않습니다.

8 typedef 선언이 명명되지 않은 클래스 (또는 열거 형)를 정의하는 경우 선언에 의해 해당 클래스 유형 (또는 열거 형)으로 선언 된 첫 번째 typedef-name은 링크 목적으로 만 클래스 유형 (또는 열거 형)을 나타내는 데 사용됩니다 ( 3.5). [ 예:

typedef struct { } *ps, S; // S is the class name for linkage purposes

따라서 typedef는 항상 다른 유형의 자리 표시 자 / 동의어로 사용됩니다.


10

typedef 구조체에는 정방향 선언을 사용할 수 없습니다.

구조체 자체는 익명 형식이므로 전달할 실제 이름이 없습니다.

typedef struct{
    int one;
    int two;
}myStruct;

이와 같은 선언은 작동하지 않습니다.

struct myStruct; //forward declaration fails

void blah(myStruct* pStruct);

//error C2371: 'myStruct' : redefinition; different basic types

함수 프로토 타입에 대한 두 번째 오류가 발생하지 않습니다. 왜 "재정의; 다른 기본 유형"이라고 말합니까? 컴파일러는 myStruct의 정의가 어떻게 보이는지 알 필요가 없습니다. myStruct는 코드 조각 (typedef 또는 포워드 선언)을 사용하여 구조체 유형을 나타냅니다.
Rich

@Rich 이름 충돌이 있다고 불평합니다. "myStruct라는 구조체를 찾으십시오"라는 포워드 선언이 있고 이름없는 구조의 이름을 "myStruct"로 바꾸는 typedef가 있습니다.
Yochai Timmer

typedef와 forward 선언을 같은 파일에 넣었습니까? 나는 gcc를 잘 컴파일했다. myStruct는 이름없는 구조로 올바르게 해석됩니다. 태그 myStruct는 태그 네임 스페이스에 있고 typedef_ed myStruct는 함수 이름, 로컬 변수 이름과 같은 다른 식별자가있는 일반 네임 스페이스에 있습니다. 따라서 충돌이 없어야합니다. 오류가 있다고 의심되면 내 코드를 보여줄 수 있습니다.
Rich

@Rich GCC도 같은 오류가 발생합니다. 텍스트는 약간 다릅니다 : gcc.godbolt.org/…
Yochai Timmer

나는 ed 이름을 typedef가진 앞으로 선언 만있을 때 typedef명명되지 않은 구조를 참조하지 않는다는 것을 이해합니다 . 대신 forward 선언은 tag로 불완전한 구조를 선언합니다 myStruct. 또한의 정의를 보지 않으면 ed 이름을 typedef사용하는 함수 프로토 타입 typedef이 적합하지 않습니다. 따라서 myStruct타입을 나타 내기 위해 사용해야 할 때마다 전체 typedef를 포함시켜야합니다 . 내가 오해하면 저를 수정하십시오. 감사.
Rich

0

C ++에서 'typedef 구조체'와 'struct'의 중요한 차이점은 'typedef 구조체'에서 인라인 멤버 초기화가 작동하지 않는다는 것입니다.

// the 'x' in this struct will NOT be initialised to zero
typedef struct { int x = 0; } Foo;

// the 'x' in this struct WILL be initialised to zero
struct Foo { int x = 0; };

3
사실이 아니다. 두 경우 모두 x초기화됩니다. Coliru 온라인 IDE에서 테스트를 참조하십시오 (42로 초기화되었으므로 할당이 실제로 발생했다는 것이 0보다 분명합니다).
콜린 D 베넷

실제로 Visual Studio 2013에서 테스트했지만 초기화되지 않았습니다. 이것은 프로덕션 코드에서 발생했던 문제였습니다. 모든 컴파일러는 다르며 특정 기준 만 충족하면됩니다.
user2796283

-3

C ++에는 차이가 없지만 C에서는 명시 적으로 수행하지 않고 구조체 Foo의 인스턴스를 선언 할 수 있다고 생각합니다.

struct Foo bar;

3
@ dirkgently의 대답 봐 ---이 있다 차이,하지만 미묘한입니다.
Keith Pinson

-3

구조는 데이터 유형을 작성하는 것입니다. typedef는 데이터 유형의 별명을 설정하는 것입니다.


12
당신은 그 질문을 이해하지 못했습니다.
Winter
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.