Primeros programas en C: tipos, operadores y bucles

Escribe tus primeros programas en C y comprende exactamente qué hacen. Compila y ejecuta código con gcc o clang y corrige los problemas que indique el compilador, almacena datos en variables de tipo entero, carácter y coma flotante, y muestra y lee valores con printf y scanf. Construye expresiones con los operadores aritméticos, de comparación, lógicos y a nivel de bits de C, prediciendo las conversiones y la precedencia, y después dirige un programa con if, switch, while, do-while y for. Para terminar, aprende por qué se desbordan los enteros, qué ocurre cuando sucede y cómo escribir y probar código que lo evite.

PrincipianteUnidad 1 de 8 del curso Curso de Programación en C

Lo que aprenderás

  1. Convierte código fuente en C en un programa ejecutable, comprende qué hace el compilador en cada etapa y utiliza sus mensajes para detectar y corregir errores.
    • Escribir, compilar y ejecutar un primer programa en C
      Identifica las partes de un programa mínimo en C, compílalo con gcc o clang y ejecuta el archivo ejecutable desde una terminal.
    • Leer los mensajes del compilador y corregir errores
      Distingue los errores de compilación, las advertencias, los errores de enlace, de ejecución y lógicos, y utiliza los mensajes del compilador y las ejecuciones de prueba para corregirlos.
  2. Declara e inicializa variables, elige entre los tipos enteros y de coma flotante de C y predice el rango y la precisión que ofrece cada tipo.
    • Declarar, inicializar y dar nombre a variables
      Crea variables con un tipo y un nombre significativo, dales valores iniciales y usa constantes para los valores fijos.
    • Elegir un tipo entero y conocer su rango
      Elige entre los tipos enteros con y sin signo de C y los valores de carácter y booleanos, a partir de sus tamaños, rangos y formas literales.
    • Trabajar con números de coma flotante y sus limitaciones
      Almacena números reales en float y double, predice su precisión y sus errores de redondeo, y convierte de forma segura entre valores de coma flotante y enteros.
  3. Da formato preciso a números y texto con printf, y lee y comprueba los valores introducidos por el usuario con scanf.
    • Dar formato a la salida con printf
      Usa especificaciones de conversión, anchos de campo, precisión, indicadores y secuencias de escape para producir exactamente la salida que deseas.
    • Leer y comprobar la entrada con scanf
      Lee números y caracteres introducidos por el usuario, comprueba que la lectura se haya realizado correctamente y recupérate de entradas no válidas.
  4. Usa los operadores aritméticos, de asignación, de comparación, lógicos y a nivel de bits de C, y predice el valor y el tipo de cualquier expresión a partir de las conversiones, la precedencia y la asociatividad.
    • Calcular con operadores aritméticos y de asignación
      Aplica +, -, *, / y %, la división entera, la asignación compuesta y el incremento y decremento, y evita sus errores habituales.
    • Predecir las conversiones de tipos en expresiones mixtas
      Explica cómo convierte C los operandos de distintos tipos, usa conversiones explícitas de tipo de forma deliberada y evita el problema de mezclar valores con y sin signo.
    • Combinar condiciones con operadores relacionales, lógicos y a nivel de bits
      Escribe comparaciones y condiciones compuestas correctas, usa la evaluación en cortocircuito y manipula bits individuales.
    • Aplicar la precedencia, la asociatividad y el orden de evaluación
      Predice cómo agrupa C los operadores de una expresión, evalúa expresiones paso a paso y usa paréntesis cuando la agrupación no esté clara.
  5. Elige entre alternativas con if, else y switch, y repite tareas con bucles while, do-while y for que empiecen, se ejecuten y terminen exactamente como deseas.
    • Tomar decisiones con if y else
      Escribe sentencias if, else if y else con condiciones y llaves correctas, y evita los errores habituales de la sintaxis de C.
    • Seleccionar entre casos con switch
      Usa switch con etiquetas case, break y default para elegir entre valores enteros fijos, y utiliza la continuación al siguiente caso solo de forma deliberada.
    • Repetir mientras se cumpla una condición con while y do-while
      Escribe bucles controlados por una condición que empiecen correctamente, progresen y terminen, incluidos bucles de entrada y bucles que deban ejecutarse al menos una vez.
    • Contar con bucles for, anidar bucles y terminarlos anticipadamente
      Escribe bucles for que se ejecuten exactamente el número de veces deseado, combina bucles dentro de otros bucles y usa break y continue de forma deliberada.
  6. Explica qué ocurre cuando un resultado no cabe en su tipo entero y escribe y prueba código que evite o detecte desbordamientos.
    • Explicar qué ocurre cuando un valor no cabe en su tipo
      Describe la reducción modular de enteros sin signo, el desbordamiento indefinido de enteros con signo y las conversiones fuera de rango, y relaciónalos con fallos reales.
    • Escribir código que evite o detecte desbordamientos
      Elige tipos con suficiente rango, comprueba las operaciones antes de que se desborden y usa herramientas del compilador y pruebas de valores límite para detectar desbordamientos a tiempo.

Guía de estudio de Primeros programas en C

6 capítulos

1

Construir y ejecutar programas en C

El código C se convierte en un programa ejecutable mediante una construcción. Aprende las partes de un primer programa completo, compílalo con un estándar del lenguaje elegido y ejecuta el archivo resultante. Cuando falle una construcción o una prueba, usa la etapa en que se produjo el fallo y el primer mensaje del compilador para localizar la causa. Seguir cuidadosamente el ciclo de edición, construcción y prueba evita que un ejecutable antiguo oculte un cambio fallido y facilita la comprobación de cada corrección.

1.1 Del código fuente a un programa en ejecución

Un programa en C empieza como texto plano y se ejecuta como código máquina. El compilador conecta esas dos formas. Guardar un cambio modifica el código fuente; no cambia el ejecutable que construiste antes.

Lee un programa completo

Guarda esto como hello.c:

#include <stdio.h>

int main(void)
{
    printf("Hello, world!\n");
    return 0;
}

#include <stdio.h> pone a disposición las declaraciones para la entrada y la salida estándar, incluida printf. La ejecución empieza en main. En int main(void), int indica el tipo de retorno y void significa que no hay parámetros. Las llaves delimitan el cuerpo. La llamada a printf escribe el texto entre comillas; \n pasa a una nueva línea. return 0; termina el programa e informa al sistema operativo de que la ejecución fue correcta. Un estado distinto de cero puede indicar un fallo. Desde C99, llegar al final de main también devuelve cero.

Las sentencias simples terminan con punto y coma. La línea de inclusión no lo lleva. C distingue entre mayúsculas y minúsculas: Printf y printf son nombres distintos. Los espacios entre tokens normalmente no afectan al programa, pero una sangría uniforme permite ver la estructura de los bloques.

Compila y después ejecuta

En la terminal, abre la carpeta que contiene el código fuente e introduce:

gcc -std=c17 -Wall -Wextra hello.c -o hello
./hello

El primer comando construye un ejecutable llamado hello; el segundo lo ejecuta desde la carpeta actual. Su salida es Hello, world! seguido de un salto de línea. Puedes usar clang en lugar de gcc. Sin -o hello, el nombre habitual de salida en Linux o macOS es a.out. Los ejecutables de Windows usan .exe. make hello de CS50 es un atajo que pide a la herramienta de construcción que invoque al compilador.

Después de cambiar el saludo, guarda, vuelve a compilar y ejecuta de nuevo. Si falla la compilación, puede que aún exista un ejecutable anterior. Al ejecutarlo se muestra el comportamiento antiguo, por lo que debes comprobar que la construcción haya tenido éxito antes de evaluar tu cambio.

Qué hace la construcción

El preprocesador procesa directivas como #include, insertando el texto de las cabeceras. El compilador propiamente dicho traduce C a lenguaje ensamblador. El ensamblador produce código objeto, que aún puede contener nombres sin resolver. El enlazador combina el código objeto y resuelve referencias como printf. El código de biblioteca puede copiarse en un ejecutable mediante enlace estático o proporcionarse mediante una biblioteca compartida que se carga al ejecutar el programa.

Puedes detener la construcción en etapas intermedias:

OpciónResultado
-ETexto preprocesado
-SLenguaje ensamblador
-cUn archivo objeto, como hello.o

Un archivo objeto aún no es el ejecutable terminado. Un ejecutable puede funcionar sin el compilador, pero está construido para un sistema operativo y un procesador concretos. Para trasladar el programa a otra plataforma, compila allí su código fuente.

Comentarios y versiones del lenguaje

// inicia un comentario que termina en el salto de línea. /* ... */ puede abarcar varias líneas, pero estos comentarios no pueden anidarse: el primer */ termina el comentario. Usa comentarios para explicar una decisión que el código por sí solo no deja clara.

Selecciona explícitamente la versión del lenguaje. C17 admite los ejemplos anteriores, incluidos los comentarios //, long long y las declaraciones en cabeceras de for. C23 añade características como bool como palabra clave; se publicó como ISO/IEC 9899:2024. -Wall -Wextra activa advertencias útiles. Los valores predeterminados de los compiladores varían, por lo que un -std=c17 explícito deja claras las reglas del lenguaje que se pretenden usar.

Puntos clave

  • El código fuente de C debe volver a compilarse después de cada cambio.
  • La ejecución empieza en main; devolver cero indica que la ejecución fue correcta.
  • Las etapas de construcción son preprocesamiento, compilación, ensamblado y enlace.
  • Usa -std=c17 -Wall -Wextra para seleccionar el estándar y activar las advertencias.

1.2 Encontrar la primera causa de un error

El momento en que aparece un problema ayuda a encontrar su causa. Un programa puede no llegar a construirse, detenerse durante la ejecución o terminar con una respuesta incorrecta. Esas situaciones requieren distintas comprobaciones.

Distingue los tipos de problemas
TipoQué observasDónde buscar
Error de compilaciónNo hay un nuevo ejecutableSintaxis, declaraciones y tipos
AdvertenciaUn posible problema; la construcción suele continuarLa expresión indicada en el mensaje
Error de enlaceExiste código objeto, pero no se puede completar el ejecutableNombres de funciones sin resolver o bibliotecas necesarias
Error de ejecuciónAparece un problema durante la ejecuciónOperaciones y valores alcanzados con esa entrada
Error lógicoEl programa se ejecuta, pero da una respuesta no deseadaCondiciones, fórmulas y actualizaciones

Una advertencia puede señalar código no válido o comportamiento indefinido, además de un error habitual. Una compilación correcta no garantiza que el programa sea correcto.

Lee un mensaje de diagnóstico

Un mensaje del compilador suele indicar una ubicación en el código fuente, un nivel de gravedad y una descripción. Un mensaje como hello.c:5:16: error: expected ';' after expression señala que falta un punto y coma cerca de la línea 5. A veces el compilador detecta el problema en la línea siguiente. Revisa la línea indicada y la inmediatamente anterior.

Corrige primero el primer error y después vuelve a compilar. Una comilla sin cerrar puede hacer que las líneas posteriores parezcan parte de una cadena; una llave sin pareja puede hacer que las declaraciones posteriores parezcan estar en el bloque equivocado. Varios mensajes pueden desaparecer después de una sola corrección. No empieces cambiando todas las líneas mencionadas en la salida.

Para este fragmento:

int total = 6
printf("%d\n", total);

La declaración necesita un punto y coma después de 6. La línea de printf puede ser donde el compilador detecta por primera vez que la declaración anterior no terminó. Comprueba también la escritura exacta, que los paréntesis y las llaves estén equilibrados y la cabecera que declara una función de biblioteca. Una llamada a printf necesita <stdio.h>; las llamadas a funciones no declaradas no están permitidas en C99 y posteriores.

Toma las advertencias en serio

Construye con -Wall -Wextra. Estas opciones ayudan a detectar una asignación en una condición, variables no utilizadas, comparaciones entre valores con y sin signo y formatos de salida que no coinciden. Por ejemplo, printf("%d\n", 3.5); pide un entero pero pasa un double; el formato no convierte el argumento por ti. Corrige el formato a %f o convierte el valor deliberadamente. -Werror hace que las advertencias detengan la construcción.

Comprende el comportamiento indefinido

Comportamiento indefinido significa que C no impone requisitos sobre el comportamiento del programa. Pueden causarlo el desbordamiento de enteros con signo, la división entera por cero y algunas lecturas de variables sin inicializar. Un programa puede detenerse, mostrar un valor inesperado o parecer que funciona. Las opciones del compilador pueden cambiar lo que ocurre.

Un mensaje como «Floating point exception» puede incluso ser resultado de una división entera por cero en algunos sistemas. Diagnostica la operación y la entrada, no solo la redacción de un mensaje del sistema operativo. Comprueba el divisor antes de ejecutar la división.

Sigue la ejecución de un resultado incorrecto

Para un error lógico, predice a mano un caso pequeño y compáralo con la salida real. Añade llamadas temporales a printf para mostrar un total acumulado o el valor comprobado por una condición. Cambia una causa a la vez y vuelve a construir después de cada cambio. Prueba valores habituales y límites como cero, uno, una entrada negativa y la mayor entrada esperada. Una traza muestra dónde empieza el programa a apartarse de tu predicción.

Puntos clave

  • Corrige el primer mensaje de diagnóstico y vuelve a construir antes de investigar los mensajes posteriores.
  • Una advertencia puede señalar comportamiento indefinido; la compilación por sí sola no demuestra que el programa sea correcto.
  • Ajusta las conversiones de formato a los tipos de los argumentos.
  • Para un resultado incorrecto, sigue la ejecución con una entrada pequeña y compara los valores intermedios.
  • Prueba tanto los límites como las entradas habituales.
2

Almacenar datos en variables de los tipos básicos

Un tipo determina cómo se interpreta un valor almacenado y qué valores puede contener. Empieza por las declaraciones, los valores iniciales y los nombres disponibles dentro de un bloque. Después elige los tipos enteros según su rango y los tipos de coma flotante según su precisión y su rango. Los códigos de caracteres y los valores booleanos también forman parte de este panorama. Leer literales y comprobar límites te prepara para predecir qué puede representar un cálculo antes de escribirlo.

2.1 Variables, nombres y valores fijos

Una variable da nombre a un valor almacenado. Su tipo indica a C cómo interpretar ese almacenamiento. Declaras el tipo antes de usar la variable, y esta conserva ese tipo durante todo su tiempo de vida.

Da un valor inicial a cada variable
int count = 0;
double length_m = 1.8;
count = count + 1;

Las dos primeras líneas declaran e inicializan variables. La tercera asigna un nuevo valor a una variable existente: lee el valor actual de count, suma uno y vuelve a almacenar el resultado. La asignación es una acción, por lo que count = count + 1 tiene sentido aunque no sería una ecuación matemática.

La inicialización ocurre en una declaración; la asignación puede ocurrir después. En int a, b = 5;, solo se inicializa b. Una a local tiene un valor indeterminado hasta que almacenas uno. Leerla puede producir un comportamiento impredecible, incluido comportamiento indefinido. Inicializa los contadores y los totales al declararlos, como en int total = 0;.

La asignación copia el valor en ese momento:

int width = 4;
int area = width * 3;
width = 7;
printf("%d %d\n", width, area);

Esto muestra 7 12. La asignación a area calculó 12 una sola vez. No creó una fórmula que vuelva a calcularse cada vez que cambia width. Para actualizar area, asígnale un nuevo cálculo.

Elige nombres legibles

Un identificador puede contener letras, dígitos y guiones bajos, pero no puede empezar por un dígito ni ser una palabra clave. height_cm y second_place son válidos; 2nd_place no lo es, y my-score se interpreta como una resta. Los nombres distinguen entre mayúsculas y minúsculas, por lo que total y Total son distintos. Los nombres que indican un significado o una unidad facilitan la revisión de las fórmulas.

Sigue el ámbito de bloque

Un nombre local está disponible desde su declaración hasta el final de su bloque. Desde C99, las declaraciones pueden aparecer entre otras sentencias.

int count = 2;
{
    int count = 9;
    printf("%d\n", count);
}
printf("%d\n", count);

La declaración interior oculta el nombre exterior mientras se ejecuta ese bloque. La salida es 9 y después 2, en líneas separadas. Son dos variables, no dos asignaciones a una misma variable. Declarar dos veces el mismo nombre en un bloque es un error. Reutilizar nombres en bloques anidados está permitido, pero dificulta seguir el código, por lo que es preferible usar nombres que aclaren cada función.

Da nombre a los valores fijos
const int SECONDS_PER_MINUTE = 60;
#define MAX_HEIGHT 8

const crea una variable con tipo a la que no se puede asignar un nuevo valor mediante ese nombre. Una sentencia posterior SECONDS_PER_MINUTE = 90; requiere un mensaje de diagnóstico. El inicializador proporciona el valor fijo en la declaración.

#define crea una sustitución del preprocesador: las apariciones posteriores de MAX_HEIGHT se sustituyen por el token 8 antes de la compilación. Esta directiva no lleva = ni punto y coma. Un punto y coma pasaría a formar parte de la sustitución y podría romper una expresión como height > MAX_HEIGHT.

Un nombre como SECONDS_PER_MINUTE indica a quien lee por qué aparece 60 en un cálculo y te da un solo lugar donde mantenerlo. Las mayúsculas son una convención de nombres, no una regla que haga constante un valor. La declaración o la directiva proporciona ese comportamiento.

Puntos clave

  • Inicializa las variables locales antes de leerlas.
  • La asignación copia un valor una vez; los valores dependientes no se actualizan por sí solos.
  • Un bloque limita dónde puede usarse un nombre local.
  • Los nombres distinguen entre mayúsculas y minúsculas y no pueden empezar por un dígito.
  • const proporciona una variable con tipo de solo lectura; #define proporciona una sustitución de tokens.

2.2 Elegir tipos enteros, de carácter y booleanos

Los tipos enteros almacenan números enteros de forma exacta dentro de un rango fijo. Elige un tipo según los valores que debe manejar el programa, incluidos los resultados intermedios, y según si los valores negativos tienen sentido.

Tamaños y rangos

El estándar garantiza anchos mínimos: al menos 8 bits para char, 16 para short e int, 32 para long y 64 para long long. Cada tipo de esta secuencia tiene al menos el mismo ancho que el anterior. Los tamaños reales dependen de la plataforma.

TipoBytes habituales en Linux/macOS de 64 bitsDiferencia en Windows de 64 bits
char1Ninguna
short2Ninguna
int4Ninguna
long84 bytes
long long8Ninguna

sizeof(int) indica el tamaño en bytes como un size_t; muéstralo con %zu. La cabecera <limits.h> proporciona nombres como INT_MIN, INT_MAX, UINT_MAX, LLONG_MAX y CHAR_BIT, el número de bits por byte.

Con N bits de valor, un entero sin signo tiene valores de 0 a 2ᴺ − 1. Un entero con signo en complemento a dos con N bits abarca de −2ᴺ⁻¹ a 2ᴺ⁻¹ − 1. Así, un int de 32 bits abarca de −2 147 483 648 a 2 147 483 647, mientras que un unsigned int de 32 bits llega a 4 294 967 295. C23 exige el complemento a dos; C17 también permitía otras representaciones.

En un patrón de complemento a dos de 8 bits, el bit de mayor peso tiene valor −128. 11111111 representa −1 y 10000000 representa −128. Para formar −5, empieza con 00000101, invierte los bits para obtener 11111010 y suma uno para obtener 11111011.

Los caracteres también son enteros

Un char almacena un código de carácter en un byte. Usa comillas simples para una constante de carácter, como en 'A'. En ASCII, 'A' tiene el código 65 y '0' tiene el código 48. printf("%c %d\n", 'A', 'A'); muestra A 65 en ASCII. Una constante de carácter en C tiene tipo int, por lo que sizeof('A') es sizeof(int).

char sin modificador puede tener signo o no, según la implementación. Usa signed char o unsigned char cuando importe el rango numérico. <stdint.h> ofrece tipos enteros de ancho exacto como int32_t y uint8_t en las plataformas que admiten esos anchos exactos; las plataformas habituales los admiten.

Lee literales enteros

Una constante decimal como 42 tiene tipo int cuando cabe en ese tipo. Los sufijos solicitan otros tipos: 42u es sin signo, 42L es long, 42LL es long long y 42ULL es unsigned long long. El hexadecimal usa 0x, como en 0x2A. Un cero inicial indica octal: 052 también significa 42 y 010 significa 8. 09 no es un octal válido. C23 añade notación binaria como 0b101010.

Valores de verdad y elección del tipo

En C17, incluye <stdbool.h> para usar bool, true y false; C23 los convierte en palabras clave. Una conversión a bool da falso para cero y verdadero para cualquier valor distinto de cero, incluido 0.5. Muestra un valor booleano con %d para ver 0 o 1.

Usa int para recuentos y contadores de bucle habituales. Usa long long o un tipo de ancho exacto adecuado cuando el rango pueda superar aproximadamente los 2,1 mil millones. Los tipos sin signo son adecuados para patrones de bits, pero una resta por debajo de cero se reduce modularmente a un valor grande, por lo que también necesitan comprobaciones de rango.

Puntos clave

  • Usa los límites del tipo, no un tamaño supuesto para long.
  • Un int con signo de 32 bits llega a 2 147 483 647.
  • Los valores sin signo no tienen rango negativo y se reducen modularmente al bajar de cero.
  • Un cero inicial indica un literal entero octal.
  • Que char sin modificador tenga signo depende de la implementación.
  • En C17, los nombres booleanos proceden de <stdbool.h>.

2.3 Valores decimales y precisión limitada

Un tipo de coma flotante almacena una aproximación a un número real. Tiene un rango amplio, pero solo un número fijo de cifras significativas. El rango y la precisión responden a preguntas distintas: qué magnitud puede tener un valor y con qué detalle pueden distinguirse valores cercanos.

Elige un tipo de coma flotante

En los sistemas IEEE 754 habituales, float ocupa 4 bytes y contiene aproximadamente 6–7 cifras decimales significativas. double ocupa 8 bytes y contiene aproximadamente 15–16. Sus mayores valores finitos son aproximadamente 3,4 × 10³⁸ y 1,8 × 10³⁰⁸. long double tiene al menos la misma precisión que double, con un formato que depende de la plataforma. double es una opción predeterminada útil para magnitudes medidas.

Las constantes como 2.0, 3.14 y 1e-3 son double; 1e-3 significa 0,001. Añade f para float, como en 2.5f, o L para long double. La constante 2 es un entero, por lo que puede dar lugar a una división entera donde 2.0 produciría una división de coma flotante.

Observa la aproximación almacenada

Muchas fracciones decimales no tienen una forma binaria finita exacta. En un sistema habitual de precisión doble:

printf("%.20f\n", 0.1);
printf("%.17f\n", 0.1 + 0.2);

Las salidas son 0.10000000000000000555 y 0.30000000000000004. Las cifras adicionales muestran la aproximación almacenada. Explican por qué 0.1 + 0.2 == 0.3 puede ser falso.

Usa una tolerancia al comparar medidas calculadas:

#include <math.h>
/* inside main */
if (fabs(a - b) < 1e-9) {
    printf("Close enough\n");
}

fabs devuelve una diferencia absoluta. Elige la tolerancia según la escala y la precisión de los valores; 1e-9 es un ejemplo, no un ajuste universal. Algunas construcciones en Linux necesitan -lm al final del comando del compilador para enlazar las funciones matemáticas.

La precisión afecta a los enteros grandes

Un double binario representa exactamente todos los enteros hasta 2⁵³. Un float binario deja de representar todos los enteros por encima de 2²⁴ = 16 777 216: asignar 16 777 217 a un float de ese tipo da 16 777 216. Un rango mayor no significa que sigan disponibles todos los pasos entre enteros consecutivos.

La impresión redondea el valor binario almacenado. printf("%.2f\n", 2.675); suele mostrar 2.67, porque el valor almacenado es ligeramente menor que 2,675. Para importes exactos en centésimas enteras, usa un entero como long long cents = 1999; y después da formato a la presentación al final.

Convierte y redondea de forma deliberada

La conversión a un entero descarta la fracción hacia cero: (int)3.99 es 3 y (int)-3.99 es −3. round de <math.h> devuelve un valor de coma flotante redondeado al entero más cercano, con los valores a mitad de camino alejados de cero: round(-2.5) da −3,0. Convierte ese resultado a un entero solo cuando quepa en el tipo. Una conversión de coma flotante a entero cuya parte entera está fuera de rango tiene comportamiento indefinido.

En la aritmética IEEE, los resultados fuera del rango finito pueden convertirse en infinito. Una división de coma flotante de un valor distinto de cero por cero puede producir infinito; cero dividido por cero produce NaN, que significa «no es un número». Un NaN se compara como distinto incluso de sí mismo. Estos son comportamientos de coma flotante IEEE; la división entera por cero tiene comportamiento indefinido.

Puntos clave

  • Los tipos de coma flotante tienen precisión y rango limitados.
  • Compara las medidas calculadas con una tolerancia adecuada.
  • La impresión redondea la aproximación almacenada.
  • La conversión de coma flotante a entero trunca hacia cero y requiere un resultado representable.
3

Mostrar la salida y leer la entrada con printf y scanf

Un programa necesita un intercambio claro de texto y valores. Da formato a la salida para que quien la lea pueda distinguir etiquetas, campos y medidas redondeadas, y después ajusta cada conversión de entrada a la variable que la recibe. La lectura de texto tiene varios resultados posibles: todos los valores solicitados, solo algunos o ninguna conversión. Comprobar esos resultados y el texto sin leer hace que los cálculos posteriores dependan de valores que el programa realmente ha obtenido.

3.1 Mostrar valores con el formato deseado

printf convierte valores en texto mediante una cadena de formato. Los caracteres ordinarios se muestran tal como están escritos. Una conversión que empieza por % indica cómo mostrar el siguiente argumento.

Ajusta cada conversión a su argumento
printf("%d samples, %.2f kg\n", 4, 1.5);

Esto muestra 4 samples, 1.50 kg y un salto de línea. Las conversiones corresponden a los argumentos en orden. Un formato no convierte el tipo del argumento proporcionado al tipo que pretendías usar.

Tipo de valor o representaciónConversión
int%d o %i
unsigned int%u
long, long long%ld, %lld
size_t%zu
double, o un float promocionado%f, %e, %g
long double%Lf
Carácter, literal de cadena%c, %s
Hexadecimal sin signo, octal%x o %X, %o

Un argumento float se promociona a double en printf, por lo que ambos usan %f; aquí %lf es equivalente. Pasar 3 a %f es una discrepancia de tipos, no una petición para mostrar 3.0. Usa 3.0 o (double)3. Un tipo que no coincide o un argumento ausente puede causar comportamiento indefinido. Construye el programa con las advertencias activadas para que se comprueben las cadenas de formato literales.

Establece el ancho y la precisión

El ancho es un número mínimo de caracteres, incluidos el signo y el punto decimal. La precisión de coma flotante después del punto controla los decimales de %f. En %8.2f, ocho es el ancho mínimo y dos es el número de cifras después del punto decimal. Un número más ancho se muestra completo; el ancho no lo recorta.

Con el entero 42, %5d da 42, %-5d da 42 , %05d da 00042 y %+d da +42. Sin una precisión, %f muestra seis decimales.

printf("%-6s%8s\n", "Item", "Price");
printf("%-6s%8.2f\n", "Tea", 2.5);
printf("%-6s%8.2f\n", "Cake", 12.75);

Cada etiqueta ocupa un campo de seis caracteres alineado a la izquierda, seguido de un campo de precio de ocho alineado a la derecha. Ambos precios tienen dos decimales, por lo que sus puntos decimales quedan alineados. Los espacios proceden de los anchos de campo, no de una separación automática entre argumentos.

Representaciones científica y general

%e usa notación científica: 12 345,678 se muestra como 1.234568e+04 con la precisión predeterminada. Para %g, la precisión cuenta cifras significativas, seis de forma predeterminada. Usa notación científica cuando el exponente decimal es menor que −4 o al menos igual a la precisión, y elimina los ceros finales. Así, %g muestra 0,0001 como 0.0001, 0,00001 como 1e-05 y 1 000 000,0 como 1e+06. No se limita a elegir la representación más corta posible.

Muestra caracteres especiales

Dentro de una cadena de C, \n significa nueva línea, \t significa tabulación, \\ significa barra invertida y \" significa comilla. Un signo de porcentaje en un formato se escribe %%: printf("100%%\n"); muestra 100% y un salto de línea.

printf no añade espacios ni saltos de línea por sí solo. printf("Count:"); printf("%d", 4); muestra Count:4. Una terminal puede retrasar el texto sin salto de línea; fflush(stdout); envía un mensaje de solicitud pendiente antes de pedir la entrada.

Puntos clave

  • Ajusta las conversiones a los tipos reales de los argumentos.
  • El ancho de campo es un mínimo; la precisión de %f cuenta decimales.
  • La precisión de %g cuenta cifras significativas.
  • Escribe %% para un signo de porcentaje literal en un formato.
  • printf no añade espacios ni saltos de línea salvo que el formato los incluya.

3.2 Leer la entrada y comprobar qué se ha leído

scanf lee texto, lo convierte y almacena valores en variables. Comprueba su resultado antes de usar esas variables. Un texto que empieza por dígitos puede aceptarse solo en parte, por lo que una conversión correcta y una línea de entrada completamente válida son cosas distintas.

Proporciona un destino del tipo adecuado
int count;
double mass_kg;
if (scanf("%d %lf", &count, &mass_kg) != 2) {
    return 1;
}

El & pasa la dirección de cada variable para que scanf pueda almacenar datos en ella. %d necesita un int, %u un entero sin signo, %ld un long y %lld un long long. Para la entrada de coma flotante, %f necesita un float y %lf un double. A diferencia de printf, estas dos conversiones no son intercambiables. La falta de & o un tipo de destino incorrecto pueden causar comportamiento indefinido.

%d lee decimal. %i también reconoce entradas hexadecimales y octales con cero inicial; con %i, el texto «010» significa 8. Usa %d para recuentos decimales habituales.

Comprueba el valor de retorno

scanf devuelve el número de asignaciones completadas. Para dos valores solicitados, una lectura correcta significa 2. Una conversión fallida deja su destino sin cambios. Puede que siga sin inicializar, por lo que no debes usarlo salvo que el número devuelto confirme que la lectura fue correcta. EOF significa que la entrada terminó o que ocurrió un error de entrada antes de que se completara la primera conversión.

Para scanf("%d:%d", &h, &m), los dos puntos deben aparecer literalmente. La entrada «14:30» rellena ambas variables. La entrada «14 30» rellena h, después no encuentra los dos puntos y deja m sin cambios. El valor de retorno es 1.

Comprende los caracteres en blanco y la entrada parcial

Las conversiones numéricas omiten espacios, tabulaciones y saltos de línea iniciales. %c lee el carácter inmediatamente siguiente, incluido un salto de línea que haya quedado pendiente. Usa scanf(" %c", &choice) para omitir los caracteres en blanco antes de leer un carácter visible. Evita un salto de línea final en el formato: puede hacer que la llamada espere un carácter posterior que no sea blanco.

Con %d, «12abc» almacena 12 y deja «abc» sin leer. «3.7» almacena 3 y deja «.7». «abc» no almacena nada y devuelve 0. Volver a llamar a la misma conversión sin consumir la entrada incorrecta puede fallar indefinidamente con el mismo carácter.

Recupérate de una conversión fallida
int n, result;
while ((result = scanf("%d", &n)) != 1) {
    if (result == EOF) {
        return 1;
    }
    int ch;
    while ((ch = getchar()) != '\n' && ch != EOF) {
    }
    printf("Try again: ");
    fflush(stdout);
}

getchar devuelve un int, lo que le permite representar tanto un carácter como EOF. El bucle interior descarta la línea que falló. El bucle exterior vuelve a intentarlo, pero sigue aceptando un prefijo numérico como 12 en «12abc». Exigir que el siguiente carácter sea '\n' comprueba la regla más estricta de un entero seguido inmediatamente de un salto de línea; esa regla también rechaza los espacios finales. Una política que permita espacios o una última línea terminada en EOF debe manejar esos casos explícitamente.

Después de una conversión correcta, comprueba el rango permitido, como de 1 a 8 para un ajuste de altura. scanf no rechaza de forma segura un valor numérico fuera de rango: un valor demasiado grande para su destino tiene comportamiento indefinido. Usa entradas de prueba que quepan en el tipo elegido. Para calcular un resultado como la masa dividida por la altura al cuadrado, valida que las medidas sean positivas y mantén el cuadrado agrupado como height_m * height_m.

Puntos clave

  • Pasa las direcciones de las variables y usa %lf para leer un double.
  • Exige que el número devuelto coincida con el número de valores necesarios.
  • Una conversión correcta puede dejar entrada adicional sin leer.
  • Consume la entrada incorrecta antes de reintentar una conversión numérica fallida.
  • Comprueba los valores permitidos después de leer; la conversión de entrada tiene sus propios límites de rango del tipo.
  • Usa " %c" cuando una lectura de carácter deba omitir los caracteres en blanco.
4

Realizar cálculos con operadores y expresiones

Una expresión combina valores, tipos y operadores. Sigue primero la división entera y las actualizaciones, y después las conversiones que ocurren en cada operación. Las comparaciones y las condiciones lógicas de protección controlan si se llega a un cálculo, mientras que las operaciones a nivel de bits actúan sobre bits individuales. Por último, distingue la agrupación establecida por la precedencia de la secuenciación necesaria cuando una expresión modifica una variable. Tanto el valor como el tipo de un resultado intermedio importan.

4.1 Aritmética, asignación y actualizaciones de una unidad

Toda expresión en C tiene un valor y un tipo. El tipo determina cómo funciona una operación. La división entera, por ejemplo, no puede conservar un resultado fraccionario aunque después lo almacenes en una variable de coma flotante.

Cocientes y restos

+, - y * suman, restan y multiplican. / divide y % da un resto entero. La multiplicación, la división y el resto tienen mayor precedencia que la suma y la resta.

Cuando ambos operandos de una división son enteros, C trunca el cociente hacia cero. 17 / 5 es 3, -7 / 2 es −3 y 7 / -3 es −2. El resto cumple la identidad de la división: multiplicar el cociente por el divisor y sumar el resto da el dividendo. Así, -7 % 2 es −1 y 7 % -3 es 1. Un resto distinto de cero tiene el signo del dividendo.

Para comprobar si un número es par, usa n % 2 == 0. Para comprobar si es impar, tanto para enteros positivos como negativos, usa n % 2 != 0; comprobar si es exactamente 1 deja fuera los valores impares negativos. % necesita operandos enteros; <math.h> proporciona fmod para restos de coma flotante. Un divisor entero igual a cero hace que tanto / como % sean indefinidos, por lo que debes comprobarlo antes de la operación.

Para 135 minutos, 135 / 60 da dos horas completas y 135 % 60 da 15 minutos restantes. Para un entero decimal positivo, % 10 extrae su última cifra y / 10 elimina esa cifra: 4096 da 6 y 409.

Conserva las fracciones cuando las necesites
int correct = 45, total = 60;
double percent = 100.0 * correct / total;

La constante de coma flotante hace que la multiplicación sea de coma flotante, y la división da 75,0. En correct / total * 100, la primera operación es una división entera y da cero. Multiplicar ese cero no puede recuperar la fracción perdida. Multiplicar primero con enteros, correct * 100 / total, da aquí 75, siempre que el producto quepa en el tipo.

Almacena un nuevo valor

Una asignación evalúa su lado derecho y almacena el valor convertido en el lado izquierdo. La expresión de asignación tiene ese valor almacenado, por lo que a = b = 0; asigna cero a ambas variables, agrupando desde la derecha.

Las asignaciones compuestas combinan un cálculo y su almacenamiento: x += 5 suma cinco a x, y x /= 2 divide x entre dos usando su tipo actual. Todo el operando derecho forma parte del cálculo. Si x es 4, x *= 2 + 1 lo convierte en 12, porque lo multiplica por tres.

Actualizaciones prefijas y posfijas

++ suma uno y -- resta uno. En una línea por separado, i++; y ++i; tienen el mismo efecto final. Dentro de una expresión mayor, la forma posfija produce el valor anterior; la prefija produce el nuevo.

InicioSentenciaValores finales
a = 5y = a++;y = 5, a = 6
a = 5y = ++a;y = 6, a = 6

Mantén las modificaciones en un orden fácil de seguir. i = i++ + 1; y printf("%d %d\n", n, n++); tienen usos conflictivos no secuenciados de la misma variable y comportamiento indefinido. Separa la lectura, la actualización y la salida en sentencias cuando importe su orden.

Puntos clave

  • La división entera trunca hacia cero.
  • Un resto entero distinto de cero tiene el signo del dividendo.
  • Usa un operando de coma flotante antes de la división si necesitas una fracción.
  • La forma posfija produce el valor anterior; la prefija produce el valor actualizado.
  • Mantén las lecturas y actualizaciones conflictivas en sentencias separadas.

4.2 Los tipos se determinan antes de almacenar un resultado

C convierte los valores a medida que se aplica cada operador. La variable de destino recibe el resultado final; no cambia las operaciones anteriores dentro de la expresión.

Convierte antes de dividir
int sum = 7, count = 2;
double a = sum / count;
double b = (double)sum / count;
double c = (double)(sum / count);

a pasa a valer 3,0: los dos operandos enteros producen el entero 3, que después se convierte. b pasa a valer 3,5: la conversión explícita transforma sum antes de la división, por lo que count también se convierte a double. c pasa a valer 3,0: su conversión explícita está fuera de la división y se realiza después de descartar la fracción.

La misma regla se aplica dentro de una expresión más larga. 7 / 2 * 2.0 da 6,0, porque el primer resultado es el entero 3. 7 / 2.0 * 2 da 7,0. Recorre los operadores en su orden de agrupación y marca el tipo de cada valor intermedio.

Encuentra un tipo aritmético común

Si alguno de los operandos es de coma flotante, la aritmética suele convertir al tipo de coma flotante más ancho que intervenga: long double, después double y después float. Para los enteros, los tipos pequeños pasan primero por una promoción de enteros. En plataformas con char de 8 bits, short de 16 bits e int de 32 bits, todos los valores de los tipos más pequeños caben en int, por lo que esos tipos se promocionan a int.

Con unsigned char a = 200, b = 100;, a + b es el entero 300 en un sistema con bytes de 8 bits e int de 32 bits. No se reduce modularmente en 255 durante la suma. Almacenar 300 en un carácter sin signo de 8 bits lo reduce a 44. La promoción también puede producir desbordamiento con signo: dos valores unsigned short de 50 000 se multiplican como int, pero 2 500 000 000 supera un INT_MAX de 32 bits.

Para los tipos con y sin signo, el rango del tipo y el rango de valores representables determinan el tipo común. int con unsigned int se convierte a un tipo sin signo. Un tipo con signo de rango superior puede mantener la operación con signo cuando representa todos los valores del tipo sin signo; en caso contrario, aún puede resultar una operación aritmética sin signo.

El problema de mezclar tipos con y sin signo

Con un entero sin signo de 32 bits, -1 < 0u es falso. El −1 con signo se convierte al valor sin signo 4 294 967 295 antes de la comparación. De igual modo, 2u - 10 da 4 294 967 288. Un resultado de sizeof también es sin signo, lo que importa al compararlo con un recuento que podría ser negativo. Activa las advertencias y mantén las cantidades relacionadas en tipos compatibles.

Las conversiones al almacenar pueden perder información

Una conversión de coma flotante a entero trunca hacia cero. Su parte entera debe caber en el tipo o el comportamiento es indefinido. Un entero convertido a un tipo sin signo de N bits se reduce módulo 2ᴺ; −1 se convierte en el valor máximo sin signo. En C17, una conversión entera fuera de rango a un tipo con signo tiene un resultado definido por la implementación o emite una señal definida por la implementación. Esa conversión es distinta del desbordamiento indefinido de la aritmética con signo.

Las constantes de carácter también participan en la aritmética. '7' - '0' da 7 porque C garantiza códigos consecutivos para los dígitos decimales. En ASCII, las letras también son consecutivas, pero no supongas esa propiedad para todos los juegos de caracteres. En las fórmulas con enteros, multiplicar antes de dividir solo puede conservar información útil si ese producto cabe en el tipo del cálculo.

Puntos clave

  • El tipo de destino no cambia la aritmética anterior.
  • Convierte explícitamente un operando antes de la operación que necesita un tipo de mayor rango o de coma flotante.
  • Los enteros pequeños se promocionan antes de la aritmética.
  • Mezclar operandos con y sin signo puede cambiar las comparaciones y las restas.
  • La conversión fuera de rango y el desbordamiento aritmético con signo siguen reglas distintas.
  • Sigue los tipos intermedios además de los valores finales.

4.3 Condiciones y patrones de bits

Una condición pregunta si algo es verdadero. Una operación a nivel de bits actúa sobre los bits individuales de un entero. C asigna operadores distintos a estas tareas, aunque sus símbolos se parezcan.

Compara valores y combina pruebas

Las comparaciones <, >, <=, >=, == y != producen un int: 1 para verdadero y 0 para falso. En una condición, cero es falso y cualquier valor distinto de cero es verdadero. Así, if (count) comprueba si count es distinto de cero. == compara dos valores; = asigna uno nuevo.

&& significa que ambas condiciones deben ser verdaderas, || significa que al menos una debe ser verdadera y ! invierte el valor de verdad. Devuelven 0 o 1. Un rango de 1 a 8 necesita dos comparaciones:

height >= 1 && height <= 8

1 <= height <= 8 no expresa ese rango en C. Se agrupa como (1 <= height) <= 8, por lo que la segunda comparación recibe 0 o 1 y siempre es verdadera. Para comprobar si está fuera del rango, escribe height < 1 || height > 8.

Negar una condición compuesta cambia ambas partes: !(a && b) equivale a !a || !b, y !(a || b) a !a && !b. Estas son las leyes de De Morgan. Explican por qué estar fuera de un intervalo necesita un «o», aunque estar dentro necesite un «y».

Coloca una protección antes de la operación de riesgo
if (d != 0 && n / d > 2) {
    printf("Above two\n");
}

&& evalúa primero su lado izquierdo. Si ese lado es falso, omite el derecho. || omite su lado derecho cuando el izquierdo es verdadero. Esta evaluación en cortocircuito permite que una condición de protección proteja una operación posterior. Invertir las dos partes anteriores haría la división antes de comprobar el divisor. Para enteros con signo, existe un problema de rango aparte cuando INT_MIN se divide por −1 en un sistema de complemento a dos; comprueba también esa pareja cuando sean posibles valores arbitrarios.

Una prueba de año bisiesto combina divisibilidad y excepciones: (y % 4 == 0 && y % 100 != 0) || y % 400 == 0. El año 2000 supera la prueba de 400; 1900 falla porque se aplica la excepción de los años de siglo.

Trabaja bit a bit

El operador a nivel de bits & conserva los bits activados en ambos operandos; | conserva los activados en cualquiera de ellos; ^ conserva los bits que difieren; ~ invierte los bits. Con 12 (1100) y 10 (1010), los resultados son 8, 14 y 6 para &, | y ^.

Los desplazamientos mueven bits. Para valores no negativos cuyo resultado cabe en el tipo, x << k multiplica por 2ᵏ y x >> k descarta k bits de menor peso, dividiendo por 2ᵏ y descartando el resto. Usa tipos sin signo para trabajar con bits. En C17, desplazar una cantidad negativa o al menos igual al ancho del tipo promocionado es indefinido; desplazar a la izquierda un valor negativo con signo es indefinido y desplazarlo a la derecha está definido por la implementación.

Las posiciones de los bits empiezan en cero. flags | (1u << 3) activa el bit 3; flags & ~(1u << 3) lo desactiva; (flags >> 3) & 1u lo lee. 1 & 2 es 0 porque no hay ningún bit activado en común, mientras que 1 && 2 es 1 porque ambos valores son distintos de cero. Los operadores a nivel de bits evalúan ambos operandos y no pueden sustituir a las condiciones lógicas de protección.

Puntos clave

  • Las comparaciones y los operadores lógicos producen 0 o 1.
  • Una prueba de rango une dos comparaciones con &&.
  • Las condiciones de protección en cortocircuito deben ir antes de la operación protegida.
  • Usa operadores lógicos para valores de verdad y operadores a nivel de bits sin signo para máscaras.
  • Las posiciones de los bits empiezan en cero; las cantidades de desplazamiento deben estar dentro del ancho del tipo promocionado.

4.4 Agrupar expresiones y ordenar efectos

Dos preguntas ayudan a leer una expresión: ¿qué operandos pertenecen a cada operador y cuándo ocurren las operaciones con efectos secundarios? La precedencia y la asociatividad responden a la pregunta sobre la agrupación. En general, no fijan el orden de evaluación de los operandos.

Sigue la precedencia

Los operadores situados más arriba en esta tabla tienen mayor precedencia. Cada fila está por debajo de la anterior; los operadores separados por «después» tienen niveles distintos.

De mayor a menorOperadores
Posfijosx++, x--, llamadas
Unarios++x, --x, +, -, !, ~, conversiones explícitas, sizeof, obtención de dirección &
Multiplicativos*, /, %
Aditivos+, -
Desplazamiento<<, >>
Relacionales<, <=, >, >=
Igualdad==, !=
A nivel de bits&, después ^, después `
Lógicos&&, después `
Condicional?:
Asignación=, += y otras asignaciones compuestas
Coma,

Los paréntesis modifican la agrupación. 2 + 3 * 4 es 14; (2 + 3) * 4 es 20.

Usa la asociatividad en niveles iguales

La mayoría de los operadores aritméticos binarios se agrupan de izquierda a derecha. 10 - 4 - 3 significa (10 - 4) - 3, que es 3. 100 / 10 / 2 da 5. La asignación se agrupa de derecha a izquierda, por lo que a = b = 0 almacena cero en ambas variables. La agrupación describe la estructura de la expresión; no establece una regla general de ejecución de izquierda a derecha.

Para int a = 3, b = 4, c = 5;, sigue la ejecución de a + b * c > 20 && c % 2 == 1. La multiplicación da 20, la suma da 23 y 23 > 20 da 1. El resto da 1 y su prueba de igualdad da 1. El 1 && 1 final es 1.

Protege con paréntesis las pruebas de bits y las asignaciones

x & 1 == 0 significa x & (1 == 0), por lo que siempre produce cero. Escribe (x & 1) == 0 para comprobar el bit de menor peso. La suma también tiene mayor precedencia que el desplazamiento: 1 << 2 + 1 es 1 << 3, u 8.

La asignación tiene menor precedencia que la comparación. a = b == c almacena el 0 o 1 de la comparación en a. En while ((ch = getchar()) != '\n'), los paréntesis interiores hacen que la asignación reciba primero el carácter. Sin ellos, ch recibiría un resultado de comparación.

Elige un valor o secuencia dos acciones

condition ? first : second evalúa la condición y después solo la expresión del valor seleccionado. max = (a > b) ? a : b; elige un valor; un if puede elegir un bloque de acciones. El operador coma evalúa primero su expresión izquierda y produce el valor de su expresión derecha. Puede coordinar dos actualizaciones en una cabecera de for. Las comas entre argumentos de funciones son separadores y no establecen ese orden.

Mantén separados los efectos secundarios

C deja sin especificar muchos órdenes de operandos y argumentos. n++ * n++ tiene cambios no secuenciados en la misma variable, por lo que tiene comportamiento indefinido. Los paréntesis adicionales no corrigen la falta de secuenciación. Usa sentencias separadas cuando una acción deba preceder a otra. &&, ||, ?: y el operador coma proporcionan reglas específicas de secuenciación; la precedencia aritmética ordinaria no ofrece esa garantía.

Puntos clave

  • La precedencia determina la agrupación; en general no determina el orden de evaluación.
  • En un mismo nivel aritmético, la agrupación suele ser de izquierda a derecha.
  • Pon entre paréntesis un resultado de una operación a nivel de bits antes de compararlo.
  • El operador condicional evalúa solo la expresión del valor seleccionado.
  • El operador coma secuencia operandos; las comas que separan argumentos no lo hacen.
  • Separa las actualizaciones conflictivas para evitar comportamiento indefinido.
5

Controlar el flujo del programa con condiciones y bucles

El control de flujo determina qué sentencias se ejecutan y con qué frecuencia. Usa condiciones para seleccionar una rama, casos enteros exactos para seleccionar una entrada de switch y bucles para repetir una tarea hasta que se cumpla su regla de parada. Las trazas hacen visible el orden: valores iniciales, pruebas, sentencias del cuerpo y actualizaciones. Presta especial atención a las entradas límite y al destino de break o continue dentro de construcciones anidadas, donde un pequeño cambio de sintaxis puede alterar todo el recorrido.

5.1 Elegir la rama adecuada

Una sentencia if elige si se ejecuta una acción. Su condición debe estar entre paréntesis. Cero significa falso; cualquier valor distinto de cero significa verdadero.

Usa llaves para mostrar la rama
if (temperature < 0) {
    printf("Freezing\n");
} else {
    printf("At or above freezing\n");
}

El primer bloque se ejecuta para una temperatura negativa. Exactamente en cero, la comparación es falsa y se ejecuta el bloque else. Lee el límite directamente a partir de <, <=, > o >=; el mensaje debe coincidir con ese límite.

Sin llaves, solo se controla la siguiente sentencia:

if (temperature > 30)
    printf("Hot\n");
printf("Drink water\n");

El segundo mensaje se ejecuta a cualquier temperatura. La sangría ayuda a las personas, pero no cambia la gramática. Las llaves permiten añadir una segunda sentencia a una rama sin dejarla fuera por accidente.

Ordena una cadena else-if

Una cadena if–else if–else ejecuta la primera rama cuya condición es verdadera y después omite el resto. Para intervalos de puntuación, empieza por el límite inferior más alto:

if (score >= 90) {
    grade = 'A';
} else if (score >= 80) {
    grade = 'B';
} else if (score >= 70) {
    grade = 'C';
} else {
    grade = 'F';
}

Una puntuación de 85 no supera la primera prueba y supera la segunda. Una puntuación de 90 supera la primera. Si comprobaras primero score >= 70, 85 entraría en esa rama y nunca llegaría a la rama B deseada. Las sentencias if separadas se comportan de otra manera: cada condición se comprueba de forma independiente, por lo que pueden ejecutarse varios bloques.

Vigila dos pequeños errores de sintaxis

if (x = 5) asigna 5 y después trata ese valor distinto de cero como verdadero. Usa if (x == 5) para comparar. Con if (x = 0), la asignación sigue ocurriendo, pero la condición es falsa. Las advertencias ayudan a revelar asignaciones accidentales en las condiciones.

En if (x > 0);, el punto y coma es una sentencia controlada vacía. Un bloque colocado después se ejecuta de forma independiente, incluso cuando x es negativo. No añadas un punto y coma entre una condición if y el bloque que debe controlar.

Asocia cada else explícitamente

Un else se asocia al if anterior más cercano que aún no tenga un else. En decisiones anidadas, usa llaves para dejar clara la asociación deseada. La anidación es útil cuando las pruebas fallidas necesitan mensajes distintos: comprueba primero si una medida es válida y después si su valor está en el intervalo deseado. Combinar ambas con && funciona cuando solo se necesita una acción compartida.

Coloca las excepciones antes de las reglas generales

Los años bisiestos proporcionan una decisión ordenada útil. Comprueba primero la divisibilidad por 400, que significa año bisiesto. Después comprueba la divisibilidad por 100, que significa que no es bisiesto. Luego comprueba la divisibilidad por 4; en caso contrario, el año no es bisiesto. Para 2000 se aplica la primera rama. Para 1900 se aplica la segunda. Comprobar solo la divisibilidad por cuatro dejaría fuera la excepción de los años de siglo. El orden importa porque las reglas generales pueden incluir casos con una excepción más específica.

Puntos clave

  • Una cadena else-if ejecuta solo su primera rama verdadera.
  • Las llaves controlan qué sentencias pertenecen a una rama.
  • == compara; = asigna.
  • Un punto y coma después de una condición if crea una rama vacía.

5.2 Seleccionar casos fijos con switch

Una sentencia switch elige un punto de entrada mediante un valor entero. Es útil para un carácter de comando o un número de menú con varias alternativas fijas. La ejecución puede continuar después hacia los casos siguientes, salvo que la detengas.

Selecciona un caso y sal de él
switch (choice) {
    case 1:
        printf("Start\n");
        break;
    case 2:
        printf("Pause\n");
        break;
    default:
        printf("Unknown choice\n");
        break;
}

Con choice igual a 2, el programa empieza en case 2, muestra Pause y sale del switch al llegar a break. Si ningún caso coincide, la ejecución empieza en default. Sin un default, un valor sin coincidencia omite el switch.

La expresión de control debe tener un tipo entero, incluido char o una enumeración. Cada caso debe ser una expresión constante entera distinta, como 3, 'q' o un nombre adecuado definido con #define. En C17, una variable const int no es una expresión constante entera válida para una etiqueta case. Los valores de coma flotante y las cadenas no pueden controlar un switch.

Comprende la continuación al siguiente caso

Las etiquetas case marcan puntos de entrada, no bloques separados que terminan automáticamente. Si eliminas el primer break anterior, una opción de 1 muestra tanto Start como Pause. El programa no comprueba si choice es 2 antes de continuar por ese caso. La ejecución se detiene solo ante una transferencia de control o al final del switch.

Varias etiquetas pueden compartir una acción:

case 'y':
case 'Y':
    printf("Yes\n");
    break;

Ambos caracteres conducen a la misma acción. Cuando un caso realiza una tarea y continúa deliberadamente en otro, explica la intención con un comentario. C23 también ofrece [[fallthrough]];. Las advertencias pueden ayudar a detectar un break ausente, pero agrupar etiquetas deliberadamente no requiere repetir la acción.

Da un bloque a las variables locales de un caso

En C17, una declaración no puede seguir directamente a una etiqueta case. Usa llaves alrededor de las sentencias de ese caso:

case 3: {
    int delay = 5;
    printf("%d\n", delay);
    break;
}

El bloque también mantiene delay como variable local de ese caso. C23 flexibiliza la restricción sobre las etiquetas anteriores a declaraciones, pero las llaves siguen dejando claro el ámbito.

Elige entre switch e if

Usa switch para alternativas enteras exactas. Usa if–else if para desigualdades, condiciones que impliquen varias variables o medidas de coma flotante. Los intervalos de puntuación de igual ancho pueden convertirse a veces en casos exactos mediante división entera. Con puntuaciones de 0 a 100, score / 10 da 8 para 85; los casos 9 y 10 pueden compartir el intervalo superior. Valida ese dominio para que las puntuaciones negativas o excesivas no reciban silenciosamente un intervalo.

Un switch dentro de un bucle

Un break sale del switch o bucle que lo contiene más cercano. Dentro de un switch que a su vez está dentro de un bucle, break termina solo el switch. Para terminar el bucle de menú, establece un indicador que compruebe su condición o vuelve desde main cuando el programa deba terminar. Un continue en ese switch se aplica al bucle que lo contiene y lo lleva a su siguiente iteración. Sigue el destino de cada transferencia antes de decidir si el menú se detiene.

Puntos clave

  • Un switch selecciona una etiqueta de entrada mediante un valor entero.
  • La ejecución continúa por los casos hasta break, otra transferencia de control o el final.
  • Los valores de los casos deben ser expresiones constantes enteras distintas.
  • Usa un bloque para una declaración local de un caso en C17.
  • El break de un switch no termina también el bucle que lo contiene.

5.3 Repetir hasta que los datos indiquen que hay que parar

Un bucle while comprueba una condición antes de cada pasada. Un bucle do–while la comprueba después del cuerpo, por lo que se ejecuta al menos una vez. Elige la forma que coloque la prueba donde el programa la necesita.

Sigue un cálculo controlado por una condición
int n = 4096, sum = 0;
while (n > 0) {
    sum += n % 10;
    n /= 10;
}

n % 10 extrae una cifra y n /= 10 la elimina. Los valores después de las pasadas son:

Pasadasumn
16409
21540
3154
4190

La siguiente condición es falsa, por lo que el bucle termina con una suma de cifras de 19. Si n empieza en cero, el cuerpo se ejecuta cero veces y la suma sigue siendo cero. Las entradas negativas también omiten este bucle, por lo que un programa que las acepte necesita una regla definida.

Un bucle de conteo establece un contador, lo comprueba y lo actualiza. Otros bucles progresan leyendo datos o llegando a break. Al dividir repetidamente 100 por la mitad mediante división entera, los valores son 50, 25, 12, 6, 3 y 1: se necesitan seis pasadas para llegar a uno.

Deja que la entrada controle la repetición
int x, sum = 0, count = 0;
while (scanf("%d", &x) == 1 && x != 0) {
    sum += x;
    count++;
}
if (count != 0) {
    printf("%.2f\n", (double)sum / count);
}

Para la entrada «5 8 3 0», el valor centinela cero termina el bucle y no se suma. La suma es 16, el recuento es 3 y la media mostrada es 5.33. La comprobación del número devuelto protege el uso de x; la prueba del recuento protege la división si el primer valor es cero. Este cálculo también requiere que las entradas y los totales quepan en int.

Sin la comprobación del centinela, while (scanf("%d", &x) == 1) se detiene ante una conversión numérica fallida o el fin de la entrada. En una terminal, Ctrl+D suele indicar el fin de la entrada en Linux y macOS; en Windows usa Ctrl+Z seguido de Intro.

Ejecuta una vez antes de comprobar
int height;
do {
    printf("Height (1-8): ");
    fflush(stdout);
    if (scanf("%d", &height) != 1) {
        return 1;
    }
} while (height < 1 || height > 8);

El mensaje de solicitud y la lectura ocurren antes de la prueba de rango. Los valores cero y nueve hacen que se repita; los valores uno y ocho se aceptan. El punto y coma después de while (...) es obligatorio en esta forma. La comprobación de la conversión termina este ejemplo ante una entrada no numérica; la prueba de rango por sí sola no puede manejar una conversión fallida.

Progresa en cada recorrido que continúe

break sale inmediatamente del bucle más interno. continue omite el resto del cuerpo y vuelve a la condición en un bucle while. Si hay una actualización después de un continue, también se omite. El bucle puede entonces seguir comprobando el mismo valor indefinidamente.

Antes de ejecutar un bucle, identifica qué cambia y por qué se alcanzará finalmente la condición de parada. Un while (1) deliberado puede detenerse mediante break; olvidar actualizar un contador es una situación distinta. Ctrl+C detiene un programa bloqueado en una terminal. Prueba la entrada más pequeña y el valor del límite de parada para detectar errores de cero pasadas o de una pasada de más.

Puntos clave

  • while comprueba primero; do–while comprueba después de al menos una pasada.
  • Un valor centinela detiene la entrada sin formar parte de los datos.
  • Inicializa los acumuladores fuera del bucle.
  • Un recorrido que continúa debe progresar hacia la condición de parada.
  • Un continue de while puede omitir una actualización posterior.
  • Comprueba si hay cero datos antes de calcular una media.

5.4 Contar pasadas y combinar bucles

Un bucle for mantiene un patrón de conteo en una sola cabecera. Los bucles anidados repiten una tarea interior por cada pasada exterior. Ambos son más fáciles de comprobar cuando enumeras los valores reales del contador e identificas cada salida.

Sigue la cabecera en orden
for (int i = 0; i < 3; i++) {
    printf("%d ", i);
}

La inicialización se ejecuta una vez. Después se comprueba la condición, el cuerpo se ejecuta si es verdadera y la actualización se ejecuta antes de la siguiente comprobación. La salida es 0 1 2 . La variable declarada en la cabecera es local al bucle; declárala antes del bucle si necesitas su valor final después.

Para n no negativo, i = 0; i < n; i++ se ejecuta n veces y visita desde cero hasta n − 1. Con i <= n, se ejecuta una vez más. Para un paso positivo s desde a hasta un b incluido, el número de pasadas es (b - a) / s + 1 mediante división entera, siempre que la aritmética y las actualizaciones del contador quepan en el tipo. De 2 a 20 en pasos de 3, los valores son 2, 5, 8, 11, 14, 17, 20: siete pasadas.

Mantén el acumulador fuera
int total = 0;
for (int i = 1; i <= 100; i++) {
    total += i;
}

El total final es 5050. La inicialización debe ocurrir una vez antes del bucle. Mover int total = 0; al cuerpo crea un total nuevo en cada pasada y pierde el trabajo anterior.

Elige contadores que puedan llegar al final

Un punto y coma inmediatamente después de la cabecera de for deja el cuerpo vacío. Un bloque posterior se ejecuta entonces una vez después del bucle. Usa llaves directamente junto a la cabecera para que el cuerpo sea visible.

Una cuenta atrás sin signo con i >= 0 no puede detenerse de esa manera: los valores sin signo siempre son no negativos y al decrementar cero se reducen modularmente al máximo. Un contador de coma flotante que suma 0.1 repetidamente puede saltarse el valor exacto 1.0, por lo que x != 1.0 no es fiable como regla de parada. Cuenta con un entero y calcula una medida como x = i / 10.0 dentro del cuerpo.

Construye filas con bucles anidados

En cada pasada exterior, el bucle interior empieza y ejecuta su propia secuencia. Tres pasadas exteriores y cuatro pasadas interiores fijas ejecutan el cuerpo interior doce veces. Cuando el límite interior depende de la fila, cuenta cada fila por separado.

int height = 3;
for (int row = 1; row <= height; row++) {
    for (int s = 0; s < height - row; s++) printf(" ");
    for (int h = 0; h < row; h++) printf("#");
    printf("\n");
}

Las filas contienen dos espacios y una almohadilla, un espacio y dos almohadillas, y después tres almohadillas. El salto de línea va después de los bucles interiores, por lo que cada pasada exterior crea una fila.

Omite trabajo o termina antes

continue en un bucle for va a la actualización y después a la siguiente comprobación. Un bucle que suma solo los valores impares de 1 a 9 puede omitir los valores pares y terminar con 25. break sale inmediatamente del bucle más interno. En bucles anidados, un break interior no detiene el bucle exterior.

Un bucle for suele ser la elección más clara para un rango conocido de un contador; while es adecuado para una repetición controlada por datos y do–while para tareas que deben realizarse antes de la comprobación. Reescribir un bucle for como un bucle while requiere especial cuidado con continue, porque la actualización debe seguir ocurriendo en ese recorrido.

Puntos clave

  • Un bucle for ejecuta la inicialización una vez y la actualización después de cada pasada que continúa.
  • Comprueba los valores visitados para detectar errores de una unidad.
  • Usa contadores enteros con suficiente rango.
  • Los bucles anidados reinician la secuencia interior en cada pasada exterior.
  • Un break interior sale solo del bucle interior.
  • Un continue de for sigue ejecutando la actualización de la cabecera.
  • Mantén la inicialización de un total acumulado fuera del cuerpo que se repite.
6

Reconocer y prevenir el desbordamiento de enteros

Los rangos finitos de los enteros afectan a los cálculos incluso cuando la respuesta final parece lo bastante pequeña. Distingue la reducción modular de enteros sin signo, el desbordamiento con signo y los valores perdidos durante una conversión, y después examina los tipos de las operaciones intermedias. La prevención comienza antes de un cálculo de riesgo: convierte un operando a un tipo de mayor rango, demuestra los límites o usa una operación con comprobación de desbordamiento. Los rangos de los contadores y las pruebas de valores límite ayudan a encontrar fallos que las entradas pequeñas habituales y las pruebas breves pueden pasar por alto.

6.1 Qué ocurre cuando un entero no cabe en su tipo

El rango de un entero es finito. Una respuesta matemática puede ser válida y, aun así, demasiado grande para el tipo de C utilizado para calcularla. Las consecuencias dependen de si se trata de una operación sin signo, con signo o de una conversión.

La aritmética sin signo se reduce modularmente

Un resultado sin signo de N bits se reduce módulo 2ᴺ. Con un unsigned int de 32 bits, UINT_MAX + 1u da cero, 0u - 1u da 4 294 967 295 y 3u - 5u da 4 294 967 294. El resultado está definido, pero puede ser incorrecto para la tarea: restar cinco artículos de una existencia de tres no debería producir miles de millones de artículos.

Para uint8_t b = 255;, b = b + 1; almacena cero. En un sistema habitual con int de 32 bits, la suma ocurre primero como int y produce 256; convertir ese valor de nuevo a un tipo sin signo de 8 bits produce cero. Un almacenamiento pequeño sin signo no significa que cada operación intermedia tenga ese tipo pequeño.

El desbordamiento de la aritmética con signo es indefinido

Con un int de 32 bits, sumar uno a INT_MAX requiere 2 147 483 648. C no define el resultado aritmético con signo. El hardware puede parecer que se reduce modularmente a INT_MIN, pero el compilador puede optimizar suponiendo que el desbordamiento con signo nunca ocurre. Una comprobación como if (x + 1 < x) puede eliminarse para enteros con signo, porque toda suma definida de uno a un entero con signo da un valor mayor.

No calcules primero para después examinar el daño. Una operación indefinida ya ha ocurrido antes de que se ejecute una prueba posterior.

Un destino de mayor rango puede llegar demasiado tarde
int a = 50000, b = 50000;
long long product = a * b;

La multiplicación se realiza como int, por lo que se desborda en un sistema con int de 32 bits. El ancho del destino no puede repararlo. (long long)a * b cambia el tipo de la operación antes de la multiplicación y produce 2 500 000 000.

Los factoriales crecen rápidamente: 12! es 479 001 600 y cabe en un entero con signo de 32 bits; 13! es 6 227 020 800 y no cabe. Con aritmética sin signo de 32 bits, 13! se reduce modularmente a 1 932 053 504. Un long long con signo de 64 bits contiene 20! = 2 432 902 008 176 640 000, pero no 21!.

La conversión tiene reglas distintas

Convertir un entero a un tipo sin signo utiliza reducción modular. En C17, convertir un entero fuera de rango a un tipo con signo da un resultado o una señal definidos por la implementación. Convertir un valor de coma flotante cuya parte entera truncada no cabe en el destino entero tiene comportamiento indefinido. Leer un número fuera de rango mediante una conversión numérica de scanf también es indefinido.

Los fallos de rango pueden tener grandes efectos

El primer vuelo del Ariane 5, el 4 de junio de 1996, se perdió después de que un valor de coma flotante no pudiera caber en un entero con signo de 16 bits en su software de referencia inercial. Ese programa estaba escrito en Ada; la lección sobre comprobar los rangos de conversión también se aplica a C.

Un contador de segundos con signo de 32 bits que empieza en la época Unix alcanza 2 147 483 647 a las 03:14:07 UTC del 19 de enero de 2038. Los sistemas que usan esa representación del tiempo necesitan una de mayor rango. En 2015, la FAA también abordó un problema en el software de control de generadores del Boeing 787 que podía desbordarse después de 248 días de alimentación continua. Estos fallos muestran por qué tanto un tiempo de ejecución prolongado como los valores intermedios deben incluirse en una estimación de rango.

Los límites de la coma flotante son distintos: los sistemas IEEE habituales pueden producir infinito y un float puede perder precisión para números enteros antes de alcanzar su máximo. La reducción modular de enteros sin signo produce un valor numérico normal; el desbordamiento con signo no ofrece ningún resultado fiable.

Puntos clave

  • La aritmética sin signo usa reducción modular.
  • El desbordamiento de la aritmética con signo tiene comportamiento indefinido.
  • Un destino de mayor rango no puede corregir una operación intermedia de menor rango.
  • La conversión de enteros, la conversión de coma flotante y el desbordamiento aritmético tienen reglas distintas.
  • Estima los valores durante todo el tiempo de funcionamiento.

6.2 Prevenir el desbordamiento antes de que ocurra

Empieza por el mayor valor que puede producir cada cálculo. Incluye los productos intermedios y las actualizaciones de los contadores, no solo la respuesta final. Después elige un tipo o una comprobación previa que mantenga cada operación dentro de rango.

Amplía el rango del cálculo desde el principio

Un entero con signo de 32 bits contiene aproximadamente 2,1 mil millones, mientras que un long long con signo de 64 bits contiene hasta 9 223 372 036 854 775 807. Un contador de milisegundos alcanza el límite de un entero con signo de 32 bits en aproximadamente 24,9 días. Usa un tipo de 64 bits cuando una duración pueda ser mayor.

Escribe (long long)a * b para ampliar el rango de un producto entero antes de evaluarlo. Las constantes también importan: 86400LL * days inicia la multiplicación en long long. (long long)(a * b) convierte solo después del producto de menor rango, por lo que no puede evitar su desbordamiento. Los tipos de mayor rango siguen teniendo límites; compáralos con los mayores valores posibles.

Comprueba la suma mediante una resta

Para int a e int b, incluye <limits.h> y comprueba:

if ((b > 0 && a > INT_MAX - b) ||
    (b < 0 && a < INT_MIN - b)) {
    printf("Sum out of range\n");
} else {
    int sum = a + b;
    printf("%d\n", sum);
}

Cada resta está protegida por el signo de b, lo que mantiene la propia comprobación dentro del rango representable. Para 2 000 000 000 más 200 000 000, INT_MAX - b es 1 947 483 647, menor que a, por lo que se rechaza la suma. El caso de igualdad cabe: cuando a es igual a INT_MAX - b, la suma es exactamente INT_MAX.

Comprueba una multiplicación positiva mediante división

Para operandos int positivos, a > INT_MAX / b significa que el producto superaría el máximo. La condición evita calcular el producto y evita un divisor cero porque b es positivo. En un cálculo de factoriales, la prueba antes de multiplicar 12! por 13 detecta que 479 001 600 es mayor que 165 191 049. Detente antes de esa multiplicación.

Para existencias sin signo, comprueba stock >= requested antes de restar. Cambiar solo a un tipo sin signo no evita un resultado sin sentido por reducción modular.

Reescribe expresiones intermedias de riesgo

El punto medio (low + high) / 2 puede desbordarse incluso cuando su respuesta cabe en el tipo. Cuando 0 <= low <= high y ambos son int, usa low + (high - low) / 2. La diferencia es no negativa y está dentro de rango, y el resultado nunca supera high. Estas condiciones importan; la fórmula no es una demostración general para extremos con signo arbitrarios.

Los contadores de bucle también necesitan rango. Un short de 16 bits no puede llegar a 40 000, por lo que un bucle que lo use para contar hasta ahí no puede terminar como se pretende. Usa un tipo de contador que pueda representar el límite y cualquier actualización final.

Usa aritmética con comprobación de desbordamiento y pruebas

C23 proporciona <stdckdint.h> con ckd_add, ckd_sub y ckd_mul. Por ejemplo, ckd_add(&r, a, b) almacena un resultado y devuelve verdadero cuando la respuesta matemática no cabe en r. GCC y clang también proporcionan __builtin_add_overflow, __builtin_sub_overflow y __builtin_mul_overflow como extensiones del compilador.

Las advertencias detectan algunos desbordamientos de constantes y errores al mezclar valores con y sin signo. En GCC o clang, -fsanitize=undefined -g puede informar del desbordamiento con signo alcanzado durante una prueba; -ftrapv solicita interrupciones ante desbordamientos de sumas, restas y multiplicaciones con signo. Prueba cero, uno, menos uno, la mayor entrada esperada y valores en los límites y justo por debajo de ellos. Una herramienta de prueba comprueba los recorridos que ejecutas, así que mantén el razonamiento sobre rangos en el código.

Puntos clave

  • Convierte un operando a un tipo de mayor rango antes de una operación de riesgo.
  • Usa comprobaciones previas que tengan en cuenta el signo para la suma con signo.
  • La comprobación previa de multiplicación mediante división requiere operandos positivos.
  • La resta sin signo sigue necesitando una comprobación de rango.
  • La fórmula más segura del punto medio requiere extremos no negativos y ordenados.
  • La aritmética con comprobación de desbordamiento indica si el resultado cabe en su destino.
  • Las pruebas de valores límite y los sanitizadores complementan el razonamiento sobre rangos.

Ejemplos de Primeros programas en C

6 Lecciones Interactivas

1

Construir un saludo y diagnosticar una edición fallida

9 pasos
Sigue un primer programa desde el código fuente hasta su ejecución y después usa mensajes de diagnóstico y una traza para corregir problemas.
  1. 1
    IntroducciónSigue la construcción

    Un programa en C guardado es texto. Para ejecutarlo se necesita un ejecutable producido a partir de ese texto. Ten claras las funciones de los dos archivos: una edición cambia el código fuente, mientras que una construcción correcta sustituye el ejecutable.

    Construirás un saludo breve, examinarás qué hace cada etapa y diagnosticarás una edición que falla. El hábito central es comprobar el resultado de la construcción antes de usar una ejecución como prueba del nuevo código fuente.

  2. 2
    ExplicaciónLee la estructura del programa

    El programa empieza en main:

    #include <stdio.h>
    int main(void)
    {
        printf("Ready\n");
        return 0;
    }
    

    La cabecera proporciona la declaración de printf. El cuerpo muestra una línea y después devuelve un valor que indica que la ejecución fue correcta. void significa aquí que main no tiene parámetros; int indica el tipo de su valor de retorno. Las llaves delimitan el cuerpo y las sentencias simples terminan con punto y coma.

  3. 3
    EjemploConstrúyelo y ejecútalo

    Guarda el código fuente como ready.c. En su carpeta, introduce:

    gcc -std=c17 -Wall -Wextra ready.c -o ready
    ./ready
    

    La construcción selecciona C17 y advertencias útiles, y después da al ejecutable el nombre ready. La ejecución muestra Ready seguido de un salto de línea. En Linux o macOS, ./ selecciona la carpeta actual. Puedes usar clang en la misma posición del comando; los ejecutables de Windows usan el sufijo .exe.

  4. 4
    ConexiónObserva dónde encaja el código objeto

    El preprocesamiento procesa la directiva de inclusión. Después, la compilación produce lenguaje ensamblador y el ensamblado produce código objeto. El enlace resuelve referencias y completa el ejecutable; una biblioteca compartida puede proporcionar código de biblioteca en tiempo de ejecución.

    Las opciones -E, -S y -c detienen el proceso en etapas sucesivamente posteriores. En particular, -c se detiene en un archivo objeto: aún no ha completado la etapa de enlace necesaria para el ejecutable.

  5. 5
    DesafíoPredice la siguiente ejecución

    Construyes el saludo y después guardas una edición que cambia «Ready» por «Begin». Ejecutas el archivo ejecutable existente sin compilar. ¿Qué texto aparece y cómo lo cambias?

    ---reveal---

    Sigue mostrando Ready. El ejecutable refleja la construcción correcta anterior. Compila la edición guardada y ejecuta el nuevo ejecutable para mostrar Begin. Guardar solo el código fuente no actualiza el código máquina.

  6. 6
    EjemploCorrige el primer error

    Una edición deja esta línea sin punto y coma:

    printf("Begin\n")
    return 0;
    

    El compilador puede señalar la primera línea o return, donde detecta que falta el final de la sentencia de expresión. Añade el punto y coma después de la llamada y vuelve a construir. Empieza por esta primera causa aunque los mensajes posteriores mencionen otras líneas; un solo error de sintaxis puede alterar la forma en que el compilador lee el código siguiente.

  7. 7
    ConsejoTrata las advertencias como indicios útiles

    Una advertencia suele permitir que continúe la construcción, pero puede señalar un error grave. printf("%d\n", 2.5); pasa un double donde el formato requiere int, lo que tiene comportamiento indefinido.

    Corrige la discrepancia de tipos en lugar de aceptar lo que muestre una ejecución de prueba. Las advertencias de -Wall -Wextra ayudan a revelar esos errores; -Werror hace que detengan la construcción. Para un cálculo incorrecto, muestra los valores intermedios y compáralos con una pequeña traza hecha a mano.

  8. 8
    DesafíoDistingue una construcción fallida de una ejecución incorrecta

    Después de construir correctamente el saludo, una nueva edición no compila. Al ejecutar ./ready sigue apareciendo un saludo. ¿Demuestra eso que la edición es válida y qué deberías revisar a continuación?

    ---reveal---

    No. El ejecutable anterior puede permanecer después de una construcción fallida. Revisa el primer error del compilador, incluida la línea anterior del código fuente, corrígelo y vuelve a construir correctamente. Después ejecuta ese nuevo archivo y compara su salida con tu predicción. El saludo existente no dice nada sobre la edición fallida.

  9. 9
    RepasoUsa un ciclo de corrección claro

    Mantén cada prueba vinculada al código fuente que realmente se construyó. Guarda la edición, compila, lee el primer mensaje de diagnóstico, corrige su causa y vuelve a construir antes de ejecutar.

    Si el programa se construye pero calcula un valor incorrecto, usa una pequeña traza y entradas límite. Si una operación tiene comportamiento indefinido, cambia el código para que no pueda ocurrir; una salida de apariencia normal no es un resultado fiable para esa operación.

2

Elegir almacenamiento para recuentos y medidas

13 pasos
Sigue la asignación y el ámbito, y después relaciona el rango de los enteros y la precisión de la coma flotante con elecciones prácticas de tipos.

Guarda esta unidad para seguir cada Lección Interactiva paso a paso.

  1. 1
    IntroducciónPregúntate qué necesita un valor
  2. 2
    EjemploUn valor copiado sigue siendo una copia
  3. 3
    ExplicaciónMantén los nombres dentro de sus bloques
  4. 4
    DesafíoSigue un nombre oculto
  5. 5
    ExplicaciónCompara los rangos antes de elegir
  6. 6
    EjemploLee un patrón de bits y un literal
  7. 7
    DesafíoElige un rango portable
  8. 8
    ExplicaciónObserva por qué se desvían las fracciones decimales
  9. 9
    EjemploCompara con un error permitido
  10. 10
    DesafíoDistingue la conversión del redondeo
  11. 11
    Idea claveLa precisión puede fallar antes que el rango
  12. 12
    DesafíoAjusta el tipo a la tarea
  13. 13
    RepasoComprueba el almacenamiento de dos maneras
3

Leer medidas y mostrar resultados claros

11 pasos
Ajusta los formatos a los tipos, examina la entrada parcial y sigue un cálculo de medidas con comprobaciones.

Guarda esta unidad para seguir cada Lección Interactiva paso a paso.

  1. 1
    IntroducciónConecta valores con tipo y texto
  2. 2
    ExplicaciónLee el formato de salida
  3. 3
    EjemploConstruye una presentación alineada
  4. 4
    DesafíoPredice un entero con relleno
  5. 5
    ExplicaciónLee en los destinos adecuados
  6. 6
    EjemploDetecta una conversión parcial
  7. 7
    ConsejoNo leas por accidente un salto de línea pendiente
  8. 8
    DesafíoSigue lo que conserva una lectura fallida
  9. 9
    EjemploComprueba el dominio del cálculo
  10. 10
    DesafíoCombina la lectura y la presentación
  11. 11
    RepasoUsa dos comprobaciones en la entrada
4

Leer una expresión desde los tipos hasta los efectos

17 pasos
Examina la división, las conversiones, las condiciones lógicas de protección, las máscaras, la agrupación y las actualizaciones en un orden definido.

Guarda esta unidad para seguir cada Lección Interactiva paso a paso.

  1. 1
    IntroducciónMarca tanto los valores como los tipos
  2. 2
    EjemploDivide una cantidad en partes enteras
  3. 3
    ExplicaciónDistingue los dos incrementos
  4. 4
    DesafíoSigue una actualización compuesta
  5. 5
    EjemploColoca el operando de coma flotante a tiempo
  6. 6
    Idea claveLos valores pequeños sin signo pueden calcularse como int con signo
  7. 7
    DesafíoSigue un valor con signo en una operación sin signo
  8. 8
    ExplicaciónUne comparaciones completas
  9. 9
    EjemploHaz que la protección se ejecute primero
  10. 10
    EjemploCambia un indicador sin alterar los demás
  11. 11
    DesafíoCompara los valores de verdad con los bits compartidos
  12. 12
    ExplicaciónAgrupa antes de seguir la ejecución
  13. 13
    ConsejoPon la máscara dentro de la comparación
  14. 14
    ConexiónElige un valor sin evaluar ambos
  15. 15
    Desafío¿Pueden los paréntesis corregir dos actualizaciones?
  16. 16
    DesafíoCombina la agrupación con una prueba de rango
  17. 17
    RepasoExamina una expresión en tres pasadas
5

Seguir decisiones y construir un total acumulado

18 pasos
Examina ramas, un menú con switch, entrada con valor centinela y conteo anidado con salidas deliberadas.

Guarda esta unidad para seguir cada Lección Interactiva paso a paso.

  1. 1
    IntroducciónSigue el recorrido que realmente se toma
  2. 2
    ExplicaciónUsa la primera rama verdadera
  3. 3
    ConsejoTanto las llaves como la igualdad importan
  4. 4
    DesafíoEncuentra el primer intervalo coincidente
  5. 5
    ExplicaciónSelecciona una entrada de comando
  6. 6
    EjemploComparte una acción de forma deliberada
  7. 7
    DesafíoEncuentra el destino de break
  8. 8
    ExplicaciónLee antes de usar la entrada
  9. 9
    EjemploSigue el cálculo con valor centinela
  10. 10
    DesafíoManeja una parada inmediata
  11. 11
    ConexiónPregunta una vez con do-while
  12. 12
    ExplicaciónCuenta exactamente las visitas previstas
  13. 13
    EjemploConserva un total entre pasadas
  14. 14
    ConsejoEvita contadores que no alcancen la salida
  15. 15
    EjemploCuenta las filas por separado
  16. 16
    DesafíoSigue una salida anticipada en un bucle anidado
  17. 17
    DesafíoCombina un indicador y un bucle de menú
  18. 18
    RepasoComprueba cada recorrido hacia la siguiente pasada
6

Asegurar un producto antes de calcularlo

12 pasos
Distingue las reglas de desbordamiento, convierte a un tipo de mayor rango en el momento adecuado y comprueba los casos límite sin provocar el fallo que se está comprobando.

Guarda esta unidad para seguir cada Lección Interactiva paso a paso.

  1. 1
    IntroducciónExamina la aritmética antes del almacenamiento
  2. 2
    EjemploEncuentra la operación de menor rango
  3. 3
    ExplicaciónUn tipo sin signo tiene un resultado definido, pero quizá incorrecto
  4. 4
    Desafío¿Corrige el producto una conversión explícita tardía?
  5. 5
    EjemploAmplía el rango de la propia operación
  6. 6
    ExplicaciónComprueba una suma sin sumar
  7. 7
    EjemploComprueba un producto positivo mediante división
  8. 8
    DesafíoComprueba el límite correctamente
  9. 9
    Idea claveUna respuesta segura puede ocultar una suma insegura
  10. 10
    ConsejoUsa herramientas y prueba los límites
  11. 11
    DesafíoComprueba un diseño completo de rangos
  12. 12
    RepasoHaz la comprobación antes de la operación

Ejercicios de Primeros programas en C

50 Preguntas de Práctica en 5 sets

Set 1: Construir programas y seguir los valores almacenados10 Preguntas de Práctica

Pregunta 1 de 10

Un programa se construyó correctamente y después se editó y guardó su saludo. Al ejecutar el archivo ejecutable existente sigue apareciendo el saludo anterior. ¿Qué acción hace que la edición surta efecto?
  1. Ejecutar de nuevo el mismo archivo ejecutable.
  2. Cambiar el nombre de salida del ejecutable.
  3. Volver a compilar el código fuente guardado.
  4. Añadir otro salto de línea al saludo.
Mostrar respuestaOcultar respuesta
Respuesta: C
El ejecutable contiene el resultado de la construcción anterior. Guardar el texto del código fuente no vuelve a construir ese archivo, por lo que debes compilar el código editado y después ejecutar el nuevo archivo. Volver a ejecutar el archivo anterior usa el mismo código máquina y no puede incorporar el saludo modificado.

Pregunta 2 de 10

Un compilador informa de que falta un punto y coma y después muestra varios errores en líneas posteriores. ¿Qué deberías corregir primero?
  1. El primer problema de sintaxis indicado.
  2. El último problema de sintaxis indicado.
  3. Cada línea posterior antes de reconstruir.
  4. La entrada del ejecutable en tiempo de ejecución.
Mostrar respuestaOcultar respuesta
Respuesta: A
Empieza por el primer error y revisa la línea que indica y la anterior. Un punto y coma ausente puede confundir la interpretación que hace el compilador de las sentencias posteriores. Corregir primero todas las líneas posteriores puede cambiar código correcto que solo se señaló debido al error anterior.

Pregunta 3 de 10

¿Qué valores muestra este código?

int units = 6;
int total = units * 4;
units = 9;
printf("%d %d\n", units, total);
  1. 9 36
  2. 6 24
  3. 6 36
  4. 9 24
Mostrar respuestaOcultar respuesta
Respuesta: D
La declaración de total calcula seis por cuatro una vez y almacena 24. La asignación posterior cambia units a nueve, pero no vuelve a calcular total, por lo que la salida es 9 y 24. Esperar 36 implica tratar la asignación anterior como una fórmula activa que vincula las dos variables.

Pregunta 4 de 10

Un valor debe contener resultados negativos y recuentos positivos de hasta 6 000 000 000 tanto en sistemas habituales de 64 bits de Linux como de Windows. ¿Qué tipo entero estándar proporciona el rango necesario?
  1. int
  2. long long
  3. unsigned int
  4. long
Mostrar respuestaOcultar respuesta
Respuesta: B
Un long long estándar tiene al menos 64 bits y puede representar estos valores positivos y negativos. Un long típico de Windows tiene solo 32 bits, aunque Windows sea de 64 bits. Unsigned int también tiene un rango insuficiente en estos sistemas y no puede representar valores negativos.

Pregunta 5 de 10

¿Qué valor decimal representa el literal entero 014 de C17?
  1. 14
  2. 10
  3. 12
  4. 16
Mostrar respuestaOcultar respuesta
Respuesta: C
El cero inicial hace que 014 sea un literal octal. Sus cifras representan un grupo de ocho más cuatro, lo que da doce. Leerlo como catorce decimal ignora el prefijo de base; un literal decimal para catorce se escribe 14 sin cero inicial.

Pregunta 6 de 10

Un double calculado difiere muy poco de un double objetivo después de varias operaciones decimales. ¿Qué comparación es adecuada para una medida que permite un pequeño error absoluto?
  1. Comparar la diferencia absoluta con el error permitido.
  2. Comparar los valores mostrados con la máxima precisión.
  3. Convertir ambos valores a int antes de comprobar la igualdad.
  4. Exigir igualdad exacta después de asignar ambos a double.
Mostrar respuestaOcultar respuesta
Respuesta: A
Una prueba de diferencia absoluta como fabs(actual - target) < tolerance comprueba el error que permite la medida. La igualdad exacta puede fallar porque los cálculos decimales introducen diferencias de redondeo binario. Convertir a int descarta las fracciones y puede ocultar una diferencia mucho mayor que el error permitido.

Pregunta 7 de 10

¿Qué muestra printf("|%04d|", 23); entre las barras verticales?
  1. | 23|
  2. |2300|
  3. |23 |
  4. |0023|
Mostrar respuestaOcultar respuesta
Respuesta: D
El ancho solicita al menos cuatro caracteres y el indicador de cero proporciona ceros iniciales, por lo que 23 se convierte en 0023. La alineación normal a la derecha usaría dos espacios iniciales, pero ese es el comportamiento de %4d sin el indicador de cero. El ancho no añade ceros al final del valor.

Pregunta 8 de 10

Dado int a = 2, b = 9;, una llamada a scanf("%d:%d", &a, &b) recibe «17 42». ¿Qué valor devuelve y qué valores quedan almacenados?
  1. Devuelve 2; a = 17, b = 42.
  2. Devuelve 1; a = 17, b = 9.
  3. Devuelve 0; a = 2, b = 9.
  4. Devuelve 1; a = 17, b = 0.
Mostrar respuestaOcultar respuesta
Respuesta: B
El primer %d almacena 17. Después la entrada tiene un espacio donde el formato exige dos puntos literales, por lo que la coincidencia se detiene y b conserva su valor anterior, nueve. El número devuelto es uno; los caracteres en blanco entre números no pueden sustituir la puntuación exigida por el formato.

Pregunta 9 de 10

En un sistema con unsigned char de 8 bits e int de 32 bits, ¿qué muestra esto?

unsigned char a = 230, b = 40;
int sum = a + b;
unsigned char small = sum;
printf("%d %u\n", sum, (unsigned)small);
  1. 14 14
  2. 270 270
  3. 14 270
  4. 270 14
Mostrar respuestaOcultar respuesta
Respuesta: D
Ambos valores de carácter se promocionan a int antes de la suma, por lo que sum recibe 270 sin reducción modular en 255. La conversión a small reduce después 270 módulo 256, dando 14. Predecir 14 para ambos valores aplica el rango del almacenamiento pequeño a la suma promocionada, donde no corresponde.

Pregunta 10 de 10

El comando gcc -std=c17 -c sensor.c se ejecuta correctamente. ¿Qué produjo y qué etapa de construcción sigue siendo necesaria antes de una ejecución normal?
  1. Un ejecutable; aún se necesita el preprocesamiento.
  2. Texto ensamblador; aún se necesita el ensamblado.
  3. Un archivo objeto; aún se necesita el enlace.
  4. C preprocesado; aún se necesita la compilación.
Mostrar respuestaOcultar respuesta
Respuesta: C
La opción -c compila y ensambla el código fuente en código objeto, normalmente sensor.o, y después se detiene antes del enlace. El enlace sigue siendo necesario para crear el ejecutable y resolver las referencias externas. El texto ensamblador es la salida anterior que selecciona -S, no -c.
Set 2: Leer entradas y calcular con tipos de C10 Preguntas de Práctica

Guarda esta unidad para ver todas las respuestas y practicar cada set.

Pregunta 1 de 10

Se declara una variable double distance_m;. ¿Qué conversión y argumento de scanf coinciden con su tipo?
  1. scanf("%f", &distance_m)
  2. scanf("%lf", &distance_m)
  3. scanf("%lf", distance_m)
  4. scanf("%d", &distance_m)
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 2 de 10

¿Cuánto valen -23 / 6 y -23 % 6 en C17?
  1. −4 y 1
  2. −3 y 5
  3. −4 y −5
  4. −3 y −5
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 3 de 10

Para int distance = 19, time = 4;, ¿qué expresión calcula una velocidad double que conserva la parte fraccionaria?
  1. (double)distance / time
  2. (double)(distance / time)
  3. distance / time
  4. (int)(distance / (double)time)
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 4 de 10

Un int leído con %d acepta la entrada «46kg». ¿Qué valor se almacena y qué entrada queda sin leer inmediatamente después de la conversión?
  1. 46; no queda nada.
  2. 0; queda «46kg».
  3. 46; queda «kg».
  4. Ningún valor; queda «kg».
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 5 de 10

Para los valores int no negativos total y count, ¿qué condición comprueba que la media entera sea mayor que 5 sin dividir por cero?
  1. total / count > 5 && count != 0
  2. count != 0 && total / count > 5
  3. count != 0 & total / count > 5
  4. count == 0 || total / count > 5
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 6 de 10

¿Qué valor produce 2 + 5 * 3 % 7?
  1. 1
  2. 0
  3. 7
  4. 3
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 7 de 10

¿Qué texto muestra printf("|%6.1f|", 8.375); con el redondeo habitual a la décima más cercana?
  1. | 8.375|
  2. |8.4 |
  3. | 8.4|
  4. |0008.4|
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 8 de 10

¿Qué valores se muestran?

int n = 8;
int a = n++;
int b = ++n;
a *= b % 4;
printf("%d %d %d\n", a, b, n);
  1. 16 10 10
  2. 18 10 10
  3. 16 9 10
  4. 24 11 11
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 9 de 10

Con int y unsigned int de 32 bits, ¿qué muestra esto?

int delta = -3;
unsigned int limit = 2;
printf("%d %u\n", delta < limit, limit + delta);
  1. 1 4294967295
  2. 1 -1
  3. 0 1
  4. 0 4294967295
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 10 de 10

Un valor sin signo es flags = 10u. ¿Qué valor resulta al activar el bit 2, contando el bit de menor peso como bit 0?
  1. 12
  2. 14
  3. 8
  4. 2
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Set 3: Seguir ramas, sentencias switch y bucles10 Preguntas de Práctica

Guarda esta unidad para ver todas las respuestas y practicar cada set.

Pregunta 1 de 10

Una puntuación es 76. ¿Qué carácter se asigna?

if (score >= 70) grade = 'C';
else if (score >= 85) grade = 'B';
else grade = 'F';
  1. C
  2. B
  3. F
  4. No se asigna ningún carácter.
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 2 de 10

¿Qué se muestra cuando mode es 2?

switch (mode) {
case 1: printf("A"); break;
case 2: printf("B");
case 3: printf("C"); break;
default: printf("D");
}
  1. B
  2. BCD
  3. BC
  4. ABC
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 3 de 10

¿Qué valores finales tienen n y passes?

int n = 45, passes = 0;
while (n > 2) {
    n /= 2;
    passes++;
}
  1. n = 1, passes = 5
  2. n = 2, passes = 5
  3. n = 3, passes = 4
  4. n = 2, passes = 4
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 4 de 10

¿Cuántas pasadas se ejecutan en for (int i = 4; i < 19; i += 4)?
  1. 3
  2. 4
  3. 5
  4. 15
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 5 de 10

¿Qué se muestra?

int level = 2;
if (level = 0) {
    printf("Low");
} else {
    printf("Other");
}
  1. Low
  2. LowOther
  3. No se muestra nada.
  4. Other
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 6 de 10

¿Qué total se muestra?

int total = 0;
for (int r = 1; r <= 4; r++) {
    for (int c = 1; c <= r; c++) {
        if (c == 3) break;
        total += c;
    }
}
printf("%d\n", total);
  1. 10
  2. 7
  3. 13
  4. 20
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 7 de 10

El cuerpo de un bucle debe solicitar una respuesta una vez antes de poder comprobarla. ¿Qué forma de bucle expresa directamente ese orden?
  1. while con una prueba antes del cuerpo
  2. do–while
  3. for con una prueba antes del cuerpo
  4. switch con una etiqueta default
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 8 de 10

¿Qué suma se obtiene?

int sum = 0;
for (int i = 2; i <= 8; i++) {
    if (i % 3 == 0) continue;
    sum += i;
}
  1. 35
  2. 9
  3. 26
  4. 8
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 9 de 10

¿Cuántas veces se muestra «tick»?

int running = 1, passes = 0;
while (running && passes < 3) {
    switch (passes) {
    case 1: break;
    default: printf("tick\n");
    }
    passes++;
}
  1. 1
  2. 3
  3. 0
  4. 2
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 10 de 10

La entrada es «9 4 0 7». ¿Qué recuento y media se muestran?

int x, sum = 0, count = 0;
while (scanf("%d", &x) == 1 && x != 0) {
    sum += x;
    count++;
}
if (count) printf("%d %.1f\n", count, (double)sum / count);
  1. 2 6.5
  2. 3 4.3
  3. 3 6.7
  4. 4 5.0
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Set 4: Diagnosticar conversiones y fallos de rango10 Preguntas de Práctica

Guarda esta unidad para ver todas las respuestas y practicar cada set.

Pregunta 1 de 10

¿Qué valor produce 0u - 1u cuando unsigned int tiene 32 bits de valor?
  1. −1
  2. 2 147 483 647
  3. 4 294 967 295
  4. 0
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 2 de 10

Con valores int de 32 bits a = 60000 y b = 40000, ¿qué expresión calcula de forma segura su producto exacto en un long long típico de 64 bits?
  1. (long long)(a * b)
  2. (long long)a * b
  3. a * b
  4. (double)(a * b)
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 3 de 10

Para flags = 6u sin signo, ¿qué resultado produce flags & 2u == 0u?
  1. 1
  2. 2
  3. 6
  4. 0
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 4 de 10

Para valores int arbitrarios a y b, ¿qué prueba detecta el desbordamiento de una suma con signo antes de evaluar a + b?
  1. (b > 0 && a > INT_MAX - b) || (b < 0 && a < INT_MIN - b)
  2. (b > 0 && a > INT_MAX - b) || (b < 0 && a > INT_MIN - b)
  3. (b > 0 && a >= INT_MAX - b) || (b < 0 && a <= INT_MIN - b)
  4. (b > 0 && a < INT_MAX - b) || (b < 0 && a < INT_MIN - b)
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 5 de 10

En una plataforma con unsigned short de 16 bits e int de 32 bits, dos variables unsigned short contienen cada una 48 000. ¿Qué ocurre cuando se evalúa su producto sin conversiones explícitas?
  1. Se multiplican sin signo y se reducen módulo 65 536.
  2. Se promocionan a unsigned int y se reducen en 2³².
  3. Se promocionan a int y su producto se desborda.
  4. Se promocionan a long long y dan el producto exacto.
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 6 de 10

Un programa C17 suma uno a un int que ya es igual a INT_MAX. ¿En qué resultado puede confiar un código C portable?
  1. El resultado debe ser INT_MIN en todos los sistemas.
  2. El resultado debe seguir siendo igual a INT_MAX.
  3. El resultado debe convertirse a unsigned int.
  4. El lenguaje no garantiza ningún resultado.
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 7 de 10

Dos extremos int no negativos cumplen low <= high. En un sistema con int de 32 bits son 1 800 000 000 y 2 000 000 000. ¿Qué expresión calcula su punto medio sin desbordarse?
  1. (low + high) / 2
  2. low + (high - low) / 2
  3. low + high / 2
  4. low / 2 + high
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 8 de 10

Un programa C17 convierte el valor double representable −6,875 a int. ¿Qué valor se almacena?
  1. −6
  2. −7
  3. 6
  4. −6,875
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 9 de 10

Un acumulador de factorial positivo de tipo int es f, y el siguiente multiplicador es un int positivo i. ¿Qué condición identifica que f * i superaría INT_MAX antes de la multiplicación?
  1. f >= INT_MAX / i
  2. f / i > INT_MAX
  3. f > INT_MAX / i
  4. f * i > INT_MAX
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 10 de 10

Un cálculo int da la salida esperada en una construcción, pero provoca un informe de desbordamiento con signo con -fsanitize=undefined. ¿Qué conclusión debería orientar la corrección?
  1. La construcción correcta demuestra que el resultado es portable.
  2. El informe se corrige usando una conversión printf de mayor rango.
  3. El informe garantiza que el int se redujo a un valor concreto.
  4. El desbordamiento alcanzado debe prevenirse en el cálculo.
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Set 5: Combinar entrada, control de flujo y aritmética segura10 Preguntas de Práctica

Guarda esta unidad para ver todas las respuestas y practicar cada set.

Pregunta 1 de 10

Un programa solicita dos medidas double en una llamada a scanf. ¿Qué valor de retorno confirma que ambas asignaciones se completaron?
  1. 0
  2. 2
  3. 1
  4. EOF
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 2 de 10

¿Qué valor se muestra?

int total = 0;
for (int r = 2; r <= 5; r++) {
    for (int c = 1; c < r; c++) {
        if (c % 2 == 0) continue;
        total += r;
    }
}
printf("%d\n", total);
  1. 14
  2. 18
  3. 40
  4. 23
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 3 de 10

Un int de 32 bits days vale 30 000. ¿Qué expresión calcula correctamente su duración en milisegundos en un long long de 64 bits?
  1. 86400000LL * days
  2. (long long)(86400000 * days)
  3. 86400000 * days
  4. (long long)(86400 * days) * 1000
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 4 de 10

¿Qué garantiza este fragmento de C17 sobre su salida?

int n = 4;
printf("%d %d\n", n++, n++);
  1. Muestra 4 y 5 porque los argumentos se evalúan de izquierda a derecha.
  2. Muestra 5 y 4 porque los argumentos se evalúan de derecha a izquierda.
  3. Tiene comportamiento indefinido por modificaciones no secuenciadas.
  4. Muestra 4 y 5 o 5 y 4 como alternativas definidas.
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 5 de 10

¿Qué se muestra cuando a = -2 y b = 7?

if (a > 0)
    if (b > 0) printf("Both");
    else printf("A only");
  1. A only
  2. Both
  3. BothA only
  4. No se muestra nada.
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 6 de 10

¿Por qué este bucle deja de progresar después de mostrar cero una vez?

int i = 0;
while (i < 4) {
    if (i == 1) continue;
    printf("%d\n", i);
    i++;
}
  1. En uno, continue sale del bucle antes de la siguiente prueba.
  2. En uno, continue omite la actualización, por lo que i sigue en uno.
  3. En cero, la condición impide la primera actualización.
  4. En cuatro, el cuerpo del bucle restablece i a uno.
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 7 de 10

Un valor de existencias sin signo es 7u y una cantidad solicitada es 11u. ¿Qué regla evita una reducción modular de las existencias y permite una resta válida cuando hay suficientes?
  1. Restar solo cuando stock >= requested.
  2. Restar y después rechazar si las nuevas existencias son negativas.
  3. Restar solo cuando stock <= requested.
  4. Restar y después exigir existencias distintas de cero.
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 8 de 10

Con unsigned int de 32 bits, ¿qué valor deja este código en flags?

unsigned int flags = 13u;
flags &= ~(1u << 2);
flags |= 1u << 1;
  1. 15
  2. 9
  3. 11
  4. 7
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 9 de 10

Un menú empieza con running = 1 y actions = 0. La entrada es «s q s». ¿Cuánto vale actions después de este bucle?

char cmd;
while (running && scanf(" %c", &cmd) == 1) {
    switch (cmd) {
    case 'q': running = 0; break;
    case 's': actions++; break;
    }
}
  1. 0
  2. 1
  3. 2
  4. 3
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 10 de 10

¿Qué valor se asigna a rate?

int completed = 14, planned = 20;
double rate = completed / planned * 100.0;
  1. 70,0
  2. 0,7
  3. 14,0
  4. 0,0
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

¿Te atascas con una pregunta tuya? Ayuda con Tareas

Términos Clave

91 Términos Clave, cada uno con su definición

A

Acumulador
Un acumulador conserva un resultado acumulado entre pasadas de un bucle, empezando con un valor inicial adecuado fuera de la tarea repetida.
El acumulador de suma de cifras empieza en cero y añade una cifra extraída en cada pasada del bucle.
Advertencia
Una advertencia es un mensaje de diagnóstico que señala un posible problema y normalmente permite que la construcción continúe.
Una advertencia de formato puede revelar que %d se combinó con un argumento double, una combinación no válida.
Almacenamiento de salida en búfer
El almacenamiento de salida en búfer retiene temporalmente el texto producido antes de enviarlo, lo que puede retrasar un mensaje de solicitud sin salto de línea.
Un mensaje de solicitud puede permanecer en el búfer de salida hasta que fflush(stdout) lo envíe antes de una llamada de entrada.
Ancho de campo
Un ancho de campo es el número mínimo de caracteres reservado para un valor de salida con formato, incluidos su signo y su punto decimal.
Un ancho de campo de seis en %6d añade cuatro espacios antes de 42, mientras que un valor de siete cifras sigue mostrándose completo.
Aritmética con comprobación de desbordamiento
La aritmética con comprobación de desbordamiento calcula e indica si el resultado matemático exacto cabe en el tipo de destino elegido.
La operación de aritmética con comprobación de desbordamiento ckd_mul de C23 puede informar de un producto que no cabe en su destino.
Asignación
Una asignación calcula un valor y lo almacena en un destino existente, convirtiéndolo al tipo de ese destino.
La asignación count = count + 1; lee el valor anterior y lo sustituye por un valor una unidad mayor.
Asignación compuesta
Una asignación compuesta combina un operador con el almacenamiento de su resultado en el destino del lado izquierdo.
La asignación compuesta total += item; actualiza el total acumulado sin cambiar item.
Asociatividad
La asociatividad determina cómo se agrupan los operadores del mismo nivel de precedencia, sin fijar por lo general el orden de evaluación.
La asociatividad de izquierda a derecha de la división agrupa 24 / 4 / 2 como (24 / 4) / 2, lo que da tres.

Á

Ámbito de bloque
El ámbito de bloque hace que un nombre local esté disponible desde su declaración hasta el final del bloque que lo contiene.
El ámbito de bloque impide usar una variable temporal declarada dentro de un bloque if después de que ese bloque termine.

B

Bloque de sentencias
Un bloque de sentencias agrupa declaraciones y sentencias entre llaves para que puedan actuar como un solo cuerpo controlado.
Un bloque de sentencias mantiene un mensaje y una actualización de contador juntos dentro de la misma rama condicional.
Bucle anidado
Un bucle anidado es un bucle dentro de otro, cuya secuencia interior se realiza de nuevo en cada pasada exterior que se alcanza.
Un bucle anidado que hace cuatro pasadas interiores por cada una de tres filas ejecuta su cuerpo interior doce veces.
Bucle do-while
Un bucle do-while ejecuta su cuerpo antes de comprobar su condición de repetición, lo que garantiza al menos una ejecución del cuerpo.
Un bucle do-while lee en su cuerpo la primera respuesta para comprobar un rango antes de decidir si vuelve a preguntar.
Bucle for
Un bucle for coloca la inicialización, una condición de repetición y una actualización en una sola cabecera para un cuerpo repetido.
Un bucle for que empieza en cero con i < n e i++ hace n pasadas para n no negativo cuando las actualizaciones caben en el tipo.
Bucle infinito
Un bucle infinito se repite sin llegar a una salida, a menudo porque el estado comprobado nunca cambia como se pretendía.
Un continue antes de la única actualización puede crear un bucle infinito que sigue comprobando un valor sin cambios.
Bucle while (ciclo while)
Un bucle while repite su cuerpo mientras una condición es verdadera y la comprueba antes de cada ejecución del cuerpo.
Un bucle while que procesa cifras se detiene cuando la división entera ha reducido el valor restante a cero.

C

Cadena de formato
Una cadena de formato combina texto literal y especificaciones de conversión que controlan cómo una llamada de entrada o salida maneja los valores.
La cadena de formato en printf("Mass: %.1f kg\n", mass) proporciona una etiqueta, una precisión decimal y una unidad.
Caracteres en blanco
Los caracteres en blanco incluyen espacios, tabulaciones y saltos de línea que los formatos pueden omitir o leer según la conversión.
Las conversiones numéricas de scanf omiten los caracteres en blanco iniciales, pero %c lee un salto de línea pendiente salvo que se le indique que lo omita.
Código fuente
El código fuente es el texto de C legible por personas que un compilador traduce a instrucciones utilizadas para construir un programa.
Después de cambiar el saludo en el código fuente, vuelve a construir el programa antes de ejecutar la nueva versión.
Compilador
Un compilador traduce un programa de un lenguaje fuente a otra forma, como lenguaje ensamblador o código máquina.
El compilador comprueba una declaración de C e informa de que falta un punto y coma antes de crear un nuevo ejecutable.
Complemento a dos
El complemento a dos es una representación binaria con signo en la que el bit de mayor peso tiene un valor negativo.
En complemento a dos de ocho bits, el patrón 10000000 representa menos 128.
Comportamiento definido por la implementación
El comportamiento definido por la implementación es una elección que la implementación de C debe documentar cuando el estándar permite varios comportamientos.
La conversión de entero fuera de rango a un tipo con signo en C17 tiene comportamiento definido por la implementación o emite una señal.
Comportamiento indefinido
El comportamiento indefinido ocurre cuando un programa en C realiza una operación para la que el lenguaje no impone requisitos.
Un producto con signo que se desborda tiene comportamiento indefinido, aunque una ejecución sin optimización parezca reducirlo modularmente.
Comprobación de rango
Una comprobación de rango comprueba si las entradas o un resultado previsto cumplen unos límites antes de realizar una operación insegura.
Una comprobación de rango de existencias compara las cantidades disponibles y solicitadas antes de que una resta sin signo pueda reducirse modularmente.
Condición
Una condición es una expresión cuyo valor, cero o distinto de cero, decide qué recorrido del flujo de control se sigue.
La condición de if (items) es falsa para cero, mientras que un recuento negativo distinto de cero hace que se tome la rama.
Constante de carácter
Una constante de carácter es una forma escrita entre comillas simples cuyo valor es un código entero; las constantes de carácter ordinarias de C tienen tipo int.
La constante de carácter ASCII 'A' puede mostrarse como la letra A con %c o como el número 65 con %d.
Continuación al siguiente caso (fallthrough)
La continuación al siguiente caso ocurre cuando la ejecución continúa desde un caso de switch hacia el código siguiente sin una transferencia de control intermedia.
La continuación al siguiente caso por falta de un break puede ejecutar la impresión del caso siguiente sin volver a comprobar su etiqueta.
Conversión a un tipo de mayor rango
La conversión a un tipo de mayor rango convierte un valor a un tipo con suficiente rango representable antes de que participe en un cálculo de riesgo.
Convertir a a long long antes de a * b cambia el tipo del producto; una conversión explícita después del producto llega demasiado tarde.
Conversión explícita de tipo (cast)
Una conversión explícita de tipo solicita expresamente la conversión a un tipo determinado mediante un tipo entre paréntesis antes de una expresión.
La conversión explícita de (double)sum / count se aplica antes de la división y conserva un cociente fraccionario.
Conversión fuera de rango
Una conversión fuera de rango intenta representar un valor de origen que está fuera de los valores permitidos por el tipo de destino.
Una conversión fuera de rango de un double enorme a int es indefinida cuando la parte entera no cabe.
Conversión implícita
Una conversión implícita cambia automáticamente el tipo de un valor cuando una operación o asignación requiere un tipo distinto.
Sumar un int a un double aplica una conversión implícita al operando entero antes de la suma.
Conversiones aritméticas usuales
Las conversiones aritméticas usuales eligen tipos comunes para los operandos de muchas operaciones aritméticas y de comparación.
Las conversiones aritméticas usuales convierten un int negativo a un tipo sin signo cuando se compara con unsigned int.

D

Declaración
Una declaración introduce un nombre y su tipo para que C pueda comprobar cómo se usa el objeto o la función nombrados.
La declaración int count = 0; da al nombre un tipo entero y también proporciona su valor inicial.
Desbordamiento de enteros
El desbordamiento de enteros ocurre cuando el resultado matemático de una operación aritmética entera queda fuera del rango del tipo de resultado.
Un producto int puede causar desbordamiento de enteros antes de que su resultado llegue a un destino long long.
Desplazamiento de bits
Un desplazamiento de bits mueve el patrón de bits de un entero a la izquierda o a la derecha una cantidad sujeta a las reglas de ancho y signo del tipo promocionado.
Desplazar un valor sin signo dos posiciones a la derecha descarta los dos bits de menor peso de su representación.
División entera
La división entera divide operandos enteros y descarta la parte fraccionaria del cociente hacia cero.
La división entera de 22 / 5 da cuatro incluso cuando el resultado final se asigna a un double.

E

Efecto secundario
Un efecto secundario es un cambio, como modificar datos almacenados o realizar una entrada o salida durante la evaluación de una expresión.
Un incremento cambia una variable como efecto secundario, por lo que dos incrementos conflictivos necesitan un orden definido.
Ejecutable
Un ejecutable es un programa construido en una forma que el sistema operativo de destino puede cargar y ejecutar en el procesador previsto.
Ejecutar ./hello inicia el ejecutable de la carpeta actual sin volver a invocar al compilador de C.
Else ambiguo (dangling else)
Un else ambiguo es un else en condicionales anidados cuya asociación puede no estar clara para quien lee si no hay llaves.
La regla del else ambiguo asocia un else al if anterior más cercano que aún no tiene else, independientemente de la sangría.
Enlazador (linker)
El enlazador combina código objeto y resuelve referencias a código y datos con nombre para producir un programa completo.
El enlazador resuelve la referencia a printf; su código de biblioteca puede proporcionarse mediante una biblioteca compartida.
Entero con signo
Un entero con signo es un tipo de número entero que representa valores negativos y no negativos dentro de un rango fijo.
Un entero con signo puede almacenar un recuento de temperatura negativo, pero las sumas posteriores también deben caber en su rango.
Entero sin signo
Un entero sin signo representa solo números enteros no negativos y usa reducción modular cuando la aritmética supera su rango.
Un entero sin signo es adecuado como máscara de bits, pero restar una cantidad excesiva de las existencias puede producir un valor enorme por reducción modular.
Entrada estándar
La entrada estándar es el flujo de texto entrante habitual del programa, a menudo conectado al teclado o a una entrada redirigida.
Una llamada a scanf lee de la entrada estándar y convierte los siguientes caracteres que coinciden en valores almacenados.
Error de compilación
Un error de compilación es un problema detectado al traducir el código fuente que impide completar correctamente una nueva construcción.
Un paréntesis de cierre sin pareja causa un error de compilación antes de que pueda construirse un nuevo ejecutable.
Error de redondeo
Un error de redondeo es la diferencia que se introduce cuando un valor o cálculo se sustituye por un valor representable cercano.
Varias sumas decimales pueden producir un pequeño error de redondeo que haga fallar una prueba de igualdad exacta.
Error de una unidad (off-by-one)
Un error de una unidad hace que un bucle o un cálculo de límites incluya un valor de más o de menos.
Sustituir i < n por i <= n en un bucle de conteo que empieza en cero causa un error de una unidad.
Error lógico
Un error lógico hace que un programa calcule o elija algo distinto del resultado deseado, aunque se ejecute.
Una prueba de intervalos de puntuación en el orden incorrecto crea un error lógico que asigna una calificación equivocada.
Especificación de conversión
Una especificación de conversión es una parte de un formato que empieza por un signo de porcentaje e indica el tipo y la presentación requeridos de un valor.
La especificación de conversión %lld corresponde a un argumento long long; %d no puede convertirlo automáticamente.
Estado de salida
Un estado de salida es el valor que un programa comunica al entorno que lo llamó cuando termina, indicando éxito o fallo.
Un estado de salida de cero desde main permite que un script llamador reconozca que la ejecución terminó correctamente.
Etiqueta case
Una etiqueta case indica una expresión constante entera distinta que puede servir como punto de entrada en un switch.
La etiqueta case case 4: es válida en C17; una variable const int ordinaria no puede proporcionar su valor.
Etiqueta default
Una etiqueta default proporciona un punto de entrada al switch para un valor de control que no coincide con ninguna etiqueta case.
Una etiqueta default puede dirigir un número de menú inesperado a un mensaje de error en lugar de omitir el switch.
Evaluación en cortocircuito
La evaluación en cortocircuito omite el operando derecho de un operador lógico cuando su operando izquierdo ya determina el resultado.
La evaluación en cortocircuito de count != 0 && sum / count > 4 omite la división cuando count es cero.

F

Fallo de coincidencia
Un fallo de coincidencia ocurre cuando la entrada disponible no puede coincidir con la conversión o el carácter literal que espera scanf.
Las letras al inicio de una conversión %d causan un fallo de coincidencia y permanecen sin leer para un nuevo intento.
Fin de la entrada
El fin de la entrada significa que el flujo entrante no tiene más caracteres disponibles para leer, a diferencia de un texto ordinario que no coincide.
Un bucle de entrada numérica se detiene al llegar al fin de la entrada, pero también puede detenerse antes ante texto no numérico.

I

Identificador
Un identificador es un nombre utilizado en código C, como el nombre de una variable, que sigue reglas de escritura y distingue entre mayúsculas y minúsculas.
El identificador distance_m es válido; 2distance no puede ser un nombre porque empieza por un dígito.
Incremento posfijo
Un incremento posfijo suma uno a una variable y produce su valor anterior para usarlo en la expresión que lo contiene.
Con n = 3, el incremento posfijo de old = n++; da a old el valor tres y deja n en cuatro.
Inicialización
La inicialización da a un objeto su valor inicial como parte de su declaración, antes de que las asignaciones posteriores lo cambien.
La inicialización de total a cero antes de un bucle proporciona a la primera suma un valor inicial definido.
Iteración
Una iteración es una ejecución del cuerpo de un bucle, después de que su prueba de control permita iniciar esa pasada.
Un bucle for que visita 3, 6, 9 y 12 tiene cuatro iteraciones aunque la actualización final del contador llegue a 15.

L

Literal entero
Un literal entero es una constante entera escrita en el código fuente con una notación de base y un sufijo de tipo opcional.
El literal entero 0x30 usa hexadecimal, mientras que 48u escribe ese valor en decimal sin signo.

M

Macro
Una macro define una sustitución del preprocesador que se aplica al código fuente posterior antes de la compilación normal.
Con #define LIMIT 8, el token LIMIT se sustituye por 8 allí donde se aplica esa macro.
Máscara de bits
Una máscara de bits es un patrón entero que se usa para seleccionar, activar, desactivar o examinar bits concretos de otro valor.
La máscara de bits 1u << 5 selecciona el bit cinco, contando el bit menos significativo como posición cero.

N

NaN (no es un número)
Un NaN es un valor de coma flotante IEEE que significa «no es un número» y se usa para resultados como cero dividido por cero.
Un NaN se compara como distinto de sí mismo, por lo que la igualdad ordinaria no puede tratarlo como un valor medido ordinario.
Número de asignaciones
El número de asignaciones devuelto por scanf es el número de destinos rellenados correctamente antes de que la llamada se detuviera.
Un número de asignaciones de dos al leer tres medidas deja sin confirmar el tercer destino.

O

Operador a nivel de bits
Un operador a nivel de bits actúa sobre bits individuales de operandos enteros para combinar, invertir o desplazar sus patrones de bits.
El operador a nivel de bits & conserva solo los bits activados en común de doce y diez, dando ocho.
Operador coma
El operador coma evalúa su operando izquierdo antes que el derecho y produce el valor del operando derecho.
El operador coma ordena las actualizaciones en i++, j--; las comas entre argumentos de printf no lo hacen.
Operador condicional
El operador condicional usa condition ? a : b para elegir una expresión de valor después de comprobar la condición.
Con una condición cero, el operador condicional de condition ? a : b evalúa b y omite a.
Operador lógico
Un operador lógico combina o invierte valores de verdad y produce cero o uno mediante &&, || o !.
El operador lógico de ready && valid comprueba si ambos valores almacenados son distintos de cero.
Operador relacional
Un operador relacional compara valores numéricos mediante <, <=, > o >= y produce un int cero o uno.
El operador relacional de height >= 1 comprueba un límite; otra comparación comprueba el otro.
Operando
Un operando es un valor o expresión al que se aplica un operador; su tipo ayuda a determinar cómo se comporta la operación.
En sum / count, los tipos de los operandos determinan si la división conserva un resultado fraccionario.
Orden de evaluación
El orden de evaluación describe cuándo se calculan los operandos y se producen los efectos secundarios, una cuestión distinta de la agrupación de la expresión.
Los argumentos de funciones en C17 no tienen un orden de evaluación obligatorio de izquierda a derecha, pese a sus posiciones escritas.

P

Precedencia
La precedencia determina qué operadores se agrupan con mayor prioridad con sus operandos cuando los paréntesis no especifican la agrupación.
La mayor precedencia de la multiplicación agrupa 4 * 2 antes de la suma en 3 + 4 * 2.
Precisión
La precisión es la cantidad de información significativa que puede conservar un formato numérico, distinta de su magnitud máxima.
La precisión limitada de float pierde pasos de una unidad entre enteros en magnitudes que aún están muy por debajo del máximo del tipo.
Preprocesador
El preprocesador procesa directivas como #include y la sustitución de macros antes de las etapas principales de traducción de C.
El preprocesador inserta declaraciones de una cabecera estándar antes de que el compilador compruebe su uso.
Promoción de enteros
Una promoción de enteros convierte un tipo entero pequeño a int, o a unsigned int si int no puede representar todo su rango.
En un sistema con short de 16 bits e int de 32 bits, la promoción de enteros permite multiplicar operandos unsigned short como int.
Prueba de valores límite
Una prueba de valores límite ejecuta valores en un límite significativo o cerca de él para comprobar el comportamiento donde cambia una regla o un rango.
Una prueba de valores límite para una suma incluye una suma igual a INT_MAX y una pareja que lo superaría en uno.

R

Rama
Una rama es un recorrido de sentencias seleccionado por una decisión, como la parte if o la parte else de una sentencia condicional.
El programa de puntuaciones llega a una sola rama de su cadena else-if, empezando por la primera condición verdadera.
Reducción modular de enteros sin signo
La reducción modular de enteros sin signo reduce módulo el resultado de una operación aritmética sin signo para devolverlo al rango no negativo del tipo.
Restar uno a cero sin signo muestra la reducción modular de enteros sin signo al producir el valor máximo.
Resto (residuo)
Un resto es el valor que deja la operación entera % de C; si es distinto de cero, tiene el signo del dividendo.
El resto -22 % 5 es −2 porque el cociente truncado es −4 y debe cumplirse la identidad de la división.
Resultado intermedio
Un resultado intermedio es un valor calculado dentro de una expresión mayor antes de una operación o asignación posterior.
La suma de (low + high) / 2 es un resultado intermedio que puede desbordarse incluso cuando el punto medio cabe en el tipo.

S

Sanitizador (sanitizer)
Un sanitizador es una herramienta de comprobación en tiempo de ejecución añadida durante la compilación que informa de ciertas operaciones no válidas en los recorridos ejecutados.
Un sanitizador de comportamiento indefinido puede identificar la multiplicación en la que se desborda un cálculo de contador con signo.
Secuencia de escape
Una secuencia de escape usa una barra invertida en un literal de carácter o de cadena de C para representar un carácter especial.
La secuencia de escape \n añade un salto de línea dentro de una cadena de C; printf usa %% para un signo de porcentaje.
Sentencia break
Una sentencia break sale inmediatamente del switch o bucle que la contiene más cercano y continúa después de esa construcción.
Una sentencia break dentro de un switch de menú sale de ese switch mientras el bucle while que lo rodea continúa.
Sentencia continue
Una sentencia continue omite el resto del cuerpo del bucle y pasa al siguiente punto de continuación de ese bucle.
Una sentencia continue en un bucle for llega a la actualización de la cabecera; en un bucle while vuelve a la prueba.
Sentencia switch
Una sentencia switch elige una etiqueta de entrada comparando un valor entero de control con valores enteros fijos de casos.
Una sentencia switch puede asignar la misma acción a los caracteres de comando r minúscula y R mayúscula.

T

Tipo entero de ancho exacto
Un tipo entero de ancho exacto es un tipo entero definido mediante typedef, cuando está disponible, cuyo nombre fija su ancho exacto en bits y si tiene signo.
El tipo entero de ancho exacto uint8_t, cuando está disponible, contiene valores de cero a 255.
Tolerancia
Una tolerancia es una diferencia numérica permitida que se elige según la escala y la exactitud requeridas por una comparación.
El programa compara fabs(measured - target) con la tolerancia en lugar de exigir igualdad exacta.
Traza de ejecución
Una traza de ejecución registra los valores que cambian y los pasos ejecutados de un programa para que su comportamiento pueda comprobarse en orden.
Una traza de las cifras restantes y la suma acumulada después de cada pasada ayuda a localizar una actualización incorrecta del bucle.
Truncamiento hacia cero
El truncamiento hacia cero elimina la parte fraccionaria de un número y reduce la magnitud de un resultado negativo.
El truncamiento hacia cero hace que convertir el valor representable −7,9 a int dé −7 en lugar de −8.

V

Valor booleano
Un valor booleano representa falso o verdadero; la conversión a bool de C transforma cero en falso y cualquier valor distinto de cero en verdadero.
Después de incluir <stdbool.h> en C17, bool ready = 2; almacena el valor booleano verdadero.
Valor centinela
Un valor centinela es un valor designado que indica el final de los datos y se reconoce antes del procesamiento habitual.
Un valor centinela cero puede terminar un cálculo de media sin aumentar ni la suma ni el número de datos.
Valor de coma flotante (punto flotante)
Un valor de coma flotante representa una magnitud real mediante una precisión fija y una escala, a menudo con redondeo.
Una longitud almacenada como valor de coma flotante puede aproximar el decimal introducido sin coincidir exactamente con él.
Variable
Una variable es una ubicación de almacenamiento con nombre y un tipo declarado que determina cómo se interpreta su valor almacenado.
La variable mass_kg almacena una medida mientras count registra cuántas muestras se introdujeron.

Sobre esta unidad

Por StudeliInformar de un error

Crea tus propios Materiales de Estudio

Convierte tus notas, diapositivas y documentos en Guías de Estudio, Preguntas de Práctica, Flashcards y 6 herramientas más.

Comenzar