Filmina 01
Operadores de bits
Estos operadores trabajan con las expresiones que manejan manipulándolas a nivel de bit, y sólo se pueden aplicar a expresiones enteras. Existen seis operadores de bits, cinco binarios y uno unitario: "&", "|", "^", "~", ">>" y "<<".
Sintaxis:
<expresión> & <expresión> <expresión> ^ <expresión> <expresión> | <expresión> ~<expresión> <expresión> << <expresión> <expresión> >> <expresión>
El operador "&" corresponde a la operación lógica "AND", o en álgebra de Boole al operador "·", compara los bits uno a uno, si ambos son "1" el resultado es "1", en caso contrario "0".
El operador "^" corresponde a la operación lógica "OR exclusivo", compara los bits uno a uno, si ambos son "1" o ambos son "0", el resultado es "0", en caso contrario "1".
El operador "|" corresponde a la operación lógica "OR", o en álgebra de Boole al operador "+", compara los bits uno a uno, si uno de ellos es "1" el resultado es "1", en caso contrario "0".
El operador "~", (se obtiene con la combinación de teclas ALT+126, manteniendo pulsada la tecla "ALT", se pulsan las teclas "1", "2" y "6" del teclado numérico), corresponde a la operación lógica "NOT", se trata de un operador unitario que invierte el valor de cada bit, si es "1" da como resultado un "0", y si es "0", un "1".
El operador "<<" realiza un desplazamiento de bits a la izquierda del valor de la izquierda, introduciendo "0" por la derecha, tantas veces como indique el segundo operador; equivale a multiplicar por 2 tantas veces como indique el segundo operando.
El operador ">>" realiza un desplazamiento de bits a la derecha del valor de la izquierda, introduciendo "0" por la izquierda, tantas veces como indique el segundo operador; equivale a dividir por 2 tantas veces como indique el segundo operando.
Filmina 02
Operador condicional
El operador "?:", se trata de un operador ternario.
Sintaxis:
<expresión lógica> ? <expresión> : <expresión>
En la expresión E1 ? E2 : E3, primero se evalúa la expresión E1, si el valor es verdadero ("true"), se evaluará la expresión E2 y E3 será ignorada, si es falso ("false"), se evaluará E3 y E2 será ignorada.
Hay ciertas limitaciones en cuanto al tipo de los argumentos.
- E1 debe ser una expresión lógica.
E2 y E3 han de seguir una de las siguientes reglas:
- Ambas de tipo aritmético.
- Ambas de estructuras o uniones compatibles.
- Ambas de tipo "void".
Hay más reglas, pero las veremos más adelante, ya que aún no hemos visto nada sobre los conocimientos implicados.
Como ejemplo veremos cómo se puede definir la macro "max":
#define max(a,b) (((a) > (b)) ? (a) : (b))
De este ejemplo sólo nos interesa la parte de la derecha. La interpretación es: si "a" es mayor que "b", se debe evaluar "a", en caso contrario evaluar "b".
Filmina 03
Definición de tipos, tipos derivados
En ocasiones puede ser útil definir nombres para tipos de datos, 'alias' que nos hagan más fácil declarar variables y parámetros, o que faciliten la portabilidad de nuestros programas.
Para ello C y C++ disponen de la palabra clave "typedef".
Sintaxis:
typedef <tipo> <identificador>;
<tipo> puede ser cualquier tipo C++, fundamental o derivado.
Ejemplos:
typedef unsigned int UINT;
UINT será un tipo válido para la declaración de variables o parámetros, y además será equivalente a un entero sin signo.
typedef unsigned char BYTE;
BYTE será un tipo equivalente a ocho bits.
typedef unsigned short int WORD;
WORD será un tipo equivalente a dieciséis bits. Este último es un caso de dependencia de la plataforma, si WORD debe ser siempre una palabra de dieciséis bits, independientemente de la plataforma, deberemos cambiar su definición dependiendo de ésta. En algunas plataformas podrá definirse como:
typedef unsigned int WORD;
y en otras como:
typedef unsigned long int WORD;
Declarar un tipo para WORD en un fichero de cabecera, nos permitirá adaptar fácilmente la aplicación a distintas plataformas.
typedef struct stpunto tipoPunto;
Define un nuevo tipo como una estructura stpunto.
typedef struct { int x; int y; int z; } Punto3D;
Define un nuevo tipo Punto3D, partiendo de una estructura.
typedef int (*PFI)();
Define PFI como un puntero a una función que devuelve un puntero.
PFI f;
Declaración de una variable f que es un puntero a una función que devuelve un entero.
Filmina 04
Parámetros con valores por defecto
Algunas veces nos puede interesar que ciertos parámetros que necesita una función no sea necesario proporcionarlos siempre. Esto suele suceder cuando esos parámetros casi siempre se usan con un mismo valor. En C++, cuando declaramos una función podemos decidir que algunos de sus parámetros sean opcionales. En ese caso tendremos que asignarles valores por defecto.
Cuando se llama a la función incluyendo valores para los parámetros opcionales funcionará como cualquiera de las funciones que hemos usado hasta ahora, pero si se omiten todos o algunos de estos parámetros la función trabajará con los valores por defecto que hemos definido.
Por ejemplo:
#include <iostream> using namespace std;
void funcion(int a = 1);
int main() {
funcion(19);
funcion();
cin.get();
return 0;
}
void funcion(int a) {
cout << a << endl;
}
La primera llamada a "funcion" dará como salida 19, que es el parámetro que le damos explícitamente. La segunda llamada dará como salida 1, que es el valor por defecto.
Sin embargo este método tiene algunas limitaciones:
- Sólo los últimos argumentos de las funciones pueden tener valores por defecto.
- De estos, sólo los últimos argumentos pueden ser omitidos en una llamada.
- Los valores por defecto deben especificarse bien en los prototipos, bien en las declaraciones, pero no en ambos.
Cuando se declaren valores de parámetros por defecto en prototipos, no es necesario indicar el nombre de los parámetros.
Por ejemplo:
void funcion(int = 1); // Legal
void funcion1(int a, int b=0, int c=1); // Legal
void funcion2(int a=1, int b, int c); // Ilegal
void funcion3(int, int, int=1); // Legal ...
void funcion3(int a, int b=3, int c) // Legal { }
Los argumentos por defecto empiezan a asignarse empezando por el último.
int funcion1(int a, int b=0, int c=1); ... funcion1(12, 10); // Legal, el valor para "c" es 1 funcion1(12); // Legal, los valores para "b" y "c" son 0 y 1 funcion1(); // Ilegal, el valor para "a" es obligatorio
Filmina 05
Argumentos de main.
Muy a menudo necesitamos especificar valores u opciones a nuestros programas cuando los ejecutamos desde la línea de comandos.
Por ejemplo, si hacemos un programa que copie ficheros, del tipo del "copy" de MS-DOS, necesitaremos especificar el nombre del archivo de origen y el de destino.
Hasta ahora siempre hemos usado la función "main" sin parámetros, sin embargo, como veremos ahora, se pueden pasar argumentos a nuestros programas a través de los parámetros de la función main.
Para tener acceso a los argumentos de la línea de comandos hay que declararlos en la función "main", la manera de hacerlo puede ser una de las siguientes:
int main(int argc, char *argv[]); int main(int argc, char **argv);
Que como sabemos son equivalentes.
El primer parámetro, "argc" (argument counter), es el número de argumentos que se han especificado en la línea de comandos. El segundo, "argv", (argument values) es un array de cadenas que contiene los argumentos especificados en la línea de comandos.
Por ejemplo, si nuestro programa se llama "programa", y lo ejecutamos con la siguiente línea de comandos:
programa arg1 arg2 arg3 arg4
argc valdrá 5, ya que el nombre del programa también se cuenta como un argumento.
argv[] contendrá la siguiente lista: "C:\programasc\programa", "arg1", "arg2", "arg3" y "arg4".
Ejemplo:
#include <iostream> using namespace std;
int main(int argc, char **argv) {
for(int i = 0; i < argc; i++)
cout << argv[i] << " ";
cout << endl;
}
Filmina 06
Funciones inline
Cuando escribimos el nombre de una función dentro de un programa decimos que "llamamos" a esa función. Esto quiere decir que lo que hace el programa es "saltar" a la función, ejecutarla y retornar al punto en que fue llamada.
Esto es cierto para las funciones que hemos usado hasta ahora, pero hay un tipo especial de funciones que trabajan de otro modo. En lugar de existir una única copia de la función dentro del código, cuando se declara una función como "inline" lo que se hace es insertar su código en el lugar en que se realiza la llamada, en lugar de invocar a la función.
Sintaxis:
inline <tipo> <nombre_de_funcion>(<lista_de_parámetros>);
Esto tiene la ventaja de que la ejecución es más rápida, pero por contra, el programa generado es más grande. Se debe evitar el uso de funciones "inline" cuando éstas son de gran tamaño, aunque con funciones pequeñas es recomendable, ya que se suelen producir programas más rápidos. Su uso es frecuente cuando las funciones tienen código en ensamblador, ya que en estos casos la optimización es mucho mayor.
En algunos casos, si la función es demasiado larga, el compilador puede decidir no insertar la función, sino simplemente llamarla. El uso de "inline" no es por lo tanto una obligación para el compilador, sino simplemente una recomendación.
Aparentemente, una función "inline" se comportará como cualquier otra función. De hecho, es incluso posible obtener un puntero a una función declara inline.
Nota: "inline" es exclusivo de C++, y no está disponible en C.
Ejemplos:
#include <iostream> using namespace std;
inline int mayor(int a, int b) {
if(a > b) return a;
else return b;
}
int main() {
cout << "El mayor de 12,32 es " << mayor(12,32) << endl;
cout << "El mayor de 6,21 es " << mayor(6,21) << endl;
cout << "El mayor de 14,34 es " << mayor(14,34) << endl;
cin.get();
return 0;
}
Filmina 07
Directiva #define:
La directiva "#define", sirve para definir macros. Esto suministra un sistema para la sustitución de palabras, con y sin parámetros.
Sintaxis:
#define identificador_de_macro <secuencia>
El preprocesador sustituirá cada ocurrencia del identificador_de_macro en el fichero fuente, por la secuencia con algunas excepciones. Cada sustitución se conoce como una expansión de la macro. La secuencia es llamada a menudo cuerpo de la macro.
Si la secuencia no existe, el identificador_de_macro será eliminado cada vez que aparezca en el fichero fuente.
Después de cada expansión individual, se vuelve a examinar el texto expandido a la búsqueda de nuevas macros, que serán expandidas a su vez. Esto permite la posibilidad de hacer macros anidadas. Si la nueva expansión tiene la forma de una directiva de preprocesador, no será reconocida como tal.
Existen otras restricciones a la expansión de macros:
Las ocurrencias de macros dentro de literales, cadenas, constantes alfanuméricas o comentarios no serán expandidas.
Una macro no será expandida durante su propia expansión, así #define A A, no será expandida indefinidamente.
No es necesario añadir un punto y coma para terminar una directiva de preprocesador. Cualquier carácter que se encuentre en una secuencia de macro, incluido el punto y coma, aparecerá en la expansión de la macro. La secuencia termina en el primer retorno de línea encontrado. Las secuencias de espacios o comentarios en la secuencia, se expandirán como un único espacio.
Filmina 08
Directiva #undef:
Sirve para eliminar definiciones de macros previamente definidas. La definición de la macro se olvida y el identificador queda indefinido.
Sintaxis:
#undef identificador_de_macro
La definición es una propiedad importante de un identificador. Las directivas condicionales #ifdef e #ifndef se basan precisamente en esta propiedad de los identificadores. Esto ofrece un mecanismo muy potente para controlar muchos aspectos de la compilación.
Después de que una macro quede indefinida puede ser definida de nuevo con #define, usando la misma u otra definición.
Si se intenta definir un identificador de macro que ya esté definido, se producirá un aviso, un warning, si la definición no es exactamente la misma. Es preferible usar un mecanismo como este para detectar macros existentes:
#ifndef NULL #define NULL 0L #endif
De este modo, la línea del #define se ignorará si el símbolo NULL ya está definido.