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ón typedef es realizada por el compilador mientras que las instrucciones #define son procesadas por el preprocesador.

  • Tenga en cuenta que #define cptr char * seguido de cptr a, b; no hace lo mismo que typedef char *cptr; seguido de cptr a, b; . Con #define , b es una variable de char simple, pero también es un puntero con el typedef .

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.

C99

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 .



Modified text is an extract of the original Stack Overflow Documentation
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow