C Language
Typedef
Buscar..
Introducción
El mecanismo typedef
permite la creación de alias para otros tipos. No crea nuevos tipos. Las personas a menudo usan typedef
para mejorar la portabilidad del código, para dar alias a los tipos de estructura o unión, o para crear alias para los tipos de función (o puntero de función).
En el estándar C, typedef
se clasifica como una 'clase de almacenamiento' por conveniencia; se produce de forma sintáctica donde pueden aparecer clases de almacenamiento, como static
o extern
.
Sintaxis
- typedef existing_name alias_name;
Observaciones
Desventajas de Typedef
typedef
podría conducir a la contaminación del espacio de nombres en grandes programas de C.
Desventajas de las estructuras Typedef
Además, las estructuras typedef
sin un nombre de etiqueta son una causa importante de la imposición innecesaria de relaciones de ordenación entre archivos de encabezado.
Considerar:
#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 tal definición, sin usar typedefs
, es posible que una unidad de compilación incluya foo.h
para obtener la definición FOO_DEF
. Si no intenta eliminar la referencia al miembro de la bar
de la estructura foo
entonces no habrá necesidad de incluir el archivo bar.h
Typedef vs #define
#define
es una directiva de preprocesador C que también se utiliza para definir los alias para varios tipos de datos similares a typedef
pero con las siguientes diferencias:
typedef
se limita a dar nombres simbólicos a tipos solo cuando como#define
se puede usar para definir alias para valores también.typedef
interpretacióntypedef
es realizada por el compilador mientras que las instrucciones#define
son procesadas por el preprocesador.Tenga en cuenta que
#define cptr char *
seguido decptr a, b;
no hace lo mismo quetypedef char *cptr;
seguido decptr a, b;
. Con#define
,b
es una variable dechar
simple, pero también es un puntero con eltypedef
.
Typedef para Estructuras y Uniones
Puedes dar nombres de alias a una struct
:
typedef struct Person {
char name[32];
int age;
} Person;
Person person;
En comparación con la forma tradicional de declarar estructuras, los programadores no necesitarían tener struct
cada vez que declaran una instancia de esa estructura.
Tenga en cuenta que el nombre de Person
(a diferencia de struct Person
) no se define hasta el último punto y coma. Por lo tanto, para las listas enlazadas y las estructuras de árbol que deben contener un puntero al mismo tipo de estructura, debe utilizar:
typedef struct Person {
char name[32];
int age;
struct Person *next;
} Person;
o:
typedef struct Person Person;
struct Person {
char name[32];
int age;
Person *next;
};
El uso de un typedef
para un tipo de union
es muy similar.
typedef union Float Float;
union Float
{
float f;
char b[sizeof(float)];
};
Se puede utilizar una estructura similar a esta para analizar los bytes que forman un valor float
.
Usos Simples de Typedef
Para dar nombres cortos a un tipo de datos
En lugar de:
long long int foo;
struct mystructure object;
uno puede usar
/* write once */
typedef long long ll;
typedef struct mystructure mystruct;
/* use whenever needed */
ll foo;
mystruct object;
Esto reduce la cantidad de escritura necesaria si el tipo se usa muchas veces en el programa.
Mejora de la portabilidad
Los atributos de los tipos de datos varían entre las diferentes arquitecturas. Por ejemplo, un int
puede ser un tipo de 2 bytes en una implementación y un tipo de 4 bytes en otra. Supongamos que un programa necesita usar un tipo de 4 bytes para ejecutarse correctamente.
En una implementación, deje que el tamaño de int
sea de 2 bytes y el de long
sea de 4 bytes. En otra, deje que el tamaño de int
sea de 4 bytes y el de long
sea de 8 bytes. Si el programa está escrito usando la segunda implementación,
/* program expecting a 4 byte integer */
int foo; /* need to hold 4 bytes to work */
/* some code involving many more ints */
Para que el programa se ejecute en la primera implementación, todas las declaraciones int
tendrán que cambiarse a long
.
/* program now needs long */
long foo; /*need to hold 4 bytes to work */
/* some code involving many more longs - lot to be changed */
Para evitar esto, se puede usar typedef
/* program expecting a 4 byte integer */
typedef int myint; /* need to declare once - only one line to modify if needed */
myint foo; /* need to hold 4 bytes to work */
/* some code involving many more myints */
Entonces, solo la instrucción typedef
tendrá que cambiarse cada vez, en lugar de examinar todo el programa.
El encabezado <stdint.h>
y el encabezado <inttypes.h>
relacionado definen nombres de tipo estándar (usando typedef
) para enteros de varios tamaños, y estos nombres son a menudo la mejor opción en el código moderno que necesita enteros de tamaño fijo. Por ejemplo, uint8_t
es un tipo entero de 8 bits sin signo; int64_t
es un tipo entero de 64 bits con signo. El tipo uintptr_t
es un tipo entero sin signo lo suficientemente grande como para contener cualquier puntero a objeto. Estos tipos son teóricamente opcionales, pero es raro que no estén disponibles. Existen variantes como uint_least16_t
(el tipo de entero sin signo más pequeño con al menos 16 bits) e int_fast32_t
(el tipo de entero con signo más rápido con al menos 32 bits). Además, intmax_t
y uintmax_t
son los tipos de enteros más grandes admitidos por la implementación. Estos tipos son obligatorios.
Especificar un uso o mejorar la legibilidad.
Si un conjunto de datos tiene un propósito particular, uno puede usar typedef
para darle un nombre significativo. Además, si la propiedad de los datos cambia de modo que el tipo base deba cambiar, solo se deberá cambiar la declaración typedef
, en lugar de examinar todo el programa.
Typedef para punteros de función
Podemos usar typedef
para simplificar el uso de punteros de función. Imagina que tenemos algunas funciones, todas con la misma firma, que usan su argumento para imprimir algo de diferentes maneras:
#include<stdio.h>
void print_to_n(int n)
{
for (int i = 1; i <= n; ++i)
printf("%d\n", i);
}
void print_n(int n)
{
printf("%d\n, n);
}
Ahora podemos usar typedef
para crear un tipo de puntero de función llamado impresora:
typedef void (*printer_t)(int);
Esto crea un tipo, llamado printer_t
para un puntero a una función que toma un solo argumento int
y no devuelve nada, lo que coincide con la firma de las funciones que tenemos arriba. Para usarlo creamos una variable del tipo creado y le asignamos un puntero a una de las funciones en cuestión:
printer_t p = &print_to_n;
void (*p)(int) = &print_to_n; // This would be required without the type
Luego, para llamar a la función apuntada por la variable de puntero de función:
p(5); // Prints 1 2 3 4 5 on separate lines
(*p)(5); // So does this
Por lo tanto, typedef
permite una sintaxis más simple cuando se trata de punteros de función. Esto se hace más evidente cuando los punteros de función se usan en situaciones más complejas, como argumentos a funciones.
Si está utilizando una función que toma un puntero de función como parámetro sin un tipo de puntero de función definido, la definición de la función sería
void foo (void (*printer)(int), int y){
//code
printer(y);
//code
}
Sin embargo, con el typedef
es:
void foo (printer_t printer, int y){
//code
printer(y);
//code
}
Del mismo modo, las funciones pueden devolver punteros a funciones y, de nuevo, el uso de typedef
puede hacer que la sintaxis sea más simple al hacerlo.
Un ejemplo clásico es la función de signal
de <signal.h>
. La declaración para ello (de la norma C) es:
void (*signal(int sig, void (*func)(int)))(int);
Esa es una función que toma dos argumentos: un int
y un puntero a una función que toma un int
como un argumento y no devuelve nada, y que devuelve un puntero para que funcione como su segundo argumento.
Si definimos un tipo SigCatcher
como un alias para el puntero al tipo de función:
typedef void (*SigCatcher)(int);
entonces podríamos declarar signal()
usando:
SigCatcher signal(int sig, SigCatcher func);
En general, esto es más fácil de entender (aunque el estándar C no eligió definir un tipo para realizar el trabajo). La función de signal
toma dos argumentos, un int
y un SigCatcher
, y devuelve un SigCatcher
, donde un SigCatcher
es un puntero a una función que toma un argumento int
y no devuelve nada.
Aunque el uso de los nombres typedef
para los tipos de puntero a función facilita la vida, también puede generar confusión para otras personas que mantendrán su código más adelante, así que úselo con precaución y la documentación adecuada. Véase también punteros a funciones .