Definitivamente, todos hemos usado typedef
sy #define
s una u otra vez. Hoy, mientras trabajaba con ellos, comencé a reflexionar sobre algo.
Considere las siguientes 2 situaciones para usar int
el tipo de datos con otro nombre:
typedef int MYINTEGER
y
#define MYINTEGER int
Al igual que en la situación anterior, podemos, en muchas situaciones, lograr muy bien algo usando #define, y también hacer lo mismo usando typedef, aunque las formas en que hacemos lo mismo pueden ser bastante diferentes. #define también puede realizar acciones MACRO que un typedef no puede.
Aunque la razón básica para usarlos es diferente, ¿qué tan diferente es su funcionamiento? ¿Cuándo se debe preferir uno sobre el otro cuando se pueden usar ambos? Además, ¿se garantiza que uno sea más rápido que el otro en qué situaciones? (por ejemplo, #define es una directiva de preprocesador, por lo que todo se hace mucho antes que durante la compilación o el tiempo de ejecución).
fuente
Respuestas:
Una
typedef
generalmente se prefiere a menos que haya alguna razón extraña que usted necesita específicamente una macro.Las macros hacen sustitución textual, lo que puede hacer una violencia considerable a la semántica del código. Por ejemplo, dado:
podrías escribir legalmente:
porque
short MYINTEGER
se expande ashort int
.Por otro lado, con un typedef:
el nombre
MYINTEGER
es otro nombre para el tipoint
, no una sustitución textual para la palabra clave "int".Las cosas empeoran aún más con los tipos más complicados. Por ejemplo, dado esto:
a
,b
yc
son todos punteros, perod
es achar
, porque la última línea se expande a:que es equivalente a
(Las definiciones de tipo para los tipos de puntero generalmente no son una buena idea, pero esto ilustra el punto).
Otro caso extraño:
fuente
typedef
, pero la forma correcta de construir una macro que hace algo con lo que sigue es el uso de argumentos:#define CHAR_PTR(x) char *x
. Esto al menos hará que el compilador se encienda si se usa incorrectamente.const CHAR_PTR mutable_pointer_to_const_char; const char_ptr const_pointer_to_mutable_char;
De lejos, el mayor problema con las macros es que no tienen alcance. Eso solo garantiza el uso de typedef. Además, es más claro semánticamente. Cuando alguien que lee su código ve una definición, no sabrá de qué se trata hasta que lo lea y comprenda toda la macro. Un typedef le dice al lector que se definirá un nombre de tipo. (Debo mencionar que estoy hablando de C ++. No estoy seguro acerca del alcance de typedef para C, pero supongo que es similar).
fuente
El código fuente está escrito principalmente para otros desarrolladores. Las computadoras usan una versión compilada.
En esta perspectiva
typedef
tiene un significado que#define
no tiene.fuente
La respuesta de Keith Thompson es muy buena y, junto con los puntos adicionales de Tamás Szelei sobre el alcance, deberían proporcionarle todos los antecedentes que necesita.
Siempre debe considerar las macros como el último recurso de los desesperados. Hay ciertas cosas que solo puedes hacer con macros. Incluso entonces, deberías pensar mucho si realmente quieres hacerlo. La cantidad de dolor en la depuración que puede ser causada por macros sutilmente rotas es considerable y lo hará mirar a través de archivos preprocesados. Vale la pena hacerlo solo una vez, para tener una idea del alcance del problema en C ++: solo el tamaño del archivo preprocesado puede ser revelador.
fuente
Además de todos los comentarios válidos sobre el alcance y el reemplazo de texto ya dados, #define tampoco es totalmente compatible con typedef! A saber, cuando se trata del caso de los punteros de función.
Esto declara un tipo
fptr_t
que es un puntero a una función del tipovoid func(void)
.No puede declarar este tipo con una macro.
#define fptr_t void(*)(void)
Obviamente no funcionará. Tendría que escribir algo oscuro como#define fptr_t(name) void(*name)(void)
, que realmente no tiene ningún sentido en el lenguaje C, donde no tenemos constructores.Los punteros de matriz tampoco se pueden declarar con #define:
typedef int(*arr_ptr)[10];
Y aunque C no tiene soporte de lenguaje para la seguridad de tipos que valga la pena mencionar, otro caso en el que typedef y #define no son compatibles es cuando realiza conversiones de tipos sospechosas. La herramienta de compilación y / o analizador astático podría dar advertencias para tales conversiones si usa typedef.
fuente
char *(*(*foo())[])();
(foo
es una función que devuelve un puntero a una matriz de punteros a funciones que devuelven el punterochar
).Use la herramienta con la menor potencia que hace el trabajo y la que tiene más advertencias. #define se evalúa en el preprocesador, en gran medida estás solo allí. typedef es evaluado por el compilador. Se dan verificaciones y typedef solo puede definir tipos, como su nombre lo indica. Entonces, en tu ejemplo, definitivamente ve por typedef.
fuente
Más allá de los argumentos normales en contra de define, ¿cómo escribirías esta función usando Macros?
Obviamente, este es un ejemplo trivial, pero esa función puede sumar cualquier secuencia iterable hacia adelante de un tipo que implemente la suma *. Los typedefs utilizados de esta manera son un elemento importante de la programación genérica .
* Acabo de escribir esto aquí, dejo compilarlo como un ejercicio para el lector :-)
EDITAR:
Esta respuesta parece estar causando mucha confusión, así que déjame sacarla más. Si tuviera que mirar dentro de la definición de un vector STL, vería algo similar a lo siguiente:
El uso de typedefs dentro de los contenedores estándar permite que una función genérica (como la que creé anteriormente) haga referencia a estos tipos. La función "Suma" se basa en el tipo de contenedor (
std::vector<int>
), no en el tipo contenido dentro del contenedor (int
). Sin typedef no sería posible hacer referencia a ese tipo interno.Por lo tanto, typedefs es fundamental para Modern C ++ y esto no es posible con Macros.
fuente
typedef
, no macros vs funciones en línea.typedef
se ajusta a la filosofía de C ++: todas las comprobaciones / afirmaciones posibles en tiempo de compilación.#define
es solo un truco de preprocesador que oculta mucha semántica al compilador. No debe preocuparse más por el rendimiento de la compilación que por la corrección del código.Cuando crea un nuevo tipo, está definiendo una nueva "cosa" que manipula el dominio de su programa. Entonces puede usar esta "cosa" para componer funciones y clases, y puede usar el compilador para su beneficio para hacer comprobaciones estáticas. De todos modos, como C ++ es compatible con C, hay mucha conversión implícita entre
int
tipos basados en int que no generan advertencias. Por lo tanto, no obtiene toda la potencia de las comprobaciones estáticas en este caso. Sin embargo, puede reemplazar sutypedef
con unenum
para encontrar conversiones implícitas. Por ejemplo: si lo has hechotypedef int Age;
, puedes reemplazarlo conenum Age { };
y obtendrás todo tipo de errores mediante conversiones implícitas entreAge
yint
.Otra cosa: el
typedef
puede estar dentro de anamespace
.fuente
El uso de define en lugar de typedefs es problemático bajo otro aspecto importante, y es el concepto de rasgos de tipo. Considere diferentes clases (piense en contenedores estándar), todas las cuales definen sus typedefs específicos. Puede escribir código genérico haciendo referencia a los typedefs. Ejemplos de esto incluyen los requisitos generales del contenedor (c ++ estándar 23.2.1 [container.requirements.general]) como
Todo esto no se puede expresar de manera genérica con una macro porque no tiene un alcance.
fuente
No olvide las implicaciones de esto en su depurador. Algunos depuradores no manejan #define muy bien. Juega con ambos en el depurador que utilizas. Recuerde, pasará más tiempo leyéndolo que escribiéndolo.
fuente
"#define" reemplazará lo que escribió después, typedef creará type. Entonces, si desea tener un tipo personalizado, use typedef. Si necesita macros, use define.
fuente