Pregunta

He visto muchos programas que consisten en estructuras como la que se muestra a continuación

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

elem user;

¿Por qué se necesita tan a menudo? ¿Alguna razón específica o área aplicable?

¿Fue útil?

Solución

Como dijo Greg Hewgill, typedef significa que ya no tienes que escribir struct por todos lados. Eso no solo ahorra pulsaciones, sino que también puede hacer que el código sea más limpio, ya que proporciona un poco más de abstracción.

Cosas como

typedef struct {
  int x, y;
} Point;

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

se vuelve más limpio cuando no necesita ver la estructura " struct " palabra clave en todo el lugar, se ve más como si realmente hubiera un tipo llamado " Punto " en tu idioma. Que, después del typedef , es el caso que supongo.

También tenga en cuenta que si bien su ejemplo (y el mío) omitió nombrar a la struct en sí, en realidad nombrarlo también es útil cuando desea proporcionar un tipo opaco. Entonces tendrías un código como este en el encabezado, por ejemplo:

typedef struct Point Point;

Point * point_new(int x, int y);

y luego proporcione la definición struct en el archivo de implementación:

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;
}

En este último caso, no puede devolver el Punto por valor, ya que su definición está oculta para los usuarios del archivo de encabezado. Esta es una técnica utilizada ampliamente en GTK + , por ejemplo.

ACTUALIZACIÓN Tenga en cuenta que también hay proyectos de C muy apreciados en los que este uso de typedef para ocultar struct se considera una mala idea. El kernel de Linux es probablemente el proyecto de este tipo más conocido. Consulte el Capítulo 5 de El documento de Kernel CodingStyle de Linux por las palabras enojadas de Linus. :) Mi punto es que el " debería " en la pregunta tal vez no esté escrito en piedra, después de todo.

Otros consejos

Es sorprendente la cantidad de personas que se equivocan. POR FAVOR, no escriba las estructuras en C, contaminará innecesariamente el espacio de nombres global que normalmente está muy contaminado en programas de C grandes.

Además, las estructuras de tipo typedef sin un nombre de etiqueta son una causa importante de imposición innecesaria de relaciones de ordenación entre archivos de encabezado.

Considera:

#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

Con esta definición, sin usar typedefs, es posible que una unidad compilable incluya foo.h para obtener la definición de FOO_DEF . Si no intenta eliminar la referencia al miembro 'bar' de la estructura foo , no habrá necesidad de incluir el " bar.h " archivo.

Además, dado que los espacios de nombres son diferentes entre los nombres de las etiquetas y los nombres de los miembros, es posible escribir códigos muy legibles como:

struct foo *foo;

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

Dado que los espacios de nombres están separados, no hay conflicto en la denominación de las variables que coinciden con el nombre de la etiqueta de su estructura.

Si tengo que mantener tu código, eliminaré tus estructuras typedef'd.

De un artículo anterior de Dan Saks ( http://www.ddj.com/ cpp / 184403396? pgno = 3 ):


  

Las reglas del lenguaje C para nombrar   Las estructuras son un poco excéntricas, pero   Son bastante inofensivos. Sin embargo cuando   Extendido a las clases en C ++, esas mismas.   Las reglas abren pequeñas grietas para que los insectos   rastrear a través.

     

En C, el nombre s que aparece en

struct s
    {
    ...
    };
     

es una etiqueta. Un nombre de etiqueta no es un tipo   nombre. Dada la definición anterior,   Declaraciones como

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

son errores en C. Debes escribirlos   como

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

Los nombres de uniones y enumeraciones.   También son etiquetas en lugar de tipos.

     

En C, las etiquetas son distintas de todas las demás   nombres (para funciones, tipos,   variables, y constantes de enumeración).   C compiladores mantienen etiquetas en un símbolo.   tabla que es conceptualmente si no   físicamente separado de la mesa   que tiene todos los otros nombres. Por lo tanto,   es posible que un programa C tenga   tanto una etiqueta como otro nombre con   La misma ortografía en el mismo ámbito.   Por ejemplo,

struct s s;
     

es una declaración válida que declara   variable s de tipo struct s. Puede   No sea buena práctica, sino compiladores de C.   debe aceptarlo. Nunca he visto un   razón por la cual C fue diseñado este   camino. Siempre he pensado que era una   error, pero ahí está.

     

Muchos programadores (incluido el tuyo)   en verdad) prefiero pensar en nombres de estructuras   Como nombres de tipo, por lo que definen un alias.   para la etiqueta usando un typedef. por   ejemplo, definiendo

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

te permite usar S en lugar de estructuras,   como en

S x;
S *p;
     

Un programa no puede usar S como el nombre de   tanto un tipo como una variable (o   Función o constante de enumeración):

S S;    // error
     

Esto es bueno.

     

El nombre de la etiqueta en una estructura, unión o   La definición de enumeración es opcional. Muchos   programadores doblan la definición de la estructura   en el typedef y prescindir de la   etiqueta en conjunto, como en:

typedef struct
    {
    ...
    } S;

El artículo vinculado también tiene una discusión acerca de cómo el comportamiento de C ++ de no requerir un typedef puede causar problemas de ocultación de nombres sutiles. Para evitar estos problemas, también es una buena idea typedef sus clases y estructuras en C ++, aunque a primera vista parezca que no es necesario. En C ++, con typedef , la ocultación del nombre se convierte en un error que el compilador le informa en lugar de una fuente oculta de posibles problemas.

El uso de typedef evita tener que escribir struct cada vez que declara una variable de ese tipo:

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

Otra buena razón para tipear siempre los resultados de enumeraciones y estructuras de este problema:

enum EnumDef
{
  FIRST_ITEM,
  SECOND_ITEM
};

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

¿Nota el error tipográfico en EnumDef en la estructura (Enu u mDef)? Esto se compila sin error (o advertencia) y es (según la interpretación literal del estándar C) correcto. El problema es que acabo de crear una nueva definición de enumeración (vacía) dentro de mi estructura. No estoy (según lo previsto) utilizando la definición anterior EnumDef.

Con un tipo de error similar a typdef habría resultado en errores de compilación al utilizar un tipo desconocido:

typedef 
{
  FIRST_ITEM,
  SECOND_ITEM
} EnumDef;

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

Yo abogaría por SIEMPRE las estructuras y enumeraciones de definición de tipo.

No solo para guardar algo de escritura (sin juego de palabras;)), sino porque es más seguro.

estilo de codificación del kernel de Linux El Capítulo 5 ofrece grandes ventajas y desventajas (en su mayoría en contra) de utilizando typedef .

  

Por favor, no uses cosas como " vps_t " ;.

     

Es un error usar typedef para estructuras y punteros. Cuando veas un

vps_t a;
     

en la fuente, ¿qué significa?

     

Por el contrario, si dice

struct virtual_container *a;
     

realmente puedes decir lo que " a " es.

     

Mucha gente piensa que typedefs " ayuda a la legibilidad " ;. No tan. Son útiles solo para:

     

(a) objetos totalmente opacos (donde el typedef se usa activamente para ocultar cuál es el objeto).

     

Ejemplo: " pte_t " etc. objetos opacos a los que solo puede acceder utilizando las funciones de acceso adecuadas.

     

¡NOTA! Opacidad y " funciones de acceso " No son buenos en sí mismos. La razón por la que los tenemos para cosas como pte_t, etc., es que realmente hay absolutamente cero información accesible allí.

     

(b) Borrar tipos de enteros, donde la abstracción ayuda evita la confusión si es " int " o " largo " ;.

     

u8 / u16 / u32 son typedefs perfectamente bien, aunque encajan en la categoría (d) mejor que aquí.

     

¡NOTA! De nuevo, debe haber una razón para esto. Si algo es " unsigned long " ;, entonces no hay razón para hacerlo

typedef unsigned long myflags_t;
     

pero si hay una razón clara por la que, bajo ciertas circunstancias, podría ser un " unsigned int " y bajo otras configuraciones puede ser " sin firma larga " ;, entonces, por todos los medios, siga adelante y use un typedef.

     

(c) cuando utiliza disperso para crear literalmente un tipo nuevo para la comprobación de tipos.

     

(d) Nuevos tipos que son idénticos a los tipos estándar de C99, en ciertas circunstancias excepcionales.

     

Aunque solo llevaría un corto período de tiempo para que los ojos y el cerebro se acostumbren a los tipos estándar como 'uint32_t', algunas personas se oponen a su uso de todos modos.

     

Por lo tanto, los tipos 'u8 / u16 / u32 / u64' específicos de Linux y sus equivalentes firmados que son idénticos a los tipos estándar están permitidos, aunque no son obligatorios en su nuevo código.

     

Al editar el código existente que ya utiliza uno u otro conjunto de tipos, debe ajustarse a las opciones existentes en ese código.

     

(e) Tipos seguros para usar en el espacio de usuario.

     

En ciertas estructuras que son visibles para el espacio de usuario, no podemos requerir tipos C99 y no podemos usar el formulario 'u32' de arriba. Por lo tanto, utilizamos __u32 y tipos similares en todas las estructuras que se comparten con el espacio de usuario.

     

Quizás también haya otros casos, pero la regla básicamente debería ser NUNCA NUNCA use typedef a menos que pueda coincidir claramente con una de esas reglas.

     

En general, un puntero, o una estructura que tiene elementos a los que se puede acceder directamente de manera razonable, nunca debe ser un typedef.

Resulta que hay pros y contras. Una fuente de información útil es el libro seminal " Programación Expert C " ( Capítulo 3 ). Brevemente, en C tiene múltiples espacios de nombres: etiquetas, tipos, nombres de miembros e identificadores . typedef introduce un alias para un tipo y lo ubica en el espacio de nombres de la etiqueta. A saber,

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

define dos cosas. Una etiqueta en el espacio de nombres de la etiqueta y un tipo en el espacio de nombres de tipo. Así que puedes hacer tanto Type myType como struct Tag myTagType . Declaraciones como struct Type myType o Tag myTagType son ilegales. Además, en una declaración como esta:

typedef Type *Type_ptr;

Definimos un puntero a nuestro Tipo. Así que si declaramos:

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

luego var1 , var2 y myTagType1 son punteros al tipo pero myTagType2 no.

En el libro mencionado anteriormente, se menciona que las estructuras de escritura de tipo no son muy útiles ya que solo evita que el programador escriba la palabra estructura. Sin embargo, tengo una objeción, como muchos otros programadores de C Aunque a veces se vuelve a ofuscar algunos nombres (por eso no es recomendable en bases de código grandes como el kernel) cuando se quiere implementar un polimorfismo en C, esto ayuda mucho busque aquí detalles . Ejemplo:

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

puedes hacer:

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

Para que pueda acceder a un miembro externo ( flags ) a través de la estructura interna ( MyPipe ) a través de casting. Para mí es menos confuso lanzar todo el tipo que hacer (struct MyWriter_ *) s; cada vez que desee realizar dicha funcionalidad. En estos casos, las referencias breves son un gran problema, especialmente si emplea la técnica en su código.

Finalmente, el último aspecto con los tipos de typedef es la incapacidad de extenderlos, en contraste con las macros. Si por ejemplo, tienes:

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

entonces puedes declarar

unsigned X x; but not
unsigned Y y;

Realmente no nos interesan las estructuras porque no se aplica a los especificadores de almacenamiento ( volatile y const ).

No creo que las declaraciones hacia adelante sean posibles con typedef. El uso de struct, enum y union permite reenviar declaraciones cuando las dependencias (conocidas) son bidireccionales.

Estilo: El uso de typedef en C ++ tiene bastante sentido. Casi puede ser necesario cuando se trata de plantillas que requieren múltiples parámetros y / o variables. El typedef ayuda a mantener los nombres rectos.

No es así en el lenguaje de programación C. El uso de typedef a menudo no tiene otro propósito que ofuscar el uso de la estructura de datos. Dado que solo se utiliza {struct (6), enum (4), union (5)} número de pulsaciones para declarar un tipo de datos, casi no hay uso para el aliasing de la estructura. ¿Es ese tipo de datos una unión o una estructura? El uso de la declaración directa no tipificada le permite saber de inmediato qué tipo es.

Observe cómo se escribe Linux con una estricta evitación de este aliasing que trae typedef sin sentido. El resultado es un estilo minimalista y limpio.

Comencemos con lo básico y avancemos hacia arriba.

Este es un ejemplo de la definición de la estructura:

struct point
  {
    int x, y;
  };

Aquí el nombre punto es opcional.

Se puede declarar una estructura durante su definición o después.

Declarar durante la definición

struct point
  {
    int x, y;
  } first_point, second_point;

Declarar después de la definición

struct point
  {
    int x, y;
  };
struct point first_point, second_point;

Ahora, tenga en cuenta el último caso anterior; debe escribir struct point para declarar Estructuras de ese tipo si decide crear ese tipo en un punto posterior de su código.

Ingrese typedef . Si pretende crear una nueva Estructura (la Estructura es un tipo de datos personalizado) más adelante en su programa usando el mismo programa, usar typedef durante su definición puede ser una buena idea, ya que puede guardar algunos escribiendo avanzando.

typedef struct point
  {
    int x, y;
  } Points;

Points first_point, second_point;

Una palabra de precaución al nombrar su tipo personalizado

Nada le impide usar el sufijo _t al final de su nombre de tipo personalizado, pero el estándar POSIX se reserva el uso del sufijo _t para denotar los nombres de los tipos de biblioteca estándar.

el nombre que le das (opcionalmente) a la estructura se denomina nombre de etiqueta y, como se ha señalado, no es un tipo en sí mismo. Para llegar al tipo se requiere el prefijo de estructura.

Aparte de GTK +, no estoy seguro de que el tagname se use tan comúnmente como typedef para el tipo de estructura, por lo que en C ++ se reconoce y puede omitir la palabra clave struct y usar el tagname como nombre de tipo también:


    struct MyStruct
    {
      int i;
    };

    // The following is legal in C++:
    MyStruct obj;
    obj.i = 7;

typedef no proporcionará un conjunto co-dependiente de estructuras de datos. Esto no se puede hacer con typdef:

struct bar;
struct foo;

struct foo {
    struct bar *b;
};

struct bar {
    struct foo *f;
};

Por supuesto que siempre puedes agregar:

typedef struct foo foo_t;
typedef struct bar bar_t;

¿Cuál es exactamente el punto de eso?

A > un typdef ayuda en el significado y la documentación de un programa al permitir la creación de sinónimos más significativos para los tipos de datos . Además, ayudan a parametrizar un programa contra problemas de portabilidad (K & amp; R, pg147, C prog lang).

B > una estructura define un tipo . Structs permite agrupar convenientemente una colección de vars por la comodidad de manejo (K & amp; R, pg127, C prog lang.) Como una sola unidad

C > typedef'ing una estructura se explica en A arriba.

D > Para mí, las estructuras son tipos o contenedores personalizados o colecciones o espacios de nombres o tipos complejos, mientras que un typdef es solo un medio para crear más apodos.

Resulta que en C99 se requiere typedef. Está desactualizado, pero muchas herramientas (como HackRank) usan c99 como su implementación en C pura. Y allí se requiere typedef.

No estoy diciendo que deban cambiar (quizás tengan dos opciones de C) si el requisito cambia, los que estudiamos para las entrevistas en el sitio serían SOL.

En el lenguaje de programación 'C', la palabra clave 'typedef' se usa para declarar un nuevo nombre para algún objeto (struct, array, function..enum type). Por ejemplo, usaré un 'struct-s'. En 'C' a menudo declaramos una 'estructura' fuera de la función 'principal'. Por ejemplo:

struct complex{ int real_part, img_part }COMPLEX;

main(){

 struct KOMPLEKS number; // number type is now a struct type
 number.real_part = 3;
 number.img_part = -1;
 printf("Number: %d.%d i \n",number.real_part, number.img_part);

}

Cada vez que decido usar un tipo de estructura necesitaré esta palabra clave 'struct' algo '' nombre '.' typedef 'simplemente cambiará el nombre de ese tipo y puedo usar ese nuevo nombre en mi programa cada vez que lo desee. Entonces nuestro código será:

typedef struct complex{int real_part, img_part; }COMPLEX;
//now COMPLEX is the new name for this structure and if I want to use it without
// a keyword like in the first example 'struct complex number'.

main(){

COMPLEX number; // number is now the same type as in the first example
number.real_part = 1;
number.img)part = 5;
printf("%d %d \n", number.real_part, number.img_part);

}

Si tiene algún objeto local (estructura, matriz, valioso) que se usará en todo su programa, simplemente puede darle un nombre usando un 'typedef'.

En absoluto, en lenguaje C, struct / union / enum son instrucciones de macro procesadas por el preprocesador en lenguaje C (no confunda con el preprocesador que trata " # include " y otros)

entonces:

struct a
{
   int i;
};

struct b
{
   struct a;
   int i;
   int j;
};

la estructura b se gasta como algo así:

struct b
{
    struct a
    {
        int i;
    };
    int i;
    int j;
}

y así, en tiempo de compilación evoluciona en la pila como algo así como: segundo: int ai int i int j

por eso también es difícil tener estructuras independientes, redondee el preprocesador C en un ciclo de declaración que no puede terminar.

typedef son especificadores de tipo, eso significa que solo el compilador C lo procesa y puede hacer lo que quiera para optimizar la implementación del código del ensamblador. Tampoco se gastan miembros de tipo tan estúpidamente como el preprocesador con estructuras, pero usan algoritmos de construcción de referencia más complejos, por lo que construcción como:

typedef struct a A; //anticipated declaration for member declaration

typedef struct a //Implemented declaration
{
    A* b; // member declaration
}A;

está permitido y es completamente funcional. Esta implementación también brinda acceso a la conversión de tipo de compilador y elimina algunos efectos de error cuando el subproceso de ejecución abandona el campo de aplicación de las funciones de inicialización.

Esto significa que en C typedefs están más cerca de la clase C ++ que las estructuras solitarias.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top