Blog
02 – Tipos, operadores y expresiones
Las variables y las constantes son los objetos de datos básicos que se manipulan en un programa. Los operadores especifican lo que se hará con las variables. Las expresiones combinan variables y constantes para producir nuevos valores.

2.1 Nombres de variables
2.2 Tipos y tamaños de datos
Ejercicio 2-1
Escriba un programa en C para determinar los rangos de variables char, short, int y long, tanto signed como unsigned, imprimiendo los valores apropiados de los headers stándar y por cálculo directo. Es más difícil si los calcula: determine los rangos de los varios tipos de punto flotante.
/* Escriba un programa en C para determinar los rangos de variables char, short, int y long, tanto signed como unsigned, imprimiendo los valores apropiados de los headers stándar y por cálculo directo. Es más difícil si los calcula: determine los rangos de los varios tipos de punto flotante. */ #include#include int main() { printf("Rangos utilizando headers stándar:\n\n"); printf("char signed: %d a %d\n", CHAR_MIN, CHAR_MAX); printf("char unsigned: 0 a %u\n", UCHAR_MAX); printf("short signed: %d a %d\n", SHRT_MIN, SHRT_MAX); printf("short unsigned: 0 a %u\n", USHRT_MAX); printf("int signed: %d a %d\n", INT_MIN, INT_MAX); printf("int unsigned: 0 a %u\n", UINT_MAX); printf("long signed: %ld a %ld\n", LONG_MIN, LONG_MAX); printf("long unsigned: 0 a %lu\n", ULONG_MAX); printf("\nRangos por cálculo directo:\n\n"); printf("char signed: %d a %d\n", -(char)((unsigned char)~0 >> 1) - 1, (char)((unsigned char)~0 >> 1)); printf("char unsigned: 0 a %u\n", (unsigned char)~0); printf("short signed: %d a %d\n", -(short)((unsigned short)~0 >> 1) - 1, (short)((unsigned short)~0 >> 1)); printf("short unsigned: 0 a %u\n", (unsigned short)~0); printf("int signed: %d a %d\n", -(int)((unsigned int)~0 >> 1) - 1, (int)((unsigned int)~0 >> 1)); printf("int unsigned: 0 a %u\n", (unsigned int)~0); printf("long signed: %ld a %ld\n", -(long)((unsigned long)~0 >> 1) - 1, (long)((unsigned long)~0 >> 1)); printf("long unsigned: 0 a %lu\n", (unsigned long)~0); return 0; }
limits.h
y stdio.h
para obtener los valores máximos y mínimos de las variables char, short, int y long, tanto signed como unsigned. También utiliza cálculos directos para determinar los rangos de las variables utilizando operaciones bit a bit.
En C, las variables signed se representan con el complemento a 2. Para encontrar el valor mínimo de una variable signed, simplemente tomamos el negativo del valor máximo más uno. Por ejemplo, para un char signed, el valor máximo es 127 (0x7F) y el valor mínimo es -128 (0x80). Por lo tanto, podemos encontrar el valor mínimo de un char signed de la siguiente manera:
-(char)((unsigned char)~0 >> 1) - 1
Aquí, ~0 es un número que tiene todos los bits establecidos en 1, es decir, 0xFFFFFFFF en sistemas de 32 bits y 0xFFFFFFFFFFFFFFFF en sistemas de 64 bits. Luego, al hacer ~0 >> 1, cambiamos el bit más significativo a 0 para obtener 0x7FFFFFFF en sistemas de 32 bits y 0x7FFFFFFFFFFFFFFF en sistemas de 64 bits. Al hacer un cast a unsigned char, unsigned short, unsigned int o unsigned long respectivamente, estamos ampliando el tamaño del tipo para poder realizar cálculos aritméticos en bits. Luego, se hace otro cast a char, short, int o long para volver al tamaño original del tipo, lo que establece el signo de la variable. Finalmente, tomamos el negativo de este valor y le restamos uno para encontrar el valor mínimo de la variable.
Para encontrar el valor máximo de una variable signed, hacemos el mismo cálculo pero sin tomar el negativo y sin restar uno. Por ejemplo, para un char signed, el valor máximo es 127 (0x7F). Por lo tanto, podemos encontrar el valor máximo de un char signed de la siguiente manera:
(char)((unsigned char)~0 >> 1)
En el caso de las variables unsigned, simplemente tomamos el valor máximo como el valor más grande que puede ser representado por el tipo. Por ejemplo, para un char unsigned, el valor máximo es 255 (0xFF). Por lo tanto, podemos encontrar el valor máximo de un char unsigned de la siguiente manera:
(unsigned char)~0
2.3 Constantes
2.4 Declaraciones
2.5 Operadores aritméticos
2.6 Operaciones de relación y lógicos
Ejercicio 2-2
(i < lim-1 && (c = getchar()) != '\n' && c != EOF)
¿Podrías escribir esta iteración sin usar && ni ||?
for (i = 0; i < lim-1; i++) { if ((c = getchar()) == '\n') { break; } if (c == EOF) { break; } }
Primero, se verifica si getchar() devuelve el caracter de nueva línea '\n'. Si es así, se rompe el bucle con la instrucción break.
Luego, se verifica si getchar() devuelve el final de archivo EOF. Si es así, también se rompe el bucle con la instrucción break.
De lo contrario, el bucle continúa iterando y se asigna el valor de getchar() a c.
2.7 Conversiones de tipo
Ejercicio 2-3
/* Escribe una funcion htoi(s) en C, que convierta una cadena de dígitos hexadecimales (incluyendo 0x ó 0X en forma optativa) en su valor entero equivalente. Los dígitos permitidos son del 0 al 9, de la a a la f, y de la A a la F.*/ #include#include int htoi(char s[]) { int i, n, hexdigit, inhex; i = 0; if (s[i] == '0') { ++i; if (s[i] == 'x' || s[i] == 'X') { ++i; } } n = 0; inhex = 1; for (; inhex == 1; ++i) { if (isdigit(s[i])) { hexdigit = s[i] - '0'; } else if (isalpha(s[i])) { hexdigit = tolower(s[i]) - 'a' + 10; } else { inhex = 0; } if (inhex == 1) { n = 16 * n + hexdigit; } } return n; } int main() { char cadena[]="0xAF50B"; printf("%s es %d", cadena, htoi(cadena)); }
2.8 Operadores de incremento y decremento
Ejercicio 2-4
Escribe una función en C denominada squeeze(s1,s2) que borre cada carácter de s1 que coincida con cualquier carácter de la cadena s2.
#include/* squeeze: borra todas los caracteres de s1 que coincidan con caracteres de s2*/ void squeeze(char s1[], char s2[]) { int i, j, k; for (i = j = 0; s1[i] != '\0'; i++) { for (k = 0; s2[k] != '\0' && s2[k] != s1[i]; k++); if (s2[k] == '\0') { s1[j++] = s1[i]; } } s1[j] = '\0'; } int main() { char s1[] = "987655895766212"; char s2[] = "562"; squeeze(s1,s2); printf("%s\n",s1); }
- La función recibe dos argumentos de cadena (string), s1 y s2, que son las cadenas de las que se van a eliminar los caracteres que coincidan.
- La función utiliza un bucle for anidado para recorrer cada carácter de la cadena s1 y cada carácter de la cadena s2.
- Si el carácter actual de s1 no coincide con ningún carácter de s2, se copia en la posición j de s1 y j se incrementa. Esto significa que los caracteres que coinciden se eliminan de la cadena s1.
- Al final, se establece el último carácter de s1 como '\0', lo que significa que la cadena se termina allí.
Ejercicio 2-5
Escribe la función any(s1,s2), que devuelve la primera posición de la cadena s1 en donde se encuentre cualquier caracter de la cadena s2, o -1 si s1 no contiene caracteres de s2.
#include/* any: devuelve la primera posicón de s1 donde se encuentra cualquier carácter de s2, o -1 si no encuentra ninguno */ int any(char s1[], char s2[]) { int i, j; for (i = 0; s1[i] != '\0'; i++) { for (j = 0; s2[j] != '\0'; j++) { if (s1[i] == s2[j]) { return i; } } } return -1; } int main() { char s1[] = "hola mundo"; char s2[] = "oe"; int pos = any(s1, s2); printf("%d\n", pos); return 0; }
- La función recibe dos argumentos de cadena (string), s1 y s2, que son las cadenas en las que se busca la coincidencia de caracteres.
- La función utiliza dos bucles for anidados para recorrer cada carácter de s1 y cada carácter de s2.
- Si se encuentra una coincidencia, la función devuelve la posición actual en s1.
- Si no se encuentra ninguna coincidencia, la función devuelve -1.
2.9 Operadores para manejo de bits
Ejercicio 2-6
Escribe una función setbits(x,p,n,y) en C, que regresa x con los n bits que principian en la posicion p iguales a los n bits más a la derecha de y, dejando los otros bits sin cambio.
#include/*Escribe una función setbits(x,p,n,y) en C, que regresa x con los n bits que principian en la posicion p iguales a los n bits más a la derecha de y, dejando los otros bits sin cambio. */ unsigned setbits(unsigned x, int p, int n, unsigned y); int main(int argc, char *argv[]) { if (argc != 5) { printf("Usage: %s
\n", argv[0]); return 1; } // Convertir los argumentos de la línea de comandos en enteros unsigned x = (unsigned) strtol(argv[1], NULL, 0); int p = (int) strtol(argv[2], NULL, 0); int n = (int) strtol(argv[3], NULL, 0); unsigned y = (unsigned) strtol(argv[4], NULL, 0); // Llamar a la función setbits y mostrar el resultado unsigned result = setbits(x, p, n, y); printf("setbits(%u,%d,%d,%u) = %u\n", x, p, n, y, result); return 0; } unsigned setbits(unsigned x, int p, int n, unsigned y) { // Se crea una máscara de n bits con 1's // y se desplaza p-n+1 bits a la izquierda unsigned mask = ~(~0 << n) << (p - n + 1); // Se obtiene los n bits más a la derecha de y unsigned bits_to_insert = (y & ~(~0 << n)) << (p - n + 1); // Se borran los n bits de x que se van a reemplazar x &= ~mask; // Se insertan los bits de y en la posición adecuada en x x |= bits_to_insert; // Se retorna el resultado return x; }
- Primero, creamos una máscara con
n
bits con todos los bits a 1. - Luego, desplazamos la máscara
p-n+1
bits a la izquierda para que esté en la posición correcta para cubrir los bits que vamos a modificar enx
. - A continuación, obtenemos los
n
bits más a la derecha dey
usando una máscara similar a la de arriba. - Luego, borramos los bits de
x
que se van a reemplazar utilizando la máscara creada en el paso 1. - Finalmente, insertamos los bits de
y
en la posición adecuada enx
y devolvemos el resultado.
Tenga en cuenta que esta función asume que los índices de bits comienzan en 0 (es decir, el bit más a la derecha es el bit 0). Además, esta función utiliza operadores de bits, por lo que es necesario utilizar el tipo de datos unsigned
para asegurarse de que el comportamiento de los bits sea el correcto.
ejer02_06 1431655765 7 8 170
Este ejemplo utiliza los siguientes argumentos: x = 1431655765, p = 7, n = 8, y y = 170. El número 1431655765 es 0x55555555 en hexadecimal, que tiene el bit 7 en 1 y los bits 8 a 15 en 0. El número 170 es 0xAA en hexadecimal, que tiene los bits 0 a 7 en 1 y los bits 8 a 31 en 0. Entonces, si llamamos a setbits(x,p,n,y), deberíamos obtener un número en el que los bits 7 a 14 sean iguales a los bits 0 a 7 de y (que son todos 1) y el resto de los bits sean los mismos que en x. Después de ejecutar el programa, deberíamos obtener el siguiente resultado:
ejer02_06(1431655765,7,8,170) = 1431655939
Este resultado significa que los bits 7 a 14 de 1431655765 ahora son iguales a los bits 0 a 7 de 170, como se esperaba.
Ejercicio 2-7
#include#include /*Escribe en C una funcion invert(x,p,n) que regresa x con los n bits que principian en la posicion p invertidos (esto es, 1 cambiado a 0 y viceversa), dejando los otros sin cambio. */ unsigned invert(unsigned x, int p, int n); int main(int argc, char *argv[]) { if (argc != 4) { printf("Usage: %s
\n", argv[0]); return 1; } // Convertir los argumentos de la línea de comandos en enteros unsigned x = (unsigned) strtol(argv[1], NULL, 0); int p = (int) strtol(argv[2], NULL, 0); int n = (int) strtol(argv[3], NULL, 0); // Llamar a la función invert y mostrar el resultado unsigned result = invert(x, p, n); printf("invert(%u,%d,%d) = %u\n", x, p, n, result); return 0; } unsigned invert(unsigned x, int p, int n) { // Se crea una máscara de n bits con 1's // y se desplaza p-n+1 bits a la izquierda unsigned mask = ~(~0 << n) << (p - n + 1); // Se invierten los bits de x que están en la posición adecuada x ^= mask; // Se retorna el resultado return x; }
- Primero, creamos una máscara con
n
bits con todos los bits a 1. - Luego, desplazamos la máscara
p-n+1
bits a la izquierda para que esté en la posición correcta para cubrir los bits que vamos a invertir enx
. - A continuación, invertimos los bits de
x
que están en la posición adecuada utilizando la máscara creada en el paso 1. - Finalmente, devolvemos el resultado.
Tenga en cuenta que esta función asume que los índices de bits comienzan en 0 (es decir, el bit más a la derecha es el bit 0). Además, esta función utiliza operadores de bits, por lo que es necesario utilizar el tipo de datos unsigned
para asegurarse de que el comportamiento de los bits sea el correcto.
Imaginemos que ejecutamos el programa:
ejer02_07 170 7 8
Este ejemplo utiliza los siguientes argumentos: x = 170
, p = 7
, y n = 8
. El número 170
es 0xAA
en hexadecimal, que tiene los bits 0 a 7 en 1 y los bits 8 a 31 en 0. Entonces, si llamamos a invert(x,p,n)
, deberíamos obtener un número en el que los bits 7 a 14 hayan sido invertidos (es decir, se hayan cambiado de 1 a 0 o de 0 a 1), y el resto de los bits sean los mismos que en x
. Después de ejecutar el programa, deberíamos obtener el siguiente resultado:
invert(170,7,8) = 4294901883
Este resultado significa que los bits 7 a 14 de 170
han sido invertidos, como se esperaba.
Ejercicio 2-8
Escribe una funcion en C rightrot(x,n) que regresa el valor del entero x rotado a la derecha n posiciones de bits, e incluye la función en un programa que ilustre su funcionamiento.
#include/* Escribe una funcion en C rightrot(x,n) que regresa el valor del entero x rotado a la derecha n posiciones de bits, e incluye la función en un programa que ilustre su funcionamiento. */ unsigned rightrot(unsigned x, int n) { int wordlength(void); unsigned rbit; while (n-- > 0) { rbit = (x & 1) << (wordlength() - 1); x = x >> 1; x = x | rbit; } return x; } int wordlength(void) { int i; unsigned v = (unsigned) ~0; for (i = 1; (v = v >> 1) > 0; i++) ; return i; } int main() { unsigned x = 0x12345678; int n = 4; printf("Original: %08x\n", x); printf("Rotated : %08x\n", rightrot(x, n)); return 0; }
La función rightrot(x,n)
toma dos argumentos: el entero x
que se va a rotar y el número de posiciones n
que se van a rotar a la derecha. La función primero define una variable rbit
que se usará para almacenar el bit más a la derecha de x
antes de que se desplace a la derecha.
Dentro del bucle while, la función desplaza x
a la derecha una posición y luego hace un desplazamiento a la izquierda con wordlength()-1
bits para recuperar el bit más a la derecha de x
. El resultado se almacena en rbit
. Luego, rbit
se coloca en la posición más a la izquierda de x
mediante una operación OR.
La función wordlength()
devuelve la longitud en bits del tipo de datos unsigned
, que se usa para determinar cuántos bits se deben desplazar a la izquierda para recuperar el bit más a la derecha de x
.
En el programa principal, se define una variable x
y se le da un valor inicial de 0x12345678
. Se llama a la función rightrot()
con x
y n
y se imprime el resultado de la rotación a la derecha.
Si se ejecuta el programa, la salida sería algo así:
Original: 12345678
Rotated : 81234567
Esto indica que el número 0x12345678
se ha rotado 4 posiciones a la derecha para producir el número 0x81234567
.
2.10 Operadores de asignación y expresiones
Ejercicio 2-9
int bitcount(unsigned x)
{
int b;
for (b = 0; x != 0; x >>= 1)
if (x & 01)
b++;
return b;
}
Para una versión más rápida de la función bitcount, podemos aplicar este truco para contar los bits de 1 en un número de manera eficiente. En lugar de iterar a través de cada bit de x y verificar si es 1, podemos simplemente aplicar la operación x &= (x-1) repetidamente hasta que x se convierte en cero. En cada iteración, eliminamos el bit 1 de más a la derecha en x, y contamos el número de iteraciones para obtener el número total de bits de 1 en x. Por lo tanto, una versión más rápida de la función bitcount se vería así:
int bitcount(unsigned x) {
int b;
for (b = 0; x != 0; x &= (x-1)) {
b++;
}
return b;
}
Esta implementación debería ser más rápida que la original, especialmente para números con muchos bits de 1.
#include/* Escribe una funcion en C rightrot(x,n) que regresa el valor del entero x rotado a la derecha n posiciones de bits, e incluye la función en un programa que ilustre su funcionamiento. */ /* bitcount: cuenta bits 1 en x */ /*int bitcount(unsigned x) { int b; for (b = 0; x != 0; x >>= 1) if (x & 01) b++; return b; }*/ /* version nueva más eficiente */ int bitcount(unsigned x) { int b; for (b = 0; x != 0; x &= (x-1)) { b++; } return b; } int main() { unsigned x = 0x12345678; printf("Número de bits 1: %d\n", bitcount(x)); return 0; }
2.11 Expresiones condicionales
2.12 Precedencia y orden de evaluación
Ejercicio 2-10
#include/* Escribe una función lower en C, que convierta letras mayúsculas en minúsculas, con una expresión condicional ?: en vez de un if-else, e ilustra su funcionamiento con un programa. */ char to_lower(char c) { return (c >= 'A' && c <= 'Z') ? c + ('a' - 'A') : c; } int main() { char c = 'H'; printf("Carácter original: %c\n", c); printf("Carácter convertido: %c\n", to_lower(c)); return 0; }
En el programa de ejemplo, se ilustra el uso de la función to_lower con el carácter 'H'. La salida del programa es:
Carácter original: H
Carácter convertido: h
Como se puede ver, el carácter 'H' se convierte en el carácter 'h' utilizando la función to_lower.