stringtranslate.com

definición de tipo

typedef es una palabra clave reservada en los lenguajes de programación C , C++ y Objective-C . Se utiliza para crear un nombre adicional ( alias ) para otro tipo de datos , pero no crea un nuevo tipo, [1] excepto en el caso poco conocido de un typedef calificado de un tipo de matriz donde los calificadores de typedef se transfieren al tipo de elemento de la matriz. [2] Como tal, se utiliza a menudo para simplificar la sintaxis de la declaración de estructuras de datos complejas que consisten en tipos de estructura y unión , aunque también se utiliza comúnmente para proporcionar nombres de tipo descriptivos específicos para tipos de datos enteros de distintos tamaños. [1]

Sintaxis

Una declaración typedef sigue la misma sintaxis que la declaración de cualquier otro identificador de C. La palabra clave typedefen sí es un especificador, lo que significa que, si bien normalmente aparece al comienzo de la declaración, también puede aparecer después de los especificadores de tipo o entre dos de ellos. [3] [4]

En la biblioteca estándar de C y en las especificaciones POSIX , el identificador de la definición de typedef suele ir acompañado del sufijo _t, como en size_ty time_t. Esto se practica en otros sistemas de codificación, aunque POSIX reserva explícitamente esta práctica para los tipos de datos POSIX.

Ejemplos

Esto crea el tipo lengthcomo sinónimo del tipo int:

typedef int longitud ;  

Uso de la documentación

Una declaración typedef se puede utilizar como documentación indicando el significado de una variable dentro del contexto de programación, por ejemplo, puede incluir la expresión de una unidad de medida o de recuentos. Las declaraciones genéricas,

int velocidad_actual ; int puntuación_alta ;  void felicitar ( int tu_puntuación ) { if ( tu_puntuación > puntuación_alta ) { // ... } }          

Puede expresarse declarando tipos específicos del contexto:

typedef int km_por_hora ; typedef int puntos ;    // `km_per_hour` es sinónimo de `int` aquí, y por lo tanto, el compilador trata // nuestras nuevas variables como números enteros. km_per_hour current_speed ; points high_score ;  void felicitar ( puntos tu_puntuación ) { if ( tu_puntuación > puntuación_alta ) { // ... } }          

Ambas secciones de código se ejecutan de forma idéntica. Sin embargo, el uso de declaraciones typedef en el segundo bloque de código deja claro que las dos variables, aunque representan el mismo tipo de datos int, almacenan datos diferentes o incompatibles. La definición en congratulate()of your_scoreindica al programador que current_speed(o cualquier otra variable no declarada como a points) no debe pasarse como argumento. Esto no sería tan evidente si ambas se declararan como variables de intdatatype. Sin embargo, la indicación es solo para el programador ; el compilador de C/C++ considera que ambas variables son de tipo inty no marca advertencias de desajuste de tipos ni errores por tipos de argumentos "incorrectos" para congratulate(points your_score)en el fragmento de código siguiente:

void foo () { km_por_hora km100 = 100 ; felicitar ( km100 ); }       

Simplificación de declaraciones

Se puede utilizar un typedef para simplificar las declaraciones de objetos que tienen tipos con nombres detallados, como struct , union o tipos de puntero . [5] Por ejemplo,

estructura MyStruct { int datos1 ; char datos2 ; };      

define el tipo de datos struct MyStruct. En C, en ausencia de una definición de tipo, se debe utilizar el nombre completo del tipo para declarar variables de ese tipo:

estructura MiEstructura a ;  

Una declaración de typedef puede proporcionar un nombre de tipo más simple que no incluya struct. Por ejemplo, con la declaración de tipo

typedef struct MiEstructura nuevotipo ;   

La declaración de variable se puede reducir a:

nuevo tipo a ; 

La declaración de estructura y typedef también se pueden combinar en una sola declaración:

typedef struct MyStruct { int datos1 ; char datos2 ; } nuevo tipo ;        

, incluso cuando no se declara ninguna etiqueta:

typedef struct { int datos1 ; char datos2 ; } nuevo tipo ;       

En C++ , a diferencia de C, las etiquetas de los tipos class, struct, uniony enum(como el " MyStruct" anterior) se pueden usar automáticamente por sí mismas como alias para los nombres de tipo completos, de forma muy similar a si hubiera definiciones de tipo explícitas que lo declararan en el punto de la declaración de tipo:

estructura MiEstructura x ; MiEstructura y ;   

De hecho, para classlos tipos struct, y union, C++ llama a la etiqueta un nombre de clase.

La correspondencia entre esta característica de C++ y typedef es muy fuerte, y se extiende al hecho de que es posible ocultar el nombre de tipo simple en un ámbito anidado al declararlo como el identificador de otro tipo de entidad. En tal caso, el nombre de tipo completo al estilo C (un "especificador de tipo elaborado") todavía se puede usar para hacer referencia al tipo de clase o enumeración.

En C++, entonces, MyStructse puede utilizar en cualquier lugar que newtypese pueda utilizar, siempre que no haya otras declaraciones de estos identificadores. Sin embargo, lo inverso no es cierto, ya que C++ requiere el nombre de la clase para algunos propósitos, como los nombres de los métodos constructores.

Un ejemplo notorio en el que incluso C++ necesita la structpalabra clave es la llamada al sistema POSIX stat que utiliza una estructura del mismo nombre en sus argumentos:

int stat ( const char * nombre_archivo , struct stat * buf ) { // ... }       

Aquí tanto C como C++ necesitan la structpalabra clave en la definición del parámetro. [ dudosodiscutir ]

Punteros

El typedef se puede utilizar para definir un nuevo tipo de puntero.

tipo de definición int * intptr ;  intptr ptr ; // Igual que: // int *ptr;

intptres un nuevo alias con el tipo de puntero int *. La definición, intptr ptr;, define una variable ptrcon el tipo int *. Por lo tanto, ptres un puntero que puede apuntar a una variable de tipo int.

El uso de typedef para definir un nuevo tipo de puntero puede generar confusión en ocasiones. Por ejemplo:

tipo de definición int * intptr ;  // Tanto 'cliff' como 'allen' son de tipo int*. intptr cliff , allen ;  // 'cliff2' es de tipo int*, pero 'allen2' es de tipo int**. intptr cliff2 , * allen2 ;  // Igual que: // intptr cliff2; // intptr *allen2;

Arriba, intptr cliff, allen;significa definir 2 variables con int*tipo para ambas. Esto se debe a que un tipo definido por typedef es un tipo, no una expansión. En otras palabras, intptr, que es el int*tipo, decora tanto cliffcomo allen. Para intptr cliff2, *allen2;, el intptrtipo decora el cliff2y *allen2. Por lo tanto, intptr cliff2, *allen2;es equivalente a 2 definiciones separadas, intptr cliff2;y intptr *allen2. intptr *allen2significa que allen2es un puntero que apunta a una memoria con int*tipo. En pocas palabras, allen2tiene el tipo, int**.

Punteros constantes

Nuevamente, debido a que typedef define un tipo, no una expansión, las declaraciones que utilizan el calificador const pueden producir resultados inesperados o poco intuitivos. El siguiente ejemplo declara un puntero constante a un tipo entero, no un puntero a un entero constante:

tipo de definición int * intptr ;  constante intptr ptr = NULL ;    // Igual que: // int *const ptr = NULL;

Dado que es un puntero constante, debe inicializarse en la declaración.

Estructuras y punteros de estructura

Las definiciones de tipos también pueden simplificar las definiciones o declaraciones de tipos de punteros de estructura . Considere lo siguiente:

estructura Nodo { int datos ; estructura Nodo * nextptr ; };       

Usando typedef, el código anterior se puede reescribir de la siguiente manera:

typedef struct Nodo Nodo ;   struct Nodo { int datos ; Nodo * nextptr ; };      

En C, se pueden declarar múltiples variables del mismo tipo en una sola declaración, incluso mezclando la estructura con punteros o no punteros. Sin embargo, sería necesario anteponer un asterisco a cada variable para designarla como puntero. En lo que sigue, un programador podría suponer que errptrefectivamente era un Node *, pero un error tipográfico significa que errptres un Node. Esto puede dar lugar a errores de sintaxis sutiles.

estructura Nodo * startptr , * endptr , * curptr , * prevptr , errptr , * refptr ;       

Al definir el typedef Node *, se asegura que todas las variables sean tipos de puntero de estructura, o sea, que cada variable sea un tipo de puntero que apunta a un tipo de estructura .

typedef struct Nodo * NodePtr ;   NodePtr inicioptr , finptr , curptr , prevptr , errptr , refptr ;      

Punteros de función

int do_math ( float arg1 , int arg2 ) { return arg2 ; }       int llamar_a_función ( int ( * llamar_a_esta )( float , int )) { int salida = llamar_a_esta ( 5.5 , 7 );          devolver salida ; } int resultado_final = llamar_a_función ( & hacer_matemática );   

El código anterior se puede reescribir con especificaciones typedef:

typedef int ( * MathFunc )( float , int );   int do_math ( float arg1 , int arg2 ) { return arg2 ; }       int llamar_a_func ( MathFunc llamar_esta ) { int salida = llamar_esta ( 5.5 , 7 );         devolver salida ; } int resultado_final = llamar_a_función ( & hacer_matemática );   

Aquí MathFuncse muestra el nuevo alias para el tipo. A MathFunces un puntero a una función que devuelve un entero y toma como argumento un número flotante seguido de un entero.

Cuando una función devuelve un puntero de función , puede resultar aún más confuso sin typedef. El siguiente es el prototipo de función de signal(3) de FreeBSD :

void ( * señal ( int sig , void ( * func )( int )))( int );    

La declaración de función anterior es críptica, ya que no muestra claramente qué acepta la función como argumento ni el tipo que devuelve. Un programador novato puede incluso suponer que la función acepta un solo como argumento inty no devuelve nada, pero en realidad también necesita un puntero de función y devuelve otro puntero de función. Se puede escribir de forma más clara:

tipo definido void ( * sighandler_t )( int );  sighandler_t señal ( int sig , sighandler_t func );    

Matrices

También se puede utilizar una definición de tipo para simplificar la definición de tipos de matriz. Por ejemplo,

typedef char arrType [ 6 ];  arrType arr = { 1 , 2 , 3 , 4 , 5 , 6 }; arrType * pArr ;         // Igual que: // char arr[6] = {1, 2, 3, 4, 5, 6}; // char (*pArr)[6];

Aquí arrTypese muestra el nuevo alias del char[6]tipo, que es un tipo de matriz con 6 elementos. Para arrType *pArr;, pArres un puntero que apunta a la memoria del char[6]tipo.

Tipos de yeso

Un typedef se crea utilizando la sintaxis de definición de tipo , pero se puede utilizar como si se hubiera creado utilizando la sintaxis de conversión de tipo . ( La conversión de tipo cambia un tipo de datos). Por ejemplo, en cada línea después de la primera línea de:

// `funcptr` es un puntero a una función que toma un `double` y devuelve un `int`. typedef int ( * funcptr )( double );  // Válido tanto en C como en C++. funcptr x = ( funcptr ) NULL ;    // Sólo válido en C++. funcptr y = funcptr ( NULL ); funcptr z = static_cast < funcptr > ( NULL );      

funcptrse utiliza en el lado izquierdo para declarar una variable y en el lado derecho para convertir un valor. Por lo tanto, los programadores que no desean saber cómo convertir la sintaxis de definición a la sintaxis de conversión de tipo pueden utilizar typedef.

Sin el typedef, por lo general no es posible utilizar la sintaxis de definición y la sintaxis de conversión de forma intercambiable. Por ejemplo:

vacío * p = NULL ;   // Esto es legal. int ( * x )( double ) = ( int ( * )( double )) p ;     // El lado izquierdo no es legal. int ( * )( double ) y = ( int ( * )( double )) p ;      // El lado derecho no es legal. int ( * z )( double ) = ( int ( * p )( double ));    

Uso en C++

En C++, los nombres de tipos pueden ser complejos y typedef proporciona un mecanismo para asignar un nombre simple al tipo.

std :: vector < std :: par < std :: cadena , int >> valores ;  para ( std :: vector < std :: par < std :: cadena , int >> :: const_iterator i = valores.begin ( ) ; i != valores.end ( ); ++ i ) { std :: par < std :: cadena , int > const & t = * i ;                 // ... }

y

typedef std :: par < std :: cadena , int > valor_t ; typedef std :: vector < valor_t > valores_t ;     valores_t valores ; para ( valores_t :: const_iterator i = valores.begin ( ); i ! = valores.end ( ) ; ++ i ) { valor_t const & t = * i ;               // ... }

C++11 introdujo la posibilidad de expresar definiciones de tipos con usingen lugar de typedef. Por ejemplo, las dos definiciones de tipos anteriores podrían escribirse de forma equivalente como

usando valor_t = std :: par < std :: cadena , int > ; usando valores_t = std :: vector < valor_t > ;       

Usar con plantillas

C++03 no proporciona definiciones de tipos con plantillas . Por ejemplo, para tener stringpair<T>una representación std::pair<std::string, T>para cada tipo, no Tse puede utilizar:

plantilla < typename T > typedef std :: pair < ​​std :: string , T > stringpair < ​​T > ; // No funciona     

Sin embargo, si uno está dispuesto a aceptar stringpair<T>::typeen lugar de stringpair<T>, entonces es posible lograr el resultado deseado a través de un typedef dentro de una clase o estructura con plantilla que de otro modo no se usaría:

plantilla < typename T > clase stringpair { private : // Evitar la instanciación de `stringpair<T>`. stringpair (); public : // Hacer que `stringpair<T>::type` represente `std::pair<std::string, T>`. typedef std :: pair < ​​std :: string , T > type ; };         // Declarar una variable de tipo `std::pair<std::string, int>`. stringpair < ​​int >:: tipo mi_par_de_cadenas_y_int ; 

En C++11 , las definiciones de tipos con plantilla se agregan con la siguiente sintaxis, que requiere la usingpalabra clave en lugar de la typedefpalabra clave. (Ver alias de plantilla ). [6]

plantilla < typename T > usando stringpair = std :: pair < ​​std :: string , T > ;      // Declarar una variable de tipo `std::pair<std::string, int>`. stringpair < ​​int > mi_par_de_cadenas_y_int ; 

Otros idiomas

En SystemVerilog , typedef se comporta exactamente de la misma manera que en C y C++. [7]

En muchos lenguajes funcionales de tipado estático, como Haskell , Miranda , OCaml , etc., se pueden definir sinónimos de tipo , que son los mismos que las definiciones de tipo en C. Un ejemplo en Haskell:

tipo ParDeInts = ( Int , Int )    

Este ejemplo ha definido un sinónimo de tipo PairOfIntscomo un tipo entero.

En Seed7, la definición de un tipo constante se utiliza para introducir un sinónimo para un tipo:

tipo constante: myVector es un entero de matriz;

En Swift , se utiliza la typealiaspalabra clave para crear un typedef:

alias de tipo  ParDeInts  =  ( Int ,  Int )

C# contiene una característica que es similar al typedef o la usingsintaxis de C++. [8] [6]

usando newType = global :: System.Runtime.Interop.Marshal ; usando otherType = Enums.MyEnumType ; usando StringListMap = System.Collections.Generic.Dictionary < cadena , System.Collections.Generic.List < cadena >> ;          

En D la palabra clave alias[9] permite crear sinónimos de tipo o de tipo parcial.

struct Foo ( T ){} alias FooInt = Foo ! int ; alias Fun = int delegate ( int );        

Preocupaciones sobre el uso

Kernighan y Ritchie indicaron dos razones para usar un typedef. [1] En primer lugar, proporciona un medio para hacer que un programa sea más portátil o más fácil de mantener. En lugar de tener que cambiar un tipo en cada aparición en los archivos fuente del programa, solo se necesita cambiar una única declaración typedef. size_ty ptrdiff_ten <stdlib.h>son dichos nombres de typedef. En segundo lugar, un typedef puede hacer que una definición o declaración compleja sea más fácil de entender.

Algunos programadores se oponen al uso extensivo de las definiciones de tipos. La mayoría de los argumentos se centran en la idea de que las definiciones de tipos simplemente ocultan el tipo de datos real de una variable. Por ejemplo, Greg Kroah-Hartman , un hacker y documentalista del kernel de Linux , desaconseja su uso para cualquier cosa que no sean declaraciones de prototipos de funciones. Argumenta que esta práctica no solo ofusca innecesariamente el código, sino que también puede hacer que los programadores utilicen incorrectamente, por accidente, estructuras grandes pensando que son tipos simples. [10]

Véase también

Referencias

  1. ^ abc Kernighan, Brian W. ; Ritchie, Dennis M. (1988). El lenguaje de programación C (2.ª ed.). Englewood Cliffs, Nueva Jersey.: Prentice Hall. pág. 147. ISBN 0-13-110362-8. Recuperado el 18 de junio de 2016. C proporciona una función llamada typedef para crear nuevos nombres de tipos de datos. … Debe enfatizarse que una declaración typedef no crea un nuevo tipo en ningún sentido; simplemente agrega un nuevo nombre para algún tipo existente.
  2. ^ "calificador de tipo constante". cppreference.com . Consultado el 20 de octubre de 2020 .
  3. ^ "especificador typedef (C++)". cppreference.com . Consultado el 18 de junio de 2016 .
  4. ^ "Declaración typedef (C)". cppreference.com . Consultado el 18 de junio de 2016 .
  5. ^ Deitel, Paul J.; Deitel, HM (2007). C cómo programar (5.ª ed.). Upper Saddle River, NJ: Pearson Prentice Hall. ISBN 9780132404167. Recuperado el 12 de septiembre de 2012. Los nombres de los tipos de estructura a menudo se definen con typedef para crear nombres de tipo más cortos.
  6. ^ ab "Alias ​​de tipo, plantilla de alias (desde C++11) - cppreference.com". es.cppreference.com . Consultado el 25 de septiembre de 2018 .
  7. ^ Tala, Deepak Kumar. "Tipos de datos de SystemVerilog, parte V". www.asic-world.com . ASIC World . Consultado el 25 de septiembre de 2018 .
  8. ^ "Documentación técnica retirada de Visual Studio 2003".
  9. ^ "Declaraciones - Lenguaje de programación D". dlang.org . Consultado el 28 de mayo de 2017 .
  10. ^ Kroah-Hartman, Greg (1 de julio de 2002). "Estilo de codificación del núcleo de Linux adecuado". Linux Journal . Consultado el 23 de septiembre de 2007. El uso de una definición de tipo solo oculta el tipo real de una variable.