[c] 왜 C에서 구조체를 이렇게 자주 typedef해야합니까?

아래와 같은 구조로 구성된 많은 프로그램을 보았습니다.

typedef struct 
{
    int i;
    char k;
} elem;

elem user;

왜 그렇게 자주 필요합니까? 특정한 이유나 적용 가능한 영역이 있습니까?



답변

Greg Hewgill이 말했듯이 typedef는 더 이상 struct모든 곳 을 쓸 필요가 없음을 의미합니다 . 키 스트로크를 절약 할뿐만 아니라 smidgen에 더 추상화를 제공하므로 코드를 더 깨끗하게 만들 수 있습니다.

같은 것

typedef struct {
  int x, y;
} Point;

Point point_new(int x, int y)
{
  Point a;
  a.x = x;
  a.y = y;
  return a;
}

“struct”키워드를 모든 곳에서 볼 필요가 없을 때 더 깨끗해집니다. 실제로 언어에 “Point”라는 유형이있는 것처럼 보입니다. 다음에 typedef내가 추측하는 경우가 있습니다.

또한 귀하의 예제 (및 광산)는 struct 자체 이름 지정을 생략했지만 실제로는 이름 지정이 불투명 한 유형을 제공하려는 경우에도 유용합니다. 그런 다음 헤더에 다음과 같은 코드가 있습니다.

typedef struct Point Point;

Point * point_new(int x, int y);

그런 다음 struct구현 파일에 정의 를 제공 하십시오.

struct Point
{
  int x, y;
};

Point * point_new(int x, int y)
{
  Point *p;
  if((p = malloc(sizeof *p)) != NULL)
  {
    p->x = x;
    p->y = y;
  }
  return p;
}

후자의 경우, 정의는 헤더 파일의 사용자에게 숨겨져 있기 때문에 Point by 값을 반환 할 수 없습니다. 이것은 예를 들어 GTK + 에서 널리 사용되는 기술 입니다.

업데이트typedef 숨기기를 사용 struct하는 것이 나쁜 생각 으로 여겨지는 C 프로젝트도 있습니다. 리눅스 커널은 아마도 가장 잘 알려진 프로젝트 일 것입니다. Linus의 화난 단어에 대해서는 Linux 커널 코딩 스타일 문서의 5 장을 참조하십시오 . 🙂 내 요점은 문제의 “반드시”가 아마도 돌로 설정되어 있지 않다는 것입니다.


답변

얼마나 많은 사람들이이 잘못을 저지르는지는 놀랍습니다. C로 구조체를 typedef하지 말고, 큰 C 프로그램에서 이미 매우 오염 된 전역 네임 스페이스를 불필요하게 오염시킵니다.

또한 태그 이름이없는 typedef의 구조체는 헤더 파일 간의 순서 관계를 불필요하게 부과하는 주요 원인입니다.

치다:

#ifndef FOO_H
#define FOO_H 1

#define FOO_DEF (0xDEADBABE)

struct bar; /* forward declaration, defined in bar.h*/

struct foo {
  struct bar *bar;
};

#endif

typedef를 사용하지 않는 그러한 정의로, compiland 유닛이 foo.h를 포함하여 FOO_DEF정의 에 도달 할 수 있습니다. foo구조체 의 ‘bar’멤버를 역 참조하지 않으면 “bar.h”파일을 포함 할 필요가 없습니다.

또한 네임 스페이스가 태그 이름과 멤버 이름간에 다르기 때문에 다음과 같이 매우 읽기 쉬운 코드를 작성할 수 있습니다.

struct foo *foo;

printf("foo->bar = %p", foo->bar);

네임 스페이스는 별개이므로 이름 지정 변수와 struct 태그 이름이 일치하는 충돌이 없습니다.

코드를 유지 관리 해야하는 경우 typedef의 구조체를 제거합니다.


답변

Dan Saks의 오래된 기사에서 ( http://www.ddj.com/cpp/184403396?pgno=3 ) :


구조체의 이름을 지정하는 C 언어 규칙은 약간 편 심적이지만 무해합니다. 그러나 C ++의 클래스로 확장 될 때 동일한 규칙으로 버그가 크롤링되지 않습니다.

C에서 이름은

struct s
    {
    ...
    };

태그입니다. 태그 이름은 유형 이름이 아닙니다. 위의 정의에서 다음과 같은 선언은

s x;    /* error in C */
s *p;   /* error in C */

C의 오류입니다. 다음과 같이 작성해야합니다.

struct s x;     /* OK */
struct s *p;    /* OK */

공용체와 열거의 이름도 유형이 아닌 태그입니다.

C에서 태그는 다른 모든 이름 (함수, 유형, 변수 및 열거 상수)과 구별됩니다. C 컴파일러는 다른 모든 이름을 보유한 테이블과 물리적으로 분리되어 있지 않은 경우 개념적으로 심볼 테이블에 태그를 유지합니다. 따라서 C 프로그램이 동일한 범위에서 동일한 철자를 가진 태그와 다른 이름을 모두 가질 수 있습니다. 예를 들어

struct s s;

struct 유형의 변수 s를 선언하는 유효한 선언입니다. 좋은 습관은 아니지만 C 컴파일러는이를 받아 들여야합니다. C가 이런 식으로 설계된 이유에 대한 이론적 근거를 본 적이 없습니다. 나는 항상 그것이 실수라고 생각했지만 거기에 있습니다.

많은 프로그래머 (당신을 포함하여)는 구조체 이름을 타입 이름으로 생각하기를 원하므로 typedef를 사용하여 태그의 별칭을 정의합니다. 예를 들어

struct s
    {
    ...
    };
typedef struct s S;

struct 대신 S를 사용할 수 있습니다.

S x;
S *p;

프로그램은 S를 유형과 변수 (또는 함수 또는 열거 상수)의 이름으로 사용할 수 없습니다.

S S;    // error

이거 좋다

구조체, 공용체 또는 열거 정의의 태그 이름은 선택 사항입니다. 많은 프로그래머들이 구조체 정의를 typedef로 접고 다음과 같이 태그를 생략합니다.

typedef struct
    {
    ...
    } S;

링크 된 기사는 또한 C ++ 동작이 필요하지 않은 C ++ 동작이 어떻게 typedef미묘한 이름 숨기기 문제를 일으킬 수 있는지에 대한 토론을 제공 합니다. 이러한 문제를 방지하려면 typedef언뜻보기에는 불필요 한 것처럼 보이지만 C ++의 클래스와 구조체에도 좋은 아이디어 입니다. C ++ typedef에서 이름 숨기기를 사용하면 잠재적 인 문제의 숨겨진 원인이 아니라 컴파일러에서 알려주는 오류가됩니다.


답변

사용 typedef하면 struct해당 유형의 변수를 선언 할 때마다 쓰지 않아도됩니다 .

struct elem
{
 int i;
 char k;
};
elem user; // compile error!
struct elem user; // this is correct


답변

이 문제의 결과를 항상 typedef 열거하고 구조화하는 또 다른 좋은 이유는 다음과 같습니다.

enum EnumDef
{
  FIRST_ITEM,
  SECOND_ITEM
};

struct StructDef
{
  enum EnuumDef MyEnum;
  unsigned int MyVar;
} MyStruct;

구조체 (Enu u mDef) 에서 EnumDef의 오타에 주목하십시오 . 이것은 오류 (또는 경고)없이 컴파일되며 (C 표준의 리터럴 해석에 따라 다름) 정확합니다. 문제는 방금 내 구조체 내에 새로운 (빈) 열거 정의를 만들었습니다. 이전 정의 EnumDef를 사용하지 않습니다.

typdef를 사용하면 비슷한 종류의 오타가 알려지지 않은 유형을 사용할 때 컴파일러 오류가 발생합니다.

typedef
{
  FIRST_ITEM,
  SECOND_ITEM
} EnumDef;

typedef struct
{
  EnuumDef MyEnum; /* compiler error (unknown type) */
  unsigned int MyVar;
} StructDef;
StrructDef MyStruct; /* compiler error (unknown type) */

나는 항상 typedef’ing 구조체와 열거를 옹호합니다.

일부 타이핑을 저장하는 것뿐만 아니라 (pun 의도하지 않음;) 더 안전하기 때문입니다.


답변

Linux 커널 코딩 스타일 5 장에서는을 사용하는 장단점을 제공 typedef합니다.

“vps_t”와 같은 것을 사용하지 마십시오.

구조체와 포인터에 typedef를 사용 하는 것은 실수 입니다. 당신이 볼 때

vps_t a;

출처에서 무엇을 의미합니까?

반대로 말하면

struct virtual_container *a;

실제로 “a”가 무엇인지 알 수 있습니다.

많은 사람들이 typedef가 “가독성을 돕는다”고 생각합니다. 별로. 그들은 다음에 대해서만 유용합니다 :

(a) 완전히 불투명 한 객체 (typedef가 적극적으로 사용되어 객체 를 숨기는 경우)

예 : “pte_t”등 올바른 접근 자 기능을 사용해서 만 액세스 할 수있는 불투명 객체.

노트! 불투명도와 “접근 자 기능”자체는 좋지 않습니다. 우리가 pte_t 등과 같은 것들에 대한 그 (것)들이 그 이유는 정말 절대적 있다는 것입니다 제로 이식 접근 가능한 정보가.

(b) 명확한 정수 유형. 추상화 가 “int”또는 “long”인지 혼동 피하는 데 도움이됩니다 .

u8 / u16 / u32는 여기보다 카테고리 (d)에 더 잘 맞지만 완벽하게 훌륭한 typedef입니다.

노트! 다시 말하지만 이것에 대한 이유가 필요합니다. “부인되지 않은”항목이 있으면 할 이유가 없습니다

typedef unsigned long myflags_t;

그러나 특정 상황에서 “부호없는 int”이고 다른 구성에서 “부호없는 long”인 이유가 분명한 경우, 반드시 typedef를 사용하십시오.

(c) 스파 스를 사용하여 문자 검사를위한 새로운 유형 을 문자 그대로 만들 때 .

(d) 예외적 인 상황에서 표준 C99 유형과 동일한 새로운 유형.

눈과 뇌가 ‘uint32_t’와 같은 표준 유형에 익숙해지기까지는 짧은 시간이 걸리지 만 일부 사람들은 어쨌든 사용에 반대합니다.

따라서 Linux 고유의 ‘u8 / u16 / u32 / u64’유형 및 표준 유형과 동일한 서명 된 동등한 유형은 허용되지만 새 코드에서는 필수는 아닙니다.

이미 하나 또는 다른 유형의 세트를 사용하는 기존 코드를 편집 할 때는 해당 코드의 기존 선택 사항을 준수해야합니다.

(e) 사용자 공간에서 사용하기에 안전한 유형.

사용자 공간에 보이는 특정 구조에서는 C99 유형을 요구할 수 없으며 위의 ‘u32’형식을 사용할 수 없습니다. 따라서 사용자 공간과 공유되는 모든 구조에서 __u32 및 유사한 유형을 사용합니다.

다른 경우도있을 수 있지만 규칙 중 하나를 명확하게 일치시킬 수 없으면 기본적으로 typedef를 절대로 사용해서는 안됩니다.

일반적으로 직접 액세스 할 수있는 요소가있는 포인터 또는 구조체 는 typedef 가 아니 어야합니다 .


답변

찬반 양론이 있음이 밝혀졌습니다. 유용한 정보 소스는 “Expert C Programming”( 3 장 ) 의 주요 저서 입니다. 간단히 말해 C에는 태그, 유형, 멤버 이름 및 식별자와 같은 여러 네임 스페이스가 있습니다 . typedef유형의 별칭을 도입하고 태그 네임 스페이스에서 찾습니다. 즉,

typedef struct Tag{
...members...
}Type;

두 가지를 정의합니다. 태그 네임 스페이스에 하나의 태그가 있고 유형 네임 스페이스에 하나의 유형이 있습니다. 그래서 당신은 둘 다 할 수 있습니다 Type myTypestruct Tag myTagType. 선언은 유사 struct Type myType하거나 Tag myTagType불법입니다. 또한 다음과 같은 선언에서 :

typedef Type *Type_ptr;

Type에 대한 포인터를 정의합니다. 우리가 선언하면 :

Type_ptr var1, var2;
struct Tag *myTagType1, myTagType2;

다음 var1, var2myTagType1입력 만하는 포인터 myTagType2하지 않음.

위에서 언급 한 책에서, typedefing structs는 프로그래머가 struct라는 단어를 쓰는 것을 막기 때문에 그다지 유용하지 않다고 언급합니다. 그러나 다른 많은 C 프로그래머와 마찬가지로 반대 의견이 있습니다. C에서 다형성을 구현하고 싶을 때 일부 이름을 난독 화하기도하지만 (커널과 같은 큰 코드베이스에서는 권장하지 않는 이유 입니다) 자세한 내용은 여기를 참조하십시오 . 예:

typedef struct MyWriter_t{
    MyPipe super;
    MyQueue relative;
    uint32_t flags;
...
}MyWriter;

넌 할 수있어:

void my_writer_func(MyPipe *s)
{
    MyWriter *self = (MyWriter *) s;
    uint32_t myFlags = self->flags;
...
}

따라서 캐스팅을 통해 flags내부 구조체 ( MyPipe) 로 외부 멤버 ( )에 액세스 할 수 있습니다 . 나를 (struct MyWriter_ *) s;위해 그러한 기능을 수행 할 때마다 전체 유형을 캐스팅하는 것이 혼란스럽지 않습니다. 이러한 경우, 짧은 참조는 특히 코드에서이 기술을 많이 사용하는 경우 큰 문제입니다.

마지막으로, typedefed 유형 의 마지막 측면 은 매크로와 달리 유형을 확장 할 수 없다는 것입니다. 예를 들어 다음이있는 경우

#define X char[10] or
typedef char Y[10]

그런 다음 선언 할 수 있습니다

unsigned X x; but not
unsigned Y y;

스토리지 지정자 ( volatileconst) 에는 적용되지 않으므로 구조체에 대해서는 실제로 신경 쓰지 않습니다 .