Archivo

Entradas Etiquetadas ‘variable’

Número variable de argumentos en una función de C

Lunes, 24 de Enero de 2011 Gaspar Fernández 3 comentarios

Si habéis programado en C, seguramente la primera función que aprendisteis a utilizar era printf() ; y en todo el tiempo que pasasteis estudiando C no encontrasteis una función similar. Y es que esta función puede tener un número indeterminado de parámetros y es capaz de analizarlos todos.

Y es que quitando printf(), scanf() y derivadas de estas, la necesidad de una función con múltiples parámetros casi no se nos presenta. Aunque tal vez, alguna vez necesitemos crear una función con parámetros opcionales, es decir, si están presentes se tomarán y si no, se tomará un valor por defecto.

Esto lo podemos hacer con la biblioteca stdarg.h la cual nos proporciona macros para hacer esto posible.

Echemos un vistazo al siguiente código:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>

void poneargs(int nargs, ...)
{
  va_list ap;
  int i;
  int num;

  va_start(ap, nargs);
  for (i =0; i<nargs; i++)
    {
      num=va_arg(ap, int);
      printf("Argumento %d = %d\n", i, num);
    }
  va_end(ap);
  printf("\n");
}

int main(int argc, char *argv[])
{
  poneargs(5, 6, 77, 445, 34, 23, 12, 43, 32);
  poneargs(2, 23, 94);
  poneargs(0);

  return EXIT_SUCCESS;
}

Aquí vemos cómo la función poneargs() nos coloca los valores de los argumentos que le hemos pasado, siendo el primero de ellos el número total de argumentos que tenemos.
Con esto, hay que dejar claro que no tenemos forma de contar los argumentos que pasamos (igual que printf, podíamos pasar tantos argumentos como %[x] tuviéramos en la cadena de formato). Así que las técnicas para llegar al límite de los comandos son:

  • Poner un argumento indicando el número de argumentos a colocar.
  • Poner un argumento con un valor fijo al final que nos indique que hemos llegado al último parámetro.
  • Poner códigos con los argumentos que tenemos que leer. Idea de printf()

Lo que sí que tenemos que tener en cuenta es que, como estamos acostumbrados cada argumento tiene un tipo fijo, y se lo tenemos que decir a va_arg() en su segundo argumento; y va_arg() nos va a devolver una variable del mismo tipo que le dijimos (éste es el valor del argumento).

En el siguiente ejemplo creamos una función new_cliente() que puede tener hasta cuatro argumentos de tipo cadena de caracteres. De hecho son opcionales. Cuando creamos un cliente en un programa real puede que no tengamos todos sus datos y puede que algunos sean opcionales:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>

void new_cliente(int nargs, ...)
{
  va_list ap;

  char *valor;

  va_start(ap, nargs);

  switch (nargs)
    {
    case 4:
      valor=va_arg(ap, char*);
      printf("Twitter: %s\n", valor);      
    case 3:
      valor=va_arg(ap, char*);
      printf("URL: %s\n", valor);      
    case 2:
      valor=va_arg(ap, char*);
      printf("Blog: %s\n", valor);      
    case 1:
      valor=va_arg(ap, char*);
      printf("Nombre: %s\n", valor);
      break;
    default:
      printf("Número de parámetros incorrecto\n");
    }
 
  va_end(ap);
  printf("\n");
}

int main(int argc, char *argv[])
{
  printf("Un parámetro:\n");
  new_cliente(1, "Gaspy");

  printf("Dos parámetros:\n");
  new_cliente(2, "Gaspy", "Poesía Binaria");

  printf("Tres parámetros:\n");
  new_cliente(3, "Gaspy", "Poesía Binaria", "http://totaki.com/poesiabinaria/");

  printf("Cuatro parámetros:\n");
  new_cliente(4, "Gaspy", "Poesía Binaria", "http://totaki.com/poesiabinaria/", "http://twitter.com/blakeyed");

  return EXIT_SUCCESS;
}

Esto también podría valernos como una pequeña simulación de sobrecarga de funciones (estilo C++) aunque en C no cumpliría la tarea de hacer más legible el código ya que en la misma función tendríamos que incluir las opciones de los argumentos.

Cuando PHP jura en hebreo (PAAMAYIM NEKUDOTAYIM)

Sábado, 22 de Enero de 2011 Gaspar Fernández 2 comentarios

Son dos palabras raras que parece que quieren despistarnos. Y no es inglés, ni japonés, ni ruso… es hebreo.
Y todo se remonta al 1997 cuando Zend Technologies reescribe el motor de PHP planteado por Rasmus Lerdorf.
Zend es una compañía de origen israelí, y de ahí que haya palabras en hebreo.

Pero… qué quiere decir esto ? La traducción es “dobles dos puntos” o lo que es lo mismo “::”. Esto es el operador de resolución de contexto (o ámbito) (Scope Resolution Operator en inglés) que tiene PHP y que desde PHP 3 identifica a qué nos referimos; y cuando programamos orientado a objetos, ayudará a PHP a identificar si llamamos a un método, una constante, atributo, etc. (Como C++).
Cuando hacemos Clase::método() para referirnos a un método estático PHP pone en práctica todo esto.

¿Qué pasa cuando hay un error en esta llamada? PHP nos devolverá un error del tipo: “Unexpected T_PAAMAYIM_NEKUDOTAYIM”, diciendo que hemos puesto “::” de forma inesperada. Podemos probarlo así desde consola:

$ php -r “::”

Y es que :: tiene la misión de detectar que delante ponemos una clase, o una palabra clave que le haga referencia ($this, self, parent, static…), y luego un método/atributo/constante/… aunque como veremos a continuación hay que tener cuidado con la versión de PHP que se utilice.

Es un error común cuando utilizamos PHP5 (<5.3.0) y queremos llamar a un método que está contenido en una variable:

1
2
$metodo="metodo_que_quiero_llamar";
Clase::$metodo

ya que no está soportado en esta versión. En cambio a partir de la 5.3 sí que podremos hacerlo. Mientras tendremos que utilizar call_user_func().

Así que si nos encontramos con este error, querrá decir que PHP no tiene claro a qué nos referimos, a veces lo encontramos cerca de :: pero otras veces no es tan claro, como por ejemplo aquí:

1
2
3
4
5
<?php
define('miConstante', 'algo');
if (empty(foo))
   echo 'vacía';
?>

Aquí fallará al hacer la llamada a empty() y es que tenemos que introducirle una variable como parámetro y no una constante (recordemos que el operador de resolución de contexto es el encargado de identificar variables, constantes y todo esto).

Más información:
Scope Resolution Operator [ Wikipedia ]
Ámbito de las variables [ PHP.NET ]
PHP Paamayim Nekudotayim [ The secret's in the code ]

Bailando con bits: Trabajando a nivel de bit II

Miércoles, 23 de Junio de 2010 Gaspar Fernández Sin comentarios

Hace unos días empecé con la serie Bailando con Bits (aunque llevaba escrito varios meses) trata de formas para trabajar a nivel de bit desde C.

Hoy voy a proponer otra forma, quizás menos intuitiva que la anterior, pero diferente. Esta vez no utilizaremos un registro enorme ni nada parecido, utilizaremos un mismo número entero para hacer el ejemplo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include <stdio.h>

#define PESOBIT(bpos) 1<<bpos
#define COGEBIT(var,bpos) (var & PESOBIT(bpos))?1:0
#define PONE_1(var,bpos) var | PESOBIT(bpos)
#define PONE_0(var,bpos) var & ~(PESOBIT(bpos))
#define CAMBIA(var,bpos) var ^ PESOBIT(bpos)

int main()
{
  int numero;
  int i;

  numero=63;
  printf ("Numero: %d\n", numero);


  for (i=31; i>=0; i--)
    printf("%4d", i);

  printf("\n");

  for (i=31; i>=0; i--)
    printf("%4d",COGEBIT(numero,i));

  printf("\n");

  numero=PONE_1(numero, 17);
  numero=PONE_0(numero, 3);
  numero=CAMBIA(numero, 20);
  numero=CAMBIA(numero, 5);

  for (i=31; i>=0; i--)
    printf("%4d",COGEBIT(numero,i));

  printf("\nNúmero: %d\n", numero);

}

Ahora usamos varias macros que harán operaciones de bit con la variable a analizar (están definidas en la parte de arriba), tenemos PESOBIT, COGEBIT, PONE_1, PONE_0 y CAMBIA:

  • PESOBIT: Nos dice cuánto vale un bit con valor 1 en la posición especificada, por ejemplo en la posición 0 (LSB) vale 1, en la posición 1, vale 2, en la posición 3, vale 4, en la posición 4, vale 8…
  • COGEBIT: Nos dice si el bit en la posición bpos de la variable var vale 0 ó 1
  • PONE_1: Pone un 1 en el bit bpos de la variable var
  • PONE_0: Pone un 0 en el bit bpos de la variable var
  • CAMBIA: Cambia el valor (de 0 a 1 y viceversa) del bit en la posición bpos de la variable var

Como vemos en el ejemplo si queremos poner a 1 el bit 5 de numero, tendremos que hacer numero=PONE_1(numero,5), aunque en el siguiente ejemplo veremos cómo simplificar todo eso.

Con este ejemplo podemos jugar con los bits de números enteros. Pero, y si queremos utilizar otro tipo de variables? (aunque estamos limitados a 32bits) También tenemos este ejemplo con un tipo float:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include <stdio.h>

#define PESOBIT(bpos) 1<<bpos
#define COGEBIT(var,bpos) (*(unsigned*)&var & PESOBIT(bpos))?1:0
#define PONE_1(var,bpos) *(unsigned*)&var |= PESOBIT(bpos)
#define PONE_0(var,bpos) *(unsigned*)&var &= ~(PESOBIT(bpos))
#define CAMBIA(var,bpos) *(unsigned*)&var ^= PESOBIT(bpos)

int main()
{
  float numero;

  int i;

  numero=63.2317;
  printf ("Numero: %f\n", numero);


  for (i=31; i>=0; i--)
    printf("%4d", i);

  printf("\n");

  for (i=31; i>=0; i--)
    printf("%4d",COGEBIT(numero,i));

  printf("\n");

  PONE_1(numero, 5);
  PONE_0(numero, 3);
  CAMBIA(numero, 20);
  CAMBIA(numero, 5);
  CAMBIA(numero, 31);

  for (i=31; i>=0; i--)
    printf("%4d",COGEBIT(numero,i));

  printf("\n");

  printf("\nNúmero: %f\n", numero);

}

En este caso no tenemos que hacer una variable igual a PONE_1(variable, 5) para poner su bit a 1; simplemente con PONE_1(variable, 5) nos vale. En este ejemplo podemos ver una variable de tipo float desglosada en bits y podemos modificar a nivel de bit para obtener otros valores. (No le veo mucha utilidad a esto, pero bueno).

Post dedicado a algm, por su comentario en el artículo Bailando con bits anterior.

Visita otras webs de la red