Parcial de práctica — Introducción a la Computación

Temas: semanas 1 a 7 · Duración: 2 horas

Este es un examen de práctica con el formato del parcial. Sirve para medir cuánto tiempo les toma cada tipo de pregunta: resuélvanlo en papel, con el reloj corriendo y sin computadora, y recién después comprueben sus respuestas compilando en el entorno del curso. El formato general está en exams/README.md. Dos horas no alcanzan para todos los temas: el examen real puede preguntar los que aquí no aparecen, como diagramas de flujo o Git.

Indicaciones

  • Los errores menores de sintaxis no importan si la idea es correcta.
  • En las preguntas de “qué escribe”, justifiquen con una tabla de variables o una explicación: la salida sola no basta.
  • Todo el código compila sin advertencias con gcc -Wall -Wextra -std=c17, salvo el de la pregunta 8, que tiene errores a propósito.
  • Supongan el entorno del curso: un int tiene 32 bits, un double tiene 64 bits, el texto está en UTF-8, y en ASCII '0' vale 48, 'A' vale 65 y 'a' vale 97.
  • Los montos van en céntimos, como enteros, igual que en el curso.
  • El pseudocódigo va en el formato del curso: ←, mientras, si, div, mod.
  • Si les piden una función, no hace falta escribir los #include.
Parte Preguntas
A. Representación de datos 1–3
B. Seguir programas a mano 4–7
C. Encontrar el bug 8–9
D. Pseudocódigo y C 10–11
E. Preguntas cortas 12–17

Parte A — Representación de datos

Pregunta 1 — Bases y complemento a dos

a) Conviertan 173 a binario de 8 bits, mostrando las divisiones, y después a hexadecimal.

b) Escriban -58 en complemento a dos con 8 bits, invirtiendo y sumando 1. Comprueben su resultado sumando los pesos de los bits.

c) ¿Qué número representa el patrón 10110100 sin signo? ¿Y en complemento a dos con 8 bits? ¿Qué decide cuál de los dos es?

Pregunta 2 — Sumas y overflow

a) Sumen en binario, con 8 bits en complemento a dos, 01011010 + 01000111. Escriban los dos números y el resultado en decimal. ¿Hay overflow? ¿Cómo se nota en los bits?

b) Sumen 01011010 + 11000110 de la misma forma. Sobra un acarreo a la izquierda. ¿Eso es overflow? ¿Por qué?

c) Un programa lee 2147483647 con scanf en un int n, y calcula n + 1. Un compañero dice: “Siempre escribe -2147483648, porque da la vuelta”. ¿Tiene razón? Después, sin ejecutarlo, ¿qué escribe este fragmento, y por qué aquí sí se puede responder con un número?

unsigned char c = 200;
c = c + 100;
printf("%d\n", c);

Pregunta 3 — Decimales y texto

a) Dupliquen el número y anoten la parte entera en cada paso, como en fraccion_a_binario, para sacar los primeros 6 bits de la parte fraccionaria de 0.4375 y de 0.7. ¿Cuál de los dos se guarda exactamente en un double? ¿Cómo lo saben?

b) La letra griega Ω es el código U+03A9. Con la tabla de abajo, digan cuántos bytes ocupa en UTF-8 y calculen esos bytes en hexadecimal.

Rango de códigos Bytes Patrón de bits
U+0000 a U+007F 1 0xxxxxxx
U+0080 a U+07FF 2 110xxxxx 10xxxxxx
U+0800 a U+FFFF 3 1110xxxx 10xxxxxx 10xxxxxx

c) ¿Cuántos caracteres y cuántos bytes tiene la palabra cañón en UTF-8?

Parte B — Seguir programas a mano

Pregunta 4 — Expresiones y decisiones

#include <stdio.h>

int main(void) {
    int a = 17;
    int b = 5;
    double c = a / b;
    double d = (double) a / b;
    int e = a % b * 3 + 2;
    int f = -a / b;
    int cents = 3 * 1990;
    char letter = 'a' + 2;

    printf("%.2f %.2f\n", c, d);
    printf("%d %d\n", e, f);
    printf("S/ %d.%02d\n", cents / 100, cents % 100);
    printf("%c %d\n", letter - 32, '7' - '0');

    if (b == 0 || a / b > 3) {
        printf("P\n");
    } else if (a > 10 && !(b > 10)) {
        printf("Q\n");
    } else {
        printf("R\n");
    }
    return 0;
}

a) ¿Qué escribe? Muestren el orden de las operaciones de e y por qué c y d son distintos.

b) Escriban !(a > 10 && b <= 5) sin un ! delante del paréntesis. Aparte: si la primera condición se escribiera a / b > 3 || b == 0, ¿qué pasaría con b igual a 0? ¿Qué evita el orden original?

Pregunta 5 — Ciclos

#include <stdio.h>

int main(void) {
    int i;
    int s = 0;
    for (i = 20; i > 3; i -= 6) {
        s += i;
    }
    printf("%d %d\n", i, s);

    int k = 10;
    do {
        printf("%d ", k);
        k += 5;
    } while (k < 10);
    printf("%d\n", k);

    int n = 2704;
    int reversed = 0;
    int zeros = 0;
    while (n > 0) {
        int d = n % 10;
        if (d == 0) {
            zeros++;
        }
        reversed = reversed * 10 + d;
        n = n / 10;
    }
    printf("%d %d\n", reversed, zeros);
    return 0;
}

a) ¿Qué escribe? Hagan una tabla de variables para el último ciclo.

b) Si n empezara en 1200, ¿qué escribiría la última línea? ¿Por qué el resultado no tiene cuatro cifras?

c) Si n empezara en 0, ¿cuántas vueltas daría el ciclo? ¿Qué escribiría la última línea? ¿Es correcto ese conteo de ceros?

d) Reescriban el primer for con while, sin cambiar lo que escribe.

Pregunta 6 — Ciclos anidados

#include <stdio.h>

int main(void) {
    int count = 0;
    for (int row = 1; row <= 4; row++) {
        for (int col = row; col <= 4; col++) {
            if ((row + col) % 2 == 0) {
                printf("*");
                count++;
            } else {
                printf(".");
            }
        }
        printf("\n");
    }
    printf("%d\n", count);
    return 0;
}

a) ¿Qué escribe?

b) Si se cambian los dos 4 por n, ¿cuántas veces corre el if, en función de n? ¿Cuántas con n igual a 8 y con n igual a 16? Si n se duplica, ¿el trabajo se duplica? ¿Qué le pasa?

Pregunta 7 — Funciones y la pila

#include <stdio.h>

int twist(int a, int b) {
    a = a + b;
    b = a - b;
    return a * b;
}

int shift(int x) {
    int y = twist(x, 2);
    x = x + 1;
    return y + x;
}

int main(void) {
    int x = 3;
    int y = shift(x);
    printf("%d %d\n", x, y);
    printf("%d\n", twist(x, x));
    return 0;
}

a) ¿Qué escribe?

b) Dibujen la pila de llamadas en la primera llamada a twist, justo antes de su return, con el valor de cada variable. Indiquen las que todavía no tienen valor.

c) shift hace x = x + 1. ¿Por qué la primera línea de la salida no muestra ese cambio? Si quisieran que el x de main terminara cambiado, ¿cómo lo harían, con lo que vimos hasta la semana 7?

Parte C — Encontrar el bug

Pregunta 8 — Tres fragmentos

Cada fragmento va dentro del main de un programa con #include <stdio.h>, y compila con advertencias. Para cada uno, digan qué hace en realidad (si se puede saber), expliquen el error y corríjanlo.

a) Debe escribir Justo solo si la nota es 11, y la nota no debe cambiar.

int grade = 8;
if (grade = 11) {
    printf("Justo\n");
}
printf("%d\n", grade);

b) El tamaño 1 cuesta 500 céntimos y el tamaño 2 cuesta 800.

int size = 1;
int price = 0;
switch (size) {
    case 1:
        price = 500;
    case 2:
        price = 800;
        break;
    default:
        price = 0;
}
printf("%d\n", price);

c) Debe escribir el promedio con dos decimales.

int sum = 47;
int count = 3;
printf("Promedio: %.2f\n", sum / count);

Pregunta 9 — Una función con pruebas

// Devuelve 1 si n es primo y 0 si no. Recibe n >= 0.
int is_prime(int n) {
    for (int d = 2; d < n; d++) {
        if (n % d == 0) {
            return 0;
        } else {
            return 1;
        }
    }
    return 1;
}

a) Sigan is_prime(9) y is_prime(1). ¿Qué devuelve cada una, y qué debería devolver? La función responde bien con 2, 3, 4 y 7. ¿Por qué eso no alcanza para confiar en ella?

b) Corrijan la función. Hay dos errores distintos.

c) Escriban cuatro pruebas con assert. Para cada una, digan qué error o qué borde revisa.

Parte D — Pseudocódigo y C

Pregunta 10 — Ahorrar para una laptop

Rosa ahorra para una laptop. El primer mes guarda first céntimos, y cada mes siguiente guarda 100 céntimos más que el mes anterior. Queremos saber cuántos meses tarda en llegar a goal céntimos o pasarse. Supongan que first es mayor que 0, que goal no es negativo, y que todas las sumas caben en un int.

a) Escriban el algoritmo meses_para_meta(primero, meta) en pseudocódigo.

b) Síganlo a mano con primero = 500 y meta = 2000, en una tabla.

c) Tradúzcanlo a la función int months_to_goal(int first, int goal).

d) Escriban dos pruebas con assert para los bordes: que la suma llegue exactamente a la meta, y que la meta sea 0. Calculen el valor esperado a mano.

Pregunta 11 — Un programa completo

Escriban un programa completo en C. Lee un entero n, y después n notas de 0 a 20. Escribe cuántas notas son aprobatorias (11 o más) y cuál es la mayor. Si scanf falla, si n es menor que 1, o si una nota está fuera de rango, escribe un aviso y termina con return 1. Con la entrada 4 14 9 17 11 debe escribir:

Aprobados: 3
Mayor: 17

Parte E — Preguntas cortas

Respondan cada una en dos o tres líneas.

Pregunta 12 — ¿Es un algoritmo?

algoritmo bajar()
    x ← 10
    mientras x ≠ 0 hacer
        x ← x - 3
    fin mientras
    escribir x
fin algoritmo

¿Qué condición de un algoritmo no cumple? ¿Qué cambiarían para que la cumpla?

Pregunta 13 — El método de la moneda más grande

Un país tiene monedas de 1, 4 y 6. Sigan el método de “la más grande que cabe” para un vuelto de 8. ¿Da la menor cantidad de monedas? ¿Qué concluyen?

Pregunta 14 — Terminal

Están en /home/rosa/lab03/src. ¿En qué carpeta quedan después de cd ../../lab04?

Pregunta 15 — Tipos de error

Digan cuándo aparece cada problema: al compilar, al ejecutar, o nunca da ningún aviso. (i) Falta un ;. (ii) Quieren 3.5 y escribieron double x = 7 / 2;. (iii) El programa, compilado con -fsanitize=undefined, lee un 0 con scanf en b y calcula a / b entre dos int.

Pregunta 16 — Números aleatorios

Escriban una expresión con rand que dé un entero de -2 a 4, con todos los valores posibles. ¿Para qué sirve fijar la semilla con srand en una simulación?

Pregunta 17 — assert

¿Por qué está mal escribir assert(scanf("%d", &n) == 1); en un programa? Den dos razones.