¿Cómo funciona el argumento de longitud variable?

En este artículo, discutiremos cómo funciona el argumento de longitud variable.

Función variadica: una función variadica son las plantillas que toman un argumento de longitud variable. Un argumento de longitud variable es una característica que permite que una función reciba cualquier número de argumentos. Hay situaciones en las que una función maneja un número variable de argumentos según los requisitos, como por ejemplo:

  • Suma de números dados.
  • Mínimo de números dados y muchos más.

Un número variable de argumentos están representados por tres puntos (…).

Programa 1:

C

// C program to demonstrate the use of
// variable number of arguments
#include <stdarg.h>
#include <stdio.h>
  
// Function to find the minimum of integer
// numbers passed, ftrst argument is count
// of numbers
int min(int arg_count, ...)
{
    int i;
    int min, a;
  
    // va_list is a type that holds the
    // information about variable arguments
    va_list ap;
  
    // va_start must be called before
    // accessing variable argument list
    va_start(ap, arg_count);
  
    // Now arguments can be accessed one
    // by one using va_arg macro.
  
    // Initialize min as the first
    // argument in list
    min = va_arg(ap, int);
  
    // Traverse the rest of arguments
    // to find out minimum
    for (i = 2; i <= arg_count; i++)
        if ((a = va_arg(ap, int)) < min)
            min = a;
  
    // va_end should be executed before
    // the function returns whenever
    // va_start has been previously
    // used in that function
    va_end(ap);
  
    return min;
}
  
// Driver Code
int main()
{
    int count = 5;
    printf("Minimum value is %d",
           min(count, 12, 67, 6, 7, 100));
  
    return 0;
}
Producción

Minimum value is 6

Convención de llamadas: la convención de llamadas se refiere a cómo se llama a una función, cómo se pasan los parámetros y cómo se limpia la pila. C / C++ tiene una variedad de convenciones de llamada, pero solo nos interesan __cdecl y __stdcall .

Ambos son muy similares pero tienen algunas diferencias.

  • __cdecl es la convención de llamadas predeterminada de C/C++.
  • __stdcall es la convención de llamada predeterminada para las funciones de la API de Windows .

Ahora ambas convenciones de llamada pasan parámetros de derecha a izquierda. Los desarrolladores de C/C++ eligen de derecha a izquierda en lugar de izquierda a derecha.

Diseño de la memoria: el diseño de la memoria se analiza a continuación:

Memory layout

Lo único que necesita atención es el segmento de pila y montón que crece en la dirección opuesta.

  1. El montón crece hacia una dirección superior.
  2. La pila crece hacia la dirección inferior. Significa que más alto en la pila es más bajo en la dirección. Cuando empujamos algo en la pila, obtiene la dirección inmediata más baja de la pila.

Entendamos esto por función:

Dentro de main() tenemos una función func(arg1, arg2, arg3)
Cuando se llama a func, main() se llama «persona que llama» y func() se llama «persona que llama» Veamos la variable local de la persona que llama   
en la pila -> inferior en la pila más arriba en la dirección —- (otras cosas) arg3 (más a la derecha) arg2 arg1 —-



Variables locales de la persona a la que se llama  -> superior en la pila inferior en la dirección ^ la nueva pila se crea aquí, no en la parte superior

En la sección anterior, puede ver claramente que el primer argumento obtiene la dirección más baja. Esta es la razón por la que los desarrolladores eligieron de derecha a izquierda en lugar de izquierda a derecha porque la convención de llamada de izquierda a derecha le dará al primer argumento la dirección más alta que puede causar un problema.

El primer argumento obtiene la dirección más baja y todos los parámetros tendrán una dirección continua en la pila.

Subíndice negativo: [] es el operador de subíndice. A continuación se presentan algunos de los puntos importantes a tener en cuenta sobre el operador de subíndice:

  • Si el operador de subíndice está operando en un puntero, entonces su comportamiento es diferente. es decir, ptr[x] significa *(ptr + x), es decir, valor en x*sizeof(data_type_of_pointer) delante de ptr.
  • De manera similar, ptr[-x] significa *(ptr – x), es decir, valor en x*sizeof(data_type_of_pointer) detrás de ptr.

A continuación se muestra un ejemplo para mostrar cómo funciona <stdarg.h>:

C++

// C++ program to implement stdarg.h
#include <iostream>
#include <stdarg.h>
using namespace std;
  
// Function to find the sum of numbers
int sum(int num, ...)
{
    int res = 0;
    va_list ap;
    va_start(ap, num);
    for (int i = 0; i < num; i++) {
        res += va_arg(ap, int);
    }
  
    // Return the resultant sum
    return res;
}
  
// Driver Code
int main()
{
    // First argument is the number
    // of arguments
    cout << sum(4, 6, 89, 34, 26);
  
    return 0;
}
Producción

155

Explicación:
La implementación incorporada tiene algunas restricciones, echemos un vistazo a ellas y veamos cómo se pueden superar. Uno de ellos es que es necesario pasar el primer argumento como obligatorio, y veamos por qué es obligatorio y cómo evitarlo.

<stdarg.h> usa va_list, va_start, va_arg y va_end. El problema se puede dividir en 2 partes:

  • ¿Qué hacen?
  • ¿Cómo podemos replicarlo?

va_list: Es un typedef de char* pero esto es bastante diferente, ya que cuando se usa como un tipo de string C obtenemos resultados impredecibles. Esto se debe a que no es typedef común . Está integrado definido.

// arg.h
typedef char* va_list;

va_start: Esta es una macro y lo que hace es inicializar ap, que en realidad es de tipo va_list ( char* ) con una dirección delante del primer parámetro, que es arg1 . Esta es la razón por la cual existe la necesidad de pasar el primer argumento como obligatorio. Puede ser cualquier valor de cualquier tipo de datos, pero por simplicidad, a menudo se pasa la cantidad de argumentos. Se utiliza para identificar la dirección en la pila donde los argumentos son continuos.

va_arg: Esta macro es bastante complicada. Hace dos cosas.

  • Devuelve el parámetro requerido
  • Avanza al siguiente parámetro

// arg.h
#define va_get(ap, tipo) ((tipo*)ap)

// convierte la dirección mantenida por ap (aquí arg2) en tipo*
#define va_advance(ap, tipo) ap = ap + sizeof(tipo)

Veamos qué son estos tres puntos. En realidad, es un operador de eclipse ( . . . ) y está definido en C++. Este operador se utiliza para pasar un número variable de argumentos. Así es como funciona stdarg.h , si no puede usar el primer argumento en la función variádica. Podemos hacer uso de ella como

#define va_start(ap, arg1) (ap = (char*)(&arg1))

Sin embargo, esto no tiene sentido ya que no sabemos cuántos argumentos se pasan, entonces, ¿cómo podemos usarlos?

Publicación traducida automáticamente

Artículo escrito por mohdatishanx y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *