Funciones en línea vs macros de preprocesador


Respuestas:


127

Las macros de preprocesador son solo patrones de sustitución aplicados a su código. Se pueden usar en casi cualquier lugar de su código porque se reemplazan con sus expansiones antes de que comience la compilación.

Las funciones en línea son funciones reales cuyo cuerpo se inyecta directamente en su sitio de llamada. Solo se pueden usar cuando sea apropiada una llamada a función.

Ahora, en lo que respecta al uso de macros frente a funciones en línea en un contexto similar a una función, tenga en cuenta que:

  • Las macros no son seguras para los tipos y se pueden expandir independientemente de si son sintácticamente correctas; la fase de compilación informará los errores resultantes de problemas de expansión de macros.
  • Las macros se pueden usar en contextos donde no se espera, lo que genera problemas
  • Las macros son más flexibles, ya que pueden expandir otras macros, mientras que las funciones en línea no necesariamente hacen esto.
  • Las macros pueden producir efectos secundarios debido a su expansión, ya que las expresiones de entrada se copian dondequiera que aparezcan en el patrón.
  • No siempre se garantiza que la función en línea esté en línea; algunos compiladores solo hacen esto en versiones de lanzamiento, o cuando están específicamente configurados para hacerlo. Además, en algunos casos, es posible que no sea posible inlining.
  • Las funciones en línea pueden proporcionar un alcance para las variables (particularmente las estáticas), las macros de preprocesador solo pueden hacer esto en bloques de código {...} y las variables estáticas no se comportarán exactamente de la misma manera.

39
No siempre se garantiza que la función en línea esté en línea: porque el compilador no estará en línea si hacerlo generará un código más lento, etc. El compilador hace muchos análisis que el ingeniero no puede y hace lo correcto.
Martin York

14
Creo que las funciones recursivas son otro ejemplo en el que la mayoría de los compiladores ignoran la inserción.
LBushkin

¿Hay diferencias importantes en C en comparación con C ++ en este caso?
rzetterberg

7
Un punto que no se menciona es que las marcas de compilación pueden influir en la inserción. Por ejemplo, cuando construye para la velocidad máxima (como GCC -O2 / -O3), el compilador elegirá en línea muchas funciones, pero cuando construye para el tamaño mínimo (-Os), normalmente las funciones en línea se llaman solo una vez (o funciones muy pequeñas ). Con las macros no existe esa opción.
dbrank0

Las macros no pueden cubrir con un especificador de acceso (como, privado o protegido) mientras que las funciones en línea son posibles.
Hit's

78

Primero, las macros del preprocesador son simplemente "copiar y pegar" en el código antes de la compilación. Por lo tanto, no hay verificación de tipos y pueden aparecer algunos efectos secundarios.

Por ejemplo, si desea comparar 2 valores:

#define max(a,b) ((a<b)?b:a)

Los efectos secundarios aparecen si lo usa, max(a++,b++)por ejemplo ( ao bse incrementará dos veces). En su lugar, use (por ejemplo)

inline int max( int a, int b) { return ((a<b)?b:a); }

3
Solo quiero agregar a su ejemplo que, además de los efectos secundarios, la macro también puede introducir una carga de trabajo adicional, considere que max(fibonacci(100), factorial(10000))la más grande se calculará dos veces :(
watashiSHUN

Todo el mundo habla sobre la verificación de tipos, pero solo proporcionaste un ejemplo del mundo real, por eso voto a favor esta respuesta.
Ivanzinho

16

La función Inline es expandida por el compilador mientras que las macros son expandidas por el Preprocesador, que es una mera sustitución textual.

  • No hay verificación de tipos durante la invocación de macros, mientras que la verificación de tipos se realiza durante la llamada a la función.

  • Pueden producirse resultados no deseados e ineficacia durante la expansión macro debido a la reevaluación de los argumentos y el orden de las operaciones. Por ejemplo

    #define MAX(a,b) ((a)>(b) ? (a) : (b))
    int i = 5, j = MAX(i++, 0);

    resultaría en

    int i = 5, j = ((i++)>(0) ? (i++) : (0));
  • Los argumentos macro no se evalúan antes de la expansión macro

    #define MUL(a, b) a*b
    int main()
    {
      // The macro is expended as 2 + 3 * 3 + 5, not as 5*8
      printf("%d", MUL(2+3, 3+5));
     return 0;
    }
    // Output: 16`
  • La palabra clave return no se puede utilizar en macros para devolver valores como en el caso de funciones.

  • Las funciones en línea pueden sobrecargarse

  • Los tokens pasados ​​a macros pueden concatenarse usando el operador ## llamado operador Token-Pasting.

  • Las macros se utilizan generalmente para la reutilización de código, donde las funciones en línea se utilizan para eliminar la sobrecarga de tiempo (exceso de tiempo) durante la llamada a la función (evitando un salto a una subrutina).


13

La diferencia clave es la verificación de tipos. El compilador comprobará si lo que pasa como valores de entrada es de tipos que se pueden pasar a la función. Eso no es cierto con las macros de preprocesador: se expanden antes de cualquier verificación de tipo y eso puede causar errores graves y difíciles de detectar.

A continuación se describen otros puntos menos obvios.


11

Para agregar otra diferencia a las ya dadas: no puede pasar por una #defineen el depurador, pero puede pasar por una función en línea.



3

Las funciones en línea son similares a las macros (porque el código de la función se expande en el punto de la llamada en tiempo de compilación), las funciones en línea son analizadas por el compilador, mientras que las macros son expandidas por el preprocesador. Como resultado, existen varias diferencias importantes:

  • Las funciones en línea siguen todos los protocolos de seguridad del tipo que se aplican a las funciones normales.
  • Las funciones en línea se especifican utilizando la misma sintaxis que cualquier otra función, excepto que incluyen la palabra clave en línea en la declaración de función.
  • Las expresiones pasadas como argumentos a funciones en línea se evalúan una vez.
  • En algunos casos, las expresiones pasadas como argumentos a macros se pueden evaluar más de una vez. http://msdn.microsoft.com/en-us/library/bf6bf4cf.aspx

  • Las macros se expanden en tiempo de precompilación, no puede usarlas para depurar, pero puede usar funciones en línea.

- buen articulo : http://www.codeguru.com/forum/showpost.php?p=1093923&postcount=1

;


2

Una función en línea mantendrá la semántica de valor, mientras que una macro de preprocesador simplemente copia la sintaxis. Puede obtener errores muy sutiles con una macro de preprocesador si usa el argumento varias veces; por ejemplo, si el argumento contiene una mutación como "i ++", es una gran sorpresa que se ejecute dos veces. Una función en línea no tendrá este problema.


1

Una función en línea se comporta sintácticamente como una función normal, proporcionando seguridad de tipos y un alcance para las variables locales de la función y acceso a los miembros de la clase si es un método. Además, al llamar a métodos en línea, debe cumplir con las restricciones privadas / protegidas.


1

Para conocer la diferencia entre la función macro y en línea , primero debemos saber qué son exactamente y cuándo debemos usarlas.

FUNCIONES :

int Square(int x){
return(x*X);
}
int main()
{
int value = 5;
int result = Square(value);
cout << result << endl;
}
  • Las llamadas a funciones tienen una sobrecarga asociada, ya que una vez que la función finaliza la ejecución, debe saber dónde debe regresar y también debe almacenar el valor en la memoria de pila.

  • Para aplicaciones pequeñas no será un problema, pero tomemos un ejemplo de aplicaciones financieras donde ocurren miles de transacciones cada segundo, no podemos ir con llamadas a funciones.

MACROS:

# define Square(x) x*x;
int main()
{
int value = 5;
int result = Square(value);
cout << result << endl;
}
  • Las macros funcionan en la etapa de preprocesamiento, es decir, en esta etapa, las declaraciones escritas con la palabra clave # se reemplazarán con el contenido, es decir

int resultado = Cuadrado (x * x)

Pero las macros tienen errores asociados.

#define Square(x) x*x
int main() {
    int val = 5;
    int result = Square(val + 1);
    cout << result << endl;
    return 0;
}

Aquí la salida es 11, no 36 .

FUNCIONES EN LÍNEA :

inline int Square(int x) {
    return x * x;
}

int main() {
    using namespace std;
    int val = 5;
    int result = Square(val + 1);
    cout << result << endl;
    return 0;
}

Salida 36

La palabra clave en línea solicita al compilador que reemplace la llamada a la función con el cuerpo de la función, aquí la salida es correcta porque primero evalúa la expresión y luego la pasa. no se requiere memoria para los argumentos de la función.

Comparación entre macros y funciones en línea:

  1. Las macros funcionan mediante sustitución, mientras que en las funciones en línea, la llamada a la función se reemplaza con el cuerpo.
  2. Las macros son propensas a errores debido a la sustitución, mientras que las funciones en línea son seguras de usar.
  3. Las macros no tienen dirección, mientras que las funciones en línea tienen dirección.
  4. Las macros son difíciles de usar con varias líneas de código, mientras que las funciones en línea no lo son.
  5. En C ++, las macros no se pueden usar con funciones miembro, mientras que la función en línea podría ser.

CONCLUSIÓN:

Las funciones en línea son a veces más útiles que las macros, ya que mejoran el rendimiento y son seguras de usar y también reducen la sobrecarga de llamadas de funciones. Es solo una solicitud al compilador, ciertas funciones no estarán en línea como:

  • grandes funciones
  • funciones que tienen demasiados argumentos condicionales
  • código recursivo y código con bucles, etc.

lo cual es bueno, porque es cuando el compilador piensa que es mejor hacer las cosas de otra manera.


Solo como un comentario: la macro se puede arreglar para evaluar al mismo número entre paréntesis. Sin embargo, todavía es propenso a errores, ya que debe pensar en la sustitución absoluta y tonta y en todos los casos durante la implementación.
Mike

0

En GCC (no estoy seguro de otros), declarar una función en línea es solo una pista para el compilador. Depende del compilador al final del día decidir si incluye o no el cuerpo de la función cada vez que se llama.

La diferencia entre las funciones en línea y las macros de preprocesador es relativamente grande. Las macros de preprocesador son solo un reemplazo de texto al final del día. Usted renuncia a gran parte de la capacidad del compilador para realizar la verificación de tipos de los argumentos y el tipo de retorno. La evaluación de los argumentos es muy diferente (si las expresiones que pasa a las funciones tienen efectos secundarios, se divertirá mucho depurando). Existen diferencias sutiles sobre dónde se pueden utilizar las funciones y macros. Por ejemplo, si tuviera:

#define MACRO_FUNC(X) ...

Donde MACRO_FUNC obviamente define el cuerpo de la función. Se debe tener especial cuidado para que se ejecute correctamente en todos los casos en que se pueda usar una función, por ejemplo, una MACRO_FUNC mal escrita causaría un error en

if(MACRO_FUNC(y)) {
 ...body
}

Se podría utilizar una función normal sin ningún problema.


0

Desde la perspectiva de la codificación, una función en línea es como una función. Por lo tanto, las diferencias entre una función en línea y una macro son las mismas que las diferencias entre una función y una macro.

Desde la perspectiva de la compilación, una función en línea es similar a una macro. Se inyecta directamente en el código, no se llama.

En general, debería considerar que las funciones en línea son funciones regulares con algunas optimizaciones menores mezcladas. Y como la mayoría de las optimizaciones, es el compilador quien decide si realmente se preocupa por aplicarlas. A menudo, el compilador ignorará felizmente cualquier intento del programador de incorporar una función, por varias razones.


0

Las funciones en línea se comportarán como una llamada de función si existe alguna instrucción iterativa o recursiva en ella, para evitar la ejecución repetida de instrucciones. Es muy útil guardar la memoria general de su programa.


-1
#include<iostream>
using namespace std;
#define NUMBER 10 //macros are preprocessed while functions are not.
int number()
{ 
    return 10;
}
/*In macros, no type checking(incompatible operand, etc.) is done and thus use of micros can lead to errors/side-effects in some cases. 
However, this is not the case with functions.
Also, macros do not check for compilation error (if any). Consider:- */
#define CUBE(b) b*b*b
int cube(int a)
{
 return a*a*a;
}
int main()
{
 cout<<NUMBER<<endl<<number()<<endl;
 cout<<CUBE(1+3); //Unexpected output 10
 cout<<endl<<cube(1+3);// As expected 64
 return 0;
}

Las macros suelen ser más rápidas que las funciones, ya que no implican una sobrecarga real de llamadas a funciones.

Algunas desventajas de las macros: No hay verificación de tipos. Difícil de depurar porque causan un reemplazo simple. Las macros no tienen espacio de nombres, por lo que una macro en una sección del código puede afectar a otra sección. Las macros pueden causar efectos secundarios como se muestra en el ejemplo de CUBE () anterior.

Las macros suelen ser de una sola línea. Sin embargo, pueden constar de más de una línea. No existen tales restricciones en las funciones.


¿Cuánto más te diviertes de #define TWO_N(n) 2 << ny luego cout << CUBE(TWO_N(3 + 1)) << endl;? (Es mejor terminar las líneas de salida con endlque comenzar con ellas.)
Jonathan Leffler
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.