Arrays y cadenas en C

Aprende cómo C almacena muchos valores en un array y cómo representa el texto mediante caracteres que terminan en un byte nulo. Recorre arrays y tablas con bucles, pásalos a funciones, procesa cadenas con tus propios bucles y con las funciones estándar de cadenas, descubre cómo se almacena el texto UTF-8 en bytes y lee los argumentos que recibe un programa en la línea de comandos. A lo largo de la unidad, aprende por qué C nunca comprueba los límites de los arrays y cómo leer y copiar datos para que siempre quepan.

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

Lo que aprenderás

  1. Declara arrays de tamaño fijo, accede a cada elemento mediante su índice, recórrelos con bucles sin pasar del final y compártelos con funciones.
    • Declarar e inicializar un array y acceder a sus elementos mediante índices
      Crea arrays con un número fijo de elementos, dales valores iniciales y lee o modifica elementos individuales mediante su índice.
    • Recorrer arrays con bucles sin salir de sus límites
      Visita cada elemento con un bucle de conteo y usa ese patrón para sumar, encontrar el valor máximo, buscar, contar e invertir.
    • Pasar arrays a funciones
      Escribe funciones que reciban arrays como parámetros, pasa la longitud junto con ellos y determina cuándo puede la función modificar los datos de la función llamadora.
  2. Trata los caracteres como códigos enteros pequeños, almacena texto en arrays de char que terminan con un carácter nulo, procésalo carácter a carácter y observa cómo se almacena el texto UTF-8 en bytes.
    • Tratar los caracteres como códigos numéricos
      Usa el tipo char y las constantes de carácter como números, convierte dígitos y letras mediante operaciones aritméticas y clasifica caracteres con <ctype.h>.
    • Almacenar texto en cadenas terminadas en nulo
      Explica cómo se distribuye una cadena de C en un array de char, por qué necesita un carácter nulo como terminador y en qué se diferencian los literales de cadena de los arrays.
    • Procesar cadenas carácter a carácter
      Recorre una cadena hasta su terminador para contar, convertir, invertir, filtrar y cifrar texto, y termina cada cadena nueva con '\0'.
    • Explicar cómo se almacena el texto UTF-8 en arrays de char
      Relaciona los caracteres, los puntos de código Unicode y los bytes UTF-8, y predice qué hacen las funciones de C basadas en bytes con texto que va más allá de ASCII.
  3. Mide, copia, concatena, compara y busca cadenas con <string.h>, y asegúrate de que cada destino tenga espacio para el resultado y su terminador.
    • Medir, copiar y concatenar cadenas
      Usa strlen, strcpy y strcat correctamente y calcula cuánto espacio necesita cada destino.
    • Comparar cadenas y realizar búsquedas en ellas
      Compara cadenas con strcmp y strncmp, encuentra caracteres y subcadenas, y divide texto en tokens.
    • Copiar sin exceder los límites y trabajar directamente con bytes de memoria
      Usa correctamente las funciones con límite strncpy, strncat y snprintf, detecta el truncamiento y copia, rellena y compara arrays completos con las funciones de memoria.
  4. Almacena tablas y listas de cadenas en arrays de dos o más dimensiones, recórrelos con bucles anidados y pásalos a funciones.
    • Declarar y recorrer arrays bidimensionales
      Declara e inicializa tablas de valores, explica cómo se distribuyen sus filas en memoria y procesa filas y columnas con bucles anidados.
    • Pasar tablas a funciones y almacenar listas de cadenas
      Escribe funciones que reciban arrays bidimensionales y guarda varias cadenas en un array de char de 2 dimensiones o en un array de punteros a cadenas.
  5. Lee las palabras escritas después del nombre de un programa mediante argc y argv, comprueba cuántas hay y conviértelas de forma segura en números y opciones.
    • Leer argc y argv
      Declara main con sus dos parámetros, describe qué contiene cada elemento de argv y comprueba el número de argumentos antes de usar cualquiera de ellos.
    • Convertir y validar argumentos numéricos
      Convierte cadenas de argumentos en números con comprobación de errores, rechaza las entradas no válidas y comprueba el rango de los valores antes de usarlos.
  6. Reconoce cómo los programas en C leen y escriben fuera de sus arrays, por qué es peligroso y cómo leer y copiar texto para que siempre quepa.
    • Reconocer los accesos fuera de los límites y sus efectos
      Explica por qué C no detiene los accesos fuera de los límites, qué puede ocurrir cuando se producen y qué errores los causan.
    • Leer y copiar texto sin exceder los límites del búfer
      Lee la entrada con límites de tamaño, detecta las líneas demasiado largas y copia texto solo cuando quepa en su destino.

Guía de estudio de Arrays y cadenas en C

6 capítulos

1

Almacenar y procesar colecciones de valores con arrays

Los arrays permiten que un mismo bucle trabaje con muchos valores relacionados. El array fija cuánto almacenamiento existe, mientras que los índices seleccionan elementos individuales y una cantidad registra cuántos se utilizan. Aprende cómo las declaraciones establecen valores iniciales, cómo los bucles calculan resultados sin superar un límite y cómo las funciones comparten los elementos de la función llamadora. Ejemplos pequeños relacionan el código con las posiciones exactas que lee o escribe.

1.1 Declarar e inicializar un array y acceder a sus elementos mediante índices

Un array da un nombre a un número fijo de valores del mismo tipo. Sus elementos se encuentran uno junto a otro en memoria. Usa un array cuando se aplique la misma operación a muchos valores, por ejemplo al actualizar un conjunto de puntuaciones.

Crear los elementos
int scores[5] = {90, 72, 85, 64, 99};
int primes[] = {2, 3, 5, 7};
int counts[6] = {0};

La primera declaración crea cinco enteros. El compilador cuenta cuatro valores en el segundo inicializador, por lo que primes tiene cuatro elementos. En el tercero, el primer elemento es explícitamente cero y cada valor de inicialización que falta también pasa a ser cero. Del mismo modo, int a[5] = {1, 2}; contiene 1, 2, 0, 0, 0. Un inicializador designado, disponible desde C99, elige una posición: int b[5] = {[2] = 7}; coloca 7 en el tercer elemento y cero en los demás.

Una declaración local automática como int data[5]; reserva espacio sin proporcionar valores iniciales. Escribe cada elemento antes de leerlo. Los arrays declarados fuera de todas las funciones y los declarados static se inicializan a cero cuando no se proporciona un inicializador explícito.

Acceder a un valor mediante su índice

Los índices empiezan en cero. Para cinco elementos las posiciones válidas van de 0 a 4. scores[1] representa la segunda puntuación; no representa la primera. Un elemento puede usarse dondequiera que se pueda usar una variable normal de su tipo:

scores[1] = 75;
scores[1] += 5;
int last = scores[4];

La segunda puntuación es ahora 80. La última sigue siendo 99. Un índice puede ser una variable o una expresión, por lo que scores[n - 1] accede al último elemento cuando n es el número de elementos y es positivo. En scores[5] no hay ningún elemento al que acceder.

Contar elementos y bytes

sizeof scores mide el array completo en bytes. En un sistema con enteros de 4 bytes, los cinco elementos ocupan 20 bytes. Dividir por el tamaño de un elemento elimina la dependencia del tamaño del entero:

size_t n = sizeof scores / sizeof scores[0];
printf("%zu\n", n);

Esto imprime 5. size_t es el tipo entero sin signo usado para tamaños; %zu lo imprime. Este cálculo funciona donde scores nombra el array real. Un parámetro de función no lleva consigo el tamaño completo del array.

Mantener separados el tamaño y los valores

Un nombre como #define NUM_SCORES 5 permite que la declaración y sus bucles compartan un tamaño. En C17, una declaración local const int n = 5; int a[n]; crea, en cambio, un array de longitud variable: const no convierte n en una expresión constante entera. Su tamaño se elige al ejecutarse la declaración y luego permanece fijo durante el tiempo de vida del array. No puede usar una lista de inicialización. La compatibilidad con arrays de longitud variable es opcional en C11 y C17; C23 añade constexpr para constantes en tiempo de compilación.

Los arrays no se pueden asignar como un todo: b = a; no es válido. Copia sus elementos con un bucle o una función de memoria adecuada. Para dos arrays separados, a == b compara direcciones, no valores de elementos. Compara los elementos correspondientes para comprobar la igualdad de valores. En cambio, una comparación de bytes con memcmp comprueba sus representaciones almacenadas; valores de coma flotante iguales, como el cero positivo y el negativo, pueden tener representaciones diferentes.

Puntos clave

  • Los índices de un array van de 0 a una unidad menos que el número de elementos.
  • Los valores de inicialización que faltan pasan a ser cero; los elementos locales sin inicializar deben escribirse antes de usarlos.
  • Usa sizeof a / sizeof a[0] solo donde a sea realmente un array.
  • Un array conserva su tamaño durante todo su tiempo de vida.
  • Copia y compara los valores de los arrays elemento por elemento.

1.2 Recorrer arrays con bucles sin salir de sus límites

Un bucle de conteo relaciona las posiciones de un array con una operación. Decide qué elementos contienen datos útiles y visita exactamente esas posiciones. El mismo patrón permite calcular totales, buscar y actualizar valores.

Visitar los elementos utilizados
int t[5] = {12, 15, 9, 14, 10};
int sum = 0;
for (int i = 0; i < 5; i++) {
    sum += t[i];
}
double average = sum / 5.0;

El bucle usa los índices 0, 1, 2, 3 y 4. El total pasa por 12, 27, 36, 50 y 60, por lo que el promedio es 12,0. Escribir i <= 5 añade un acceso no válido en el índice 5. Esa pasada adicional es un error de una unidad.

Para calcular un promedio, usa división de coma flotante. Con un total de 61, 61 / 5 da el entero 12; 61 / 5.0 da 12,2. Almacenar después el primer resultado en un double no puede recuperar la fracción ya descartada.

Encontrar un máximo o una coincidencia

Para un array no vacío, inicializa el máximo con su primer valor:

int max = t[0];
for (int i = 1; i < 5; i++) {
    if (t[i] > max) max = t[i];
}

La primera comparación reemplaza 12 por 15. Los valores restantes son menores, por lo que permanece 15. Empezar desde cero fallaría para una lista como {-8, -3, -11}: cero es mayor que todos los valores almacenados, pero no está en la lista.

Una búsqueda lineal comprueba los valores en orden:

int found = -1;
for (int i = 0; i < 5; i++) {
    if (t[i] == 14) {
        found = i;
        break;
    }
}

Aquí found pasa a ser 3. El valor -1 indica que no hay coincidencia porque no puede ser un índice válido. Indica que no se encontró el valor solo después de comprobar todos los candidatos. Devolver -1 después del primer elemento diferente hace que se pasen por alto coincidencias posteriores en el array.

Contar y reorganizar

Un array de frecuencias almacena un contador por categoría. Para los dígitos, int counts[10] = {0}; proporciona contadores de 0 a 9. Después de verificar d >= 0 && d < 10, counts[d]++ incrementa el contador del dígito d. La comprobación protege el array cuando el valor del dígito procede de la entrada.

Para invertir un array, intercambia pares desde sus extremos:

for (int i = 0; i < 5 / 2; i++) {
    int temp = t[i];
    t[i] = t[5 - 1 - i];
    t[5 - 1 - i] = temp;
}

El primer intercambio cambia 12 por 10. El segundo cambia 15 por 14. El 9 central permanece en su lugar, y queda {10, 14, 9, 15, 12}. Detente a mitad de camino: continuar por las cinco posiciones intercambiaría cada par dos veces.

Llevar la cuenta por separado de la capacidad

Un array con espacio para 100 valores puede contener actualmente solo 37 entradas útiles. Su capacidad es 100; su número de elementos utilizados es 37. Procesa las posiciones inferiores a count. Para añadir un valor, comprueba primero count < CAPACITY, almacénalo en a[count] y luego incrementa count. Recorrer hasta la capacidad puede hacer que se lean posiciones que nunca han recibido valores útiles, aunque esas posiciones estén físicamente dentro del array.

Puntos clave

  • Usa un límite de bucle con < estricto.
  • Usa división de coma flotante cuando un promedio pueda tener una fracción.
  • Inicializa el máximo de un array no vacío a partir de un elemento, incluso cuando todos los datos sean negativos.
  • Se determina que una búsqueda ha fallado después de comprobar todos los candidatos.
  • Valida un valor antes de usarlo como índice.
  • Invierte el array intercambiando solo la primera mitad de los pares.
  • El número de elementos utilizados indica cuántos se usan; la capacidad es el espacio disponible.

1.3 Pasar arrays a funciones

Una función puede procesar un array sin recibir una copia separada de cada elemento. La llamada comparte el acceso al almacenamiento de la función llamadora. Pasa el número de elementos junto con el array para que la función sepa dónde detenerse.

Proporcionar los datos y su longitud
#include <stddef.h>

double average(const int a[], size_t n) {
    if (n == 0) return 0.0;
    int sum = 0;
    for (size_t i = 0; i < n; i++) {
        sum += a[i];
    }
    return (double) sum / n;
}

La función llamadora puede declarar int values[] = {7, 8, 10}; y llamar a average(values, sizeof values / sizeof values[0]). Las tres sumas producen 25; convertir sum a double antes de la división produce aproximadamente 8,33. Devolver 0,0 cuando no hay valores es el comportamiento que esta función ha elegido para una entrada vacía. Los valores pequeños de este ejemplo mantienen la suma entera dentro de rango.

En una declaración de parámetro, int a[] se ajusta a int *a. Recibe la dirección del primer elemento. Incluso int a[10] no comprueba ni exige diez elementos. La función llamadora debe proporcionar una longitud que corresponda a los datos disponibles.

Por qué sizeof pierde la longitud

En el punto de llamada, sizeof values mide el array. Dentro de la función, sizeof a mide el parámetro de tipo puntero. Donde los punteros ocupan 8 bytes y los enteros ocupan 4, dividir estos tamaños da 2, independientemente del número de elementos de la función llamadora. Ese resultado no describe ni una copia de dos elementos ni el array original. Mantén la longitud como parámetro explícito.

size_t, declarado por cabeceras como <stddef.h>, es el tipo sin signo que produce sizeof. Puede representar tamaños de objetos, y %zu lo imprime. Usarlo para el índice del bucle evita comparar un índice con signo con una longitud sin signo.

Decidir si los elementos pueden cambiar

El const de const int a[] impide asignar valores a los elementos mediante este parámetro. Es adecuado para una función que calcula un resultado a partir de los datos. Una función destinada a actualizar elementos lo omite:

void zero(int a[], size_t n) {
    for (size_t i = 0; i < n; i++) {
        a[i] = 0;
    }
}

Después de zero(values, 3), los tres elementos de la función llamadora contienen cero. El array sigue teniendo tres elementos. Un parámetro escalar como n es una copia separada; modificar esa copia no cambiaría una variable de cantidad de la función llamadora.

Para producir varios resultados, deja que la función llamadora proporcione un array de salida y su longitud. Una función puede llenar out[i] con cada resultado. Las funciones de C no pueden devolver un array por valor, y devolver la dirección de un array local falla porque ese array deja de existir cuando la función retorna.

Contar hacia atrás con un índice sin signo

Un índice sin signo no puede volverse negativo. En for (size_t i = n - 1; i >= 0; i--), la condición siempre es verdadera; después de cero, el valor pasa por reducción modular a un número positivo grande. Con n == 0, incluso la resta inicial sufre reducción modular. Empieza desde el número de elementos:

for (size_t i = n; i > 0; i--) {
    printf("%d\n", a[i - 1]);
}

Con tres elementos, los índices a los que se accede son 2, 1 y 0. Con cero elementos, el cuerpo nunca se ejecuta. Incluye <stdio.h> para printf.

Puntos clave

  • Pasa la longitud de un array por separado; no se puede recuperar el número de elementos a partir de un parámetro.
  • Las escrituras mediante un parámetro de tipo array modifican los elementos de la función llamadora.
  • Usa const para un parámetro que solo lee elementos.
  • La función llamadora proporciona almacenamiento para varios valores de salida.
  • Cuenta hacia atrás desde n y accede a i - 1 cuando uses size_t.
2

Representar texto con caracteres y cadenas terminadas en nulo

El texto comienza con códigos de caracteres y termina con una regla de almacenamiento: una cadena de C se detiene en su primer byte cero. Aprende en qué se diferencian los caracteres, los literales y los arrays modificables, y luego usa bucles para contar y transformar texto. UTF-8 añade una distinción esencial entre un byte y un punto de código completo. Seguir tanto el texto como su almacenamiento explica por qué una palabra visible puede necesitar más posiciones en un array de lo que sugieren sus letras.

2.1 Tratar los caracteres como códigos numéricos

Un carácter de C se almacena como un código numérico. Esto permite que un programa imprima un símbolo, lo compare con otro o realice operaciones aritméticas con su código. El conjunto de caracteres determina qué número representa cada símbolo.

Leer el código y el carácter

Un char ocupa un byte: sizeof(char) es 1 por definición. Una constante de carácter como 'A' tiene tipo int. En el conjunto de caracteres basado en ASCII que se usa aquí, su código es 65:

printf("%c %d\n", 'A', 'A');

La salida es «A 65». %c muestra el carácter; %d muestra su número. ASCII asigna los códigos de 0 a 127. Los dígitos van de 48 a 57, las letras mayúsculas de 65 a 90, las minúsculas de 97 a 122 y el espacio tiene el código 32.

Las comillas simples y las dobles crean cosas diferentes. 'A' es un código de carácter. "A" es un array que contiene 'A' y un byte cero como terminador. Almacena un carácter con char c = 'A'; y compáralo con c == 'A'.

Escribir caracteres que necesitan secuencias de escape
Escritura en CCarácter
'\n'Salto de línea, código ASCII 10
'\t'Tabulador, código ASCII 9
'\\'Barra invertida
'\''Comilla simple
'\0'Carácter nulo, código 0

El carácter nulo es distinto del dígito visible '0', cuyo código ASCII es 48. Una cadena usa el carácter nulo para marcar su final.

Convertir códigos en valores útiles

El estándar de C garantiza códigos de dígitos consecutivos. Después de comprobar que c es un dígito, c - '0' produce su valor. Por tanto, '7' - '0' es 7, no 55. Restar el código inicial elimina la posición del dígito dentro del conjunto de caracteres.

Las letras ASCII también son consecutivas, por lo que restar 'A' convierte una letra mayúscula en una posición de 0 a 25. Un desplazamiento de César de tres posiciones usa:

char shifted = (c - 'A' + 3) % 26 + 'A';

Para 'X', restar 65 da 23; sumar tres da 26; el resto de dividir entre 26 es cero; sumar 65 devuelve 'A'. La expresión 'X' + 3 por sí sola da el código 91, que es '['. Las operaciones aritméticas con letras dependen de un orden de letras como el de ASCII; C garantiza dígitos consecutivos, pero no exige letras consecutivas.

Clasificar y convertir con ctype.h

Incluye <ctype.h> para usar isdigit, isalpha, isalnum, isspace, isupper, islower, toupper y tolower. Las comprobaciones devuelven un valor distinto de cero si hay coincidencia y cero en caso contrario. toupper('q') devuelve 'Q'; toupper('3') deja el dígito sin cambios. isspace reconoce separadores como el espacio, el tabulador y el salto de línea.

Un char sin especificador puede tener signo. Un byte mayor que 127 puede convertirse entonces en un valor negativo, que no es un argumento válido para estas funciones salvo que sea el marcador especial de entrada EOF. Al procesar bytes de una cadena, conviértelos primero:

if (isalpha((unsigned char) c)) {
    c = (char) toupper((unsigned char) c);
}

En la configuración regional «C» predeterminada, estas funciones reconocen las letras básicas, no los caracteres UTF-8 completos de varios bytes. La conversión explícita hace que el byte sea un argumento válido; no decodifica UTF-8.

Puntos clave

  • Las constantes de carácter usan comillas simples; los literales de cadena usan comillas dobles.
  • \0 es un carácter de valor cero, no el dígito 0.
  • Resta '0' a un carácter que representa un dígito para obtener su valor.
  • Las operaciones con posiciones de letras usan aquí el orden ASCII.
  • Convierte un byte de una cadena a unsigned char antes de una llamada a ctype.
  • Una comprobación de ctype devuelve un valor distinto de cero si hay coincidencia, no necesariamente 1.

2.2 Almacenar texto en cadenas terminadas en nulo

Una cadena de C es una secuencia de caracteres que termina con '\0'. El terminador forma parte del almacenamiento, pero no de la longitud del texto. C no tiene un tipo de cadena integrado independiente: los caracteres ocupan un array.

Ver los bytes en el array
char word[] = "cat";
char name[20] = "Ada";

word contiene cuatro bytes: 'c', 'a', 't', '\0'. El tamaño de su array es 4 y strlen(word) es 3. Incluye <string.h> para usar strlen. El array name, más grande, contiene las tres letras, su terminador y dieciséis bytes cero más. Su capacidad es 20, mientras que su longitud actual es 3.

Un inicializador carácter a carácter puede crear la misma cadena:

char word[] = {'c', 'a', 't', '\0'};

Omitir el último elemento crea tres caracteres sin terminador de cadena. C también permite char word[3] = "cat";, que llena las tres posiciones y omite el byte cero. Un array así no es una cadena de C. Una función como strlen o printf con %s sigue leyendo hasta encontrar un byte cero, por lo que no puede usar de forma segura ese array de tres bytes como texto.

Elegir un array modificable o un literal
DeclaraciónDónde está el texto¿Pueden cambiar sus caracteres?
char a[] = "hello";Una copia en el array aSí
const char *p = "hello";Un literal de cadena al que apunta pNo

a[0] = 'H'; cambia la copia a «Hello». Un literal no debe modificarse. Aunque se asigne a un char * sin calificador, escribir mediante ese puntero produce comportamiento indefinido. Escribir const char * hace visible esa restricción para el compilador.

El compilador concatena los literales de cadena contiguos: "Hello, " "world" produce el mismo texto que "Hello, world". La cadena resultante sigue teniendo un solo terminador final. Esto es útil para dividir un literal largo entre varias líneas del código fuente.

Imprimir y modificar el texto
printf("%s\n", word);
printf("%c\n", word[0]);

La primera llamada imprime la cadena completa. La segunda imprime su primer carácter. Un literal de cadena en printf va entre comillas dobles porque es texto que contiene un formato; %c recibe un código de carácter.

Una asignación a un elemento modifica un carácter almacenado. Con char name[20] = "Ada";, establecer name[0] = 'E'; produce «Eda». Establecer después name[1] = '\0'; produce «E» como cadena. Los bytes de 'a' y del terminador anterior siguen existiendo más adelante en el array, pero las operaciones con cadenas se detienen en el primer cero.

Un array no puede recibir texto de reemplazo mediante name = "Bob"; después de la declaración. Copia los caracteres en él mediante una llamada comprobada a strcpy o un bucle, e incluye el terminador. La longitud del texto de reemplazo debe ser menor que la capacidad.

Representar la ausencia de texto

La cadena vacía "" sigue ocupando un byte para '\0'. Tiene longitud cero y se puede comprobar con name[0] == '\0'. Una cadena vacía es una cadena real con un terminador; un puntero nulo, en cambio, no apunta a ningún almacenamiento de cadena.

Puntos clave

  • Una cadena de C termina en su primer '\0'.
  • La capacidad incluye el terminador; la longitud de la cadena lo excluye.
  • Un array de caracteres sin terminador no puede pasarse como cadena de C.
  • Las copias de literales en arrays son modificables; los propios literales no lo son.
  • Escribir un terminador antes acorta el texto sin cambiar el tamaño de su array.

2.3 Procesar cadenas carácter a carácter

El terminador de una cadena da a un bucle su punto de parada. Visita los bytes anteriores para contar, convertir o filtrar texto. Al crear una cadena nueva, el propio programa debe escribir un nuevo terminador.

Recorrer hasta el final una vez
for (size_t i = 0; s[i] != '\0'; i++) {
    /* Process s[i]. */
}

En cada pasada, la condición pregunta si el byte actual pertenece al texto. El bucle se detiene antes del terminador. Llamar a strlen(s) en esa condición puede volver a recorrer toda la cadena en cada pasada. Comprueba directamente el byte actual o calcula size_t len = strlen(s); una sola vez cuando la longitud vaya a permanecer sin cambios.

Para convertir a mayúsculas, usa un array modificable y <ctype.h>:

for (size_t i = 0; s[i] != '\0'; i++) {
    s[i] = (char) toupper((unsigned char) s[i]);
}

«Hello, World 42» se convierte en «HELLO, WORLD 42». La conversión conserva la coma, los espacios y los dígitos. La conversión a un tipo sin signo hace que cada byte almacenado sea un argumento válido para ctype.

Contar categorías e inicios de palabras

El texto original «Hello, World 42» tiene 15 bytes de texto: 10 letras, dos dígitos, dos espacios y una coma. Los contadores separados pueden usar isalpha, isdigit e isspace. Decide qué es una palabra antes de contarla. Para una secuencia de letras, una palabra nueva empieza cuando una letra sigue a un carácter que no es una letra o es el primer carácter:

int in_word = 0;
size_t words = 0;
for (size_t i = 0; s[i] != '\0'; i++) {
    int letter = isalpha((unsigned char) s[i]) != 0;
    if (letter && !in_word) words++;
    in_word = letter;
}

El ejemplo tiene dos secuencias de letras, «Hello» y «World». Los dígitos no forman una palabra según esta definición. Actualizar in_word en cada pasada permite que la puntuación termine una secuencia.

Invertir solo el texto

Para una cadena de un solo byte por carácter, obtén len una vez y luego intercambia s[i] con s[len - 1 - i] mientras i < len / 2. Invertir «stressed» intercambia su primera y última letras, luego el siguiente par, hasta que se convierte en «desserts». El byte cero permanece en el índice len. Empezar un intercambio en s[len] llevaría ese cero al principio. Invertir bytes de esta manera es adecuado para texto ASCII; los caracteres de varios bytes necesitan decodificarse antes de reorganizarlos.

Construir un resultado filtrado

Usa índices diferentes para leer y escribir, porque se omiten algunos bytes de entrada:

char in[] = "a1b2c3";
char out[sizeof in];
size_t j = 0;
for (size_t i = 0; in[i] != '\0'; i++) {
    if (isalpha((unsigned char) in[i])) {
        out[j++] = in[i];
    }
}
out[j] = '\0';

El resultado es «abc». El destino tiene espacio suficiente porque el filtrado no puede alargar esta entrada. Sin out[j] = '\0';, la salida no tiene un final de cadena definido.

Para un cifrado César, desplaza las letras mayúsculas con respecto a 'A' y las minúsculas con respecto a 'a', y luego copia los demás bytes sin cambios. Con la clave 13, «Hello, World!» se convierte en «Uryyb, Jbeyq!». Repetir ese desplazamiento restablece el original porque dos desplazamientos suman 26. Una comparación como s[i] == 'a' comprueba un carácter; usa strcmp para comparar texto completo.

Puntos clave

  • Comprueba el byte actual o guarda la longitud para evitar recorridos completos repetidos.
  • Mantén índices de lectura y escritura separados al filtrar.
  • Escribe un terminador después de construir una cadena nueva.
  • Invierte los bytes del texto sin mover el terminador.
  • Define un límite de palabra antes de escribir un contador de palabras.
  • Aplica los desplazamientos de letras ASCII por separado a mayúsculas y minúsculas.

2.4 Explicar cómo se almacena el texto UTF-8 en arrays de char

La longitud del texto puede referirse a un número de bytes o a un número de puntos de código Unicode. Estas cantidades suelen diferir. Un array de char de C expone bytes individuales, por lo que sus índices y las funciones de cadenas no siguen automáticamente caracteres completos que no sean ASCII.

Distinguir caracteres, puntos de código y bytes

Una codificación de caracteres describe cómo se almacena el texto. ASCII usa los códigos de 0 a 127 para letras básicas, dígitos, signos de puntuación y caracteres de control. Unicode asigna puntos de código a caracteres de muchos sistemas de escritura: «é» tiene el punto de código U+00E9 y «€» tiene U+20AC. UTF-8 codifica valores escalares de Unicode usando de uno a cuatro bytes.

Los puntos de código ASCII conservan sus codificaciones de un byte. La «é» precompuesta usa dos bytes, 0xC3 y 0xA9; «€» usa tres. Muchos puntos de código individuales de emojis usan cuatro bytes. Un símbolo mostrado puede contener varios puntos de código, por lo que incluso un recuento de puntos de código no siempre coincide con el número de símbolos visibles.

Medir una cadena UTF-8
char s[] = "café";
printf("%zu %zu\n", strlen(s), sizeof s);

Con texto de ejecución en UTF-8, esto imprime «5 6». Las tres primeras letras usan tres bytes y «é» usa dos, lo que suma cinco bytes de texto. El terminador añade el sexto byte de almacenamiento. s[3] es el primer byte de «é», no la letra completa.

Un corte después de cuatro bytes conservaría 0xC3, pero eliminaría su byte de continuación 0xA9. El resultado ya no sería UTF-8 válido. Por tanto, un límite de búfer expresado en bytes requiere comprobar los límites entre caracteres si el programa trunca texto UTF-8.

Contar los bytes iniciales en UTF-8 válido

Un byte de continuación empieza con los bits 10. Su valor va de 0x80 a 0xBF. Un byte inicial o un byte ASCII no tiene ese patrón:

size_t points = 0;
for (size_t i = 0; s[i] != '\0'; i++) {
    unsigned char c = (unsigned char) s[i];
    if ((c & 0xC0) != 0x80) points++;
}

La máscara 0xC0 conserva solo los dos bits más significativos; 0x80 representa el patrón 10 en esas posiciones. La comprobación cuenta «café» como cuatro puntos de código al contar sus cuatro bytes iniciales. Supone que el UTF-8 es válido y no valida secuencias mal formadas ni combina puntos de código en símbolos visibles.

Elegir operaciones que conservan la codificación

Copiar una cadena UTF-8 completa conserva sus bytes. Concatenar cadenas completas, comparar sus secuencias de bytes para comprobar su igualdad y buscar un carácter ASCII o una subcadena UTF-8 completa también funcionan. Los bytes de continuación UTF-8 no pueden confundirse con caracteres ASCII ni con bytes iniciales.

strcmp ordena valores de bytes sin signo, por lo que su orden difiere del orden alfabético específico de cada idioma. En UTF-8, las letras latinas acentuadas normales tienen bytes iniciales mayores que el de 'z' en ASCII. Por tanto, una comparación de bytes coloca esas letras después de 'z', independientemente de las convenciones del diccionario.

En la configuración regional «C» predeterminada, toupper e isalpha procesan un byte y no decodifican «é» como una letra. La conversión explícita a unsigned char hace válida la llamada, pero no puede convertirla en una conversión Unicode entre mayúsculas y minúsculas. Para esa tarea se necesitan funciones de caracteres anchos o una biblioteca Unicode. Un bucle de César limitado a ASCII puede conservar las letras acentuadas si solo modifica los bytes de A–Z o a–z.

El byte cero es la codificación UTF-8 de U+0000; los demás puntos de código codificados no contienen ningún byte cero. Esto se ajusta a la convención de terminación de C. UTF-16 puede incluir bytes cero dentro de texto ordinario codificado, por lo que un array de bytes UTF-16 no puede procesarse como una cadena normal de char.

Puntos clave

  • strlen cuenta bytes, también en texto UTF-8.
  • Un punto de código Unicode puede ocupar varios bytes.
  • Un índice de char puede apuntar al interior de un carácter codificado.
  • Contar los bytes que no son de continuación solo cuenta puntos de código en UTF-8 válido.
  • La igualdad byte a byte funciona para texto codificado idéntico; el orden de los bytes no es el orden del diccionario.
  • Las llamadas a ctype de un solo byte no decodifican UTF-8.
3

Utilizar las funciones estándar de cadenas

La biblioteca estándar proporciona operaciones habituales con texto, pero cada función espera un tipo de entrada concreto y un significado concreto para su argumento de tamaño. Aprende a medir, copiar, añadir, comparar y buscar cadenas, y luego elige el formato con límite o las operaciones con bytes cuando corresponda. Comprobar la capacidad e interpretar los valores de retorno convierte una llamada breve a una función en una operación predecible sobre un almacenamiento conocido.

3.1 Medir, copiar y concatenar cadenas

Las funciones de <string.h> trabajan con texto terminado en nulo. Pueden determinar su longitud, copiarlo o añadir más texto, pero las funciones de copia no averiguan la capacidad del destino. Calcula el almacenamiento necesario antes de escribir.

Medir la longitud sin el terminador

strlen(s) devuelve un size_t que indica el número de bytes anteriores al primer byte cero. Así, strlen("hello") es 5 y strlen("") es cero. Imprime el resultado con %zu.

char buf[64] = "hi";
printf("%zu %zu\n", strlen(buf), sizeof buf);

Esto imprime «2 64». El primer resultado mide el texto actual; el segundo mide el almacenamiento disponible en el array. Una copia posterior puede cambiar el primero sin cambiar el segundo. Ambas llamadas son útiles aquí porque buf es realmente un array y ya contiene una cadena válida.

Copiar una cadena completa

strcpy(dest, src) copia el texto y su byte cero final. La capacidad necesaria es la longitud del origen más uno:

char dest[12];
const char *src = "Maple";
if (strlen(src) < sizeof dest) {
    strcpy(dest, src);
}

«Maple» usa cinco bytes de texto y un terminador, por lo que cabe. La comparación estricta < es importante: un origen cuya longitud sea igual a la capacidad no deja espacio para su byte cero. Si la comprobación falla, el programa debe gestionar el error antes de usar el destino como una nueva cadena.

Una asignación a un array como dest = src; no puede reemplazar su contenido. Asignar una variable char * a otra es válido, pero solo copia una referencia al mismo almacenamiento. No crea otra copia de los caracteres.

Añadir texto a una cadena existente

strcat(dest, src) encuentra primero el terminador que ya está en dest. Reemplaza ese terminador por el primer byte del origen y escribe un nuevo terminador después del texto añadido. La capacidad del destino debe cubrir las dos longitudes actuales más uno.

char full[16];
strcpy(full, "Ada");
strcat(full, " ");
strcat(full, "Lovelace");

Las longitudes aumentan de 3 a 4 y a 12. El resultado final «Ada Lovelace» necesita 13 bytes de almacenamiento y deja tres bytes sin usar. Un destino de 12 bytes sería un byte demasiado pequeño, aunque coincida con la longitud del texto visible.

Nunca añadas texto a un búfer local sin inicializar. char out[20]; strcat(out, "hi"); no proporciona a strcat un final de texto definido que pueda encontrar. Empieza con char out[20] = "";, escribe out[0] = '\0'; o haz que la primera operación sea una copia comprobada.

Tener en cuenta el almacenamiento y el coste de los recorridos

El origen y el destino no deben solaparse en strcpy ni en strcat. Mover texto dentro del mismo búfer puede crear un solapamiento aunque ambas ubicaciones sean válidas por separado. memmove es la función destinada a regiones de bytes solapadas.

strlen debe recorrer la cadena hasta el terminador. strcat debe recorrer el destino actual antes de añadir texto. Por tanto, las adiciones repetidas en un bucle vuelven a recorrer texto que ya se ha construido. Al construir muchas partes, conserva la longitud actual de la salida y escribe cada parte en ese índice, comprobando la capacidad restante y manteniendo el terminador. Una longitud conocida solo es útil mientras siga coincidiendo con el contenido real.

Puntos clave

  • strlen mide el texto actual; sizeof aplicado a un array mide su capacidad.
  • Una copia necesita la longitud del origen más un byte.
  • Una concatenación necesita las longitudes de ambas cadenas más un byte.
  • Inicializa el destino como una cadena antes de añadir texto.
  • Copiar o añadir cadenas con regiones de memoria solapadas no es válido.

3.2 Comparar cadenas y realizar búsquedas en ellas

La comparación de cadenas responde preguntas sobre el texto, mientras que la búsqueda de cadenas encuentra una posición dentro de ese texto. Ninguna de las dos tareas se realiza comparando nombres de arrays con ==. Usa el resultado de la biblioteca según lo que represente: el signo de una comparación, una longitud o la ubicación de una coincidencia.

Comparar texto y prefijos

strcmp(a, b) compara los bytes correspondientes como valores unsigned char hasta encontrar una diferencia o el final de una cadena. Su resultado es cero para textos iguales, negativo cuando a va primero y positivo cuando va después. Solo se especifica el signo; no esperes exactamente -1 o 1.

if (strcmp(command, "quit") == 0) {
    puts("Goodbye");
}

La comparación explícita con cero significa igualdad. Una condición if (strcmp(command, "quit")) sin comparación entra cuando los textos son distintos, porque un valor distinto de cero es verdadero. Comparar command == "quit" compararía ubicaciones de almacenamiento.

En el orden ASCII, «apple» va antes que «banana» por la primera letra. «Zebra» va antes que «apple» porque la mayúscula 'Z' tiene el código 90 y la minúscula 'a' tiene el código 97. «app» va antes que «apple» porque se encuentra su byte cero antes que la 'l' de la otra cadena. Este es un orden de bytes, no una comparación de diccionario.

strncmp(a, b, n) compara como máximo n bytes. strncmp(s, "--", 2) == 0 reconoce un prefijo de dos guiones, pero no significa que toda la cadena sea solo «--». El C estándar no tiene una función para comparar cadenas completas sin distinguir mayúsculas de minúsculas. Un programa puede comparar por sí mismo los bytes convertidos; POSIX proporciona strcasecmp en <strings.h>.

Buscar una ubicación

Usa strchr para la primera aparición de un carácter, strrchr para la última y strstr para la primera subcadena completa. Devuelven un puntero a la ubicación de la coincidencia o NULL si no hay ninguna.

char name[] = "report.final.txt";
char *extension = strrchr(name, '.');
if (extension != NULL) {
    printf("%s\n", extension);
}

Esto imprime «.txt», incluido el punto encontrado. Empezar desde el primer punto con strchr imprimiría «.final.txt». strstr(name, "final") encuentra la parte final que empieza por «final.txt»; buscar «draft» devuelve NULL. Comprueba si el resultado es NULL antes de imprimirlo o leerlo.

Localizar un separador o dividir texto

strcspn(s, set) cuenta los bytes iniciales que no corresponden a ninguno de los caracteres de set. Con "oak:pine,birch" y el conjunto ":,", se detiene en los dos puntos. Para una cadena de entrada válida, este patrón elimina el primer salto de línea:

buf[strcspn(buf, "\n")] = '\0';

Si no hay salto de línea, el índice devuelto es la longitud de la cadena, por lo que la asignación vuelve a escribir el terminador que ya está allí.

strtok divide una cadena modificable usando cualquier carácter de un conjunto de delimitadores:

char colors[] = "red, green,blue";
char *token = strtok(colors, " ,");
while (token != NULL) {
    puts(token);
    token = strtok(NULL, " ,");
}

Los tokens son «red», «green» y «blue». La primera llamada proporciona el array; las llamadas posteriores pasan NULL para continuar. La función escribe bytes cero en la entrada y mantiene un estado oculto entre llamadas. Usa almacenamiento modificable y no esperes que la cadena original con sus separadores permanezca intacta. Los delimitadores consecutivos no producen tokens vacíos.

Puntos clave

  • Usa strcmp(a, b) == 0 para comprobar que los textos son iguales.
  • Interpreta el resultado de una comparación por su signo, no por su magnitud exacta.
  • Una coincidencia de prefijo no establece la igualdad de las cadenas completas.
  • Los resultados de búsqueda requieren comprobar si el puntero es nulo.
  • strcspn devuelve la longitud de un prefijo; strtok modifica su entrada y recuerda su posición.
  • La ordenación de cadenas sigue los códigos de los bytes sin signo.

3.3 Copiar sin exceder los límites y trabajar directamente con bytes de memoria

Un límite de longitud solo es útil cuando describe la cantidad correcta. Algunas funciones limitan los bytes de origen, otras la capacidad del destino, y las funciones de memoria manejan bytes directamente sin finales de cadena. Lee las condiciones de cada función antes de elegir su límite.

Saber qué significa n

strncpy(dest, src, n) escribe exactamente n posiciones cuando el origen es más corto, rellenando con bytes cero. Cuando el origen tiene al menos n bytes de texto, copia esos bytes sin terminador. Para un array de capacidad positiva, una copia truncada y terminada en nulo es:

strncpy(dest, src, sizeof dest - 1);
dest[sizeof dest - 1] = '\0';

La asignación final es esencial cuando el origen llena el límite. Truncar un nombre o comando aún puede cambiar su significado, así que rechaza las entradas demasiado largas cuando se necesite una copia completa.

strncat(dest, src, n) añade como máximo n bytes de texto y luego un terminador. No recibe la capacidad total. Para una cadena válida que ya está dentro de un array de destino, el espacio restante para texto es sizeof dest - strlen(dest) - 1. Pasar sizeof dest como n puede desbordarlo.

Dar formato y detectar el truncamiento

snprintf, declarada en <stdio.h>, recibe la capacidad total del destino. Si la capacidad es mayor que cero, escribe como máximo un byte de texto menos y después un terminador:

char buf[8];
int needed = snprintf(buf, sizeof buf, "%s-%d", "item", 2026);
if (needed < 0) {
    puts("Formatting error");
} else if ((size_t) needed >= sizeof buf) {
    puts("Text did not fit");
}

El resultado completo «item-2026» tiene nueve bytes de texto. El búfer almacena «item-20» y su byte cero, mientras que needed es 9. El valor de retorno cuenta el texto que se habría producido, sin incluir el terminador. Comprueba si el resultado es negativo antes de convertirlo a size_t.

Copiar y rellenar bytes

memcpy(dest, src, n) copia exactamente n bytes, incluidos los bytes cero, sin buscar un terminador. Para dos arrays de enteros del mismo tamaño, memcpy(b, a, sizeof a) copia todo su almacenamiento cuando las regiones no se solapan. Donde cada uno de cinco enteros ocupa cuatro bytes, esa cantidad es 20 bytes. memmove copia bytes con un comportamiento definido para regiones solapadas.

memset(a, 0, sizeof a) rellena cada byte con cero y pone a cero un array de enteros. Su segundo argumento describe cada byte, no cada elemento. Con enteros de cuatro bytes, rellenar con 1 produce los bytes 01 01 01 01, lo que da 16 843 009 en la representación habitual, no el entero 1. Usa un bucle para asignar valores enteros distintos de cero.

memcmp(a, b, n) compara los bytes almacenados. Cero significa que esas secuencias de bytes coinciden. Para comprobar la igualdad de valores, compara los elementos: los valores de coma flotante 0.0 y -0.0 se consideran iguales al compararlos, aunque sus representaciones pueden diferir.

Reconocer otras familias de bibliotecas

strlcpy y strlcat de BSD se incorporaron a POSIX en 2024, pero no son funciones principales de C17. Devuelven la longitud que intentaron crear; un resultado al menos tan grande como el tamaño proporcionado indica truncamiento. strlcpy termina la cadena cuando el tamaño es positivo. strlcat solo la termina si el texto existente acaba dentro de ese tamaño. Están disponibles en glibc 2.38 y posteriores. Las funciones opcionales del anexo K, como strcpy_s, no están presentes en muchas bibliotecas, incluida glibc.

Puntos clave

  • strncpy puede omitir el terminador; strncat necesita espacio para uno nuevo.
  • snprintf devuelve la longitud completa del texto necesario o un valor negativo si hay un error.
  • Un resultado de formato no negativo al menos tan grande como la capacidad indica truncamiento.
  • Las funciones de memoria cuentan bytes y no se detienen en cero.
  • Usa memmove para regiones solapadas.
  • memset rellena bytes; memcmp compara representaciones.
4

Organizar datos en arrays multidimensionales

Las tablas organizan valores mediante más de una posición. Una tabla de C almacena filas de igual ancho en secuencia, y los bucles anidados permiten que un programa trabaje por filas o por columnas. Aprende a declarar esa estructura, pasarla a una función y mantener ambos índices dentro de los límites. Las listas de texto presentan una elección de diseño útil: las filas de caracteres modificables reservan un espacio fijo, mientras que los arrays de punteros apuntan a cadenas almacenadas en otro lugar.

4.1 Declarar y recorrer arrays bidimensionales

Una tabla necesita un índice de fila y un índice de columna. En C, un array bidimensional es un array cuyos elementos son a su vez arrays. Cada fila tiene el mismo número de elementos y cada índice tiene su propio límite.

Declarar filas y columnas
int grid[3][4];
int m[2][3] = {{1, 2, 3}, {4, 5, 6}};

grid contiene tres filas con cuatro enteros cada una: doce enteros en total. Sus índices de fila van de 0 a 2; sus índices de columna van de 0 a 3. grid[1][2] accede al tercer valor de la segunda fila.

Las llaves de inicialización anidadas muestran la agrupación por filas. Los elementos cuyo valor de inicialización falta pasan a ser cero. Puedes dejar que el compilador cuente las filas en int m[][3] = {{1, 2, 3}, {4, 5, 6}};, pero debes proporcionar el número de columnas. Sin él, el compilador no podría determinar dónde termina cada fila.

Seguir el almacenamiento por filas

C almacena una fila completa antes de empezar la siguiente. En grid, los primeros cuatro enteros almacenados son la fila 0, los cuatro siguientes son la fila 1 y los últimos cuatro son la fila 2. grid[1][2] es el séptimo entero desde el inicio, en la posición de elemento 6 si se cuenta desde cero: una fila completa de cuatro y luego dos elementos más.

Donde los enteros ocupan cuatro bytes, una fila usa dieciséis bytes y toda la tabla usa cuarenta y ocho. En el lugar de la declaración:

size_t rows = sizeof grid / sizeof grid[0];
size_t cols = sizeof grid[0] / sizeof grid[0][0];

La primera división da 3 y la segunda da 4. Una fila es un array por derecho propio, por eso sizeof grid[0] mide más de un entero.

Recorrer y sumar la tabla
int sales[2][3] = {{4, 0, 7}, {5, 2, 1}};
int total = 0;
for (int r = 0; r < 2; r++) {
    int row_sum = 0;
    for (int c = 0; c < 3; c++) {
        row_sum += sales[r][c];
    }
    printf("%d\n", row_sum);
    total += row_sum;
}

La primera fila produce 11 y la segunda 8. Sumar esos totales por fila da 19. Restablecer row_sum dentro del bucle exterior inicia un total nuevo para cada fila. Si se inicializara una sola vez antes de ambos bucles, el segundo valor impreso también incluiría la primera fila.

Para los totales por columna, coloca el bucle de columnas fuera y el de filas dentro, accediendo igualmente a sales[r][c]. Los tres totales pasan a ser 9, 2 y 8, que suman de nuevo 19. Cambiar el orden del recorrido no exige cambiar el significado de los dos índices. Recorrer primero por filas sigue el almacenamiento contiguo y suele mejorar el uso de la caché en tablas grandes.

Mantener cada dimensión dentro de sus límites

grid[c][r] intercambia el significado de los índices. Si c llega a 3, solicita una cuarta fila que no existe. El hecho de que el objeto completo contenga doce enteros no permite superar el límite de una fila con un índice de columna.

Escribe dos operaciones de indexación separadas: grid[1][2]. En C17, grid[1, 2] evalúa la expresión con coma y selecciona grid[2], una fila, en lugar de un elemento. Más dimensiones aplican la misma idea repetidamente: int cube[2][3][4] tiene 24 elementos enteros, seleccionados mediante tres índices.

Puntos clave

  • Un array bidimensional es un array de filas de igual ancho.
  • Las filas se almacenan una detrás de otra en orden por filas.
  • Mantén separados los límites de filas y columnas.
  • Restablece un acumulador de fila o columna al iniciar un grupo nuevo.
  • Solo se puede deducir la primera dimensión de un inicializador.

4.2 Pasar tablas a funciones y almacenar listas de cadenas

Una función que recibe una tabla necesita información suficiente para identificar cada fila. Una lista de cadenas añade otra elección: almacenar los caracteres en filas de igual ancho o almacenar referencias a cadenas de distintas longitudes.

Indicar el ancho de fila en un parámetro de tabla
void double_grid(int rows, int g[][4]) {
    for (int r = 0; r < rows; r++) {
        for (int c = 0; c < 4; c++) {
            g[r][c] *= 2;
        }
    }
}

La primera dimensión puede omitirse porque el número de filas se pasa por separado. El ancho 4 define el tipo de cada fila. Sin él, g[r][c] no tendría una separación conocida entre filas. Una declaración con int g[][] no es válida. Los cambios realizados mediante este parámetro permanecen en la tabla de la función llamadora; un parámetro de tabla de solo lectura puede usar const int g[][4].

Desde C99, los compiladores que admiten parámetros de tipo modificado variablemente aceptan dimensiones declaradas anteriormente en la lista de parámetros:

void print_grid(int rows, int cols, int g[rows][cols]);

GCC y Clang admiten esta forma de C17. Permite que una función reciba distintos anchos de fila; pasa dimensiones positivas que coincidan con la tabla real. C23 exige compatibilidad con estos tipos de parámetros, mientras que los objetos automáticos de tipo array de longitud variable siguen siendo una característica opcional independiente. Los parámetros de ancho fijo son útiles cuando la compatibilidad del compilador es limitada.

Almacenar cadenas en filas
char days[7][10] = {
    "Monday", "Tuesday", "Wednesday", "Thursday",
    "Friday", "Saturday", "Sunday"
};

Cada fila tiene almacenamiento para diez caracteres, independientemente de la longitud de su texto actual. «Wednesday» necesita las diez posiciones: nueve letras y un terminador. days[2] selecciona toda esa fila como una cadena; days[2][0] selecciona 'W'.

Una fila se puede imprimir con %s, medir con strlen o comparar con strcmp. También puede recibir una copia comprobada. strcpy(days[0], "Mon"); cabe en diez bytes y modifica el texto de la fila 0. La fila sigue teniendo diez bytes de ancho y la siguiente permanece en la misma ubicación. Un texto de reemplazo de diez bytes necesitaría once bytes y no cabría.

Almacenar referencias a literales
const char *names[] = {"Ann", "Bartholomew", "Li"};

Esto declara un array de punteros. Cada elemento apunta a un literal con su propia longitud más un terminador. names[1] apunta a «Bartholomew» y names[1][0] es 'B'. Los literales no deben modificarse.

Los elementos de tipo puntero pueden cambiar aunque los caracteres de los literales no puedan hacerlo. names[0] = "Anna"; cambia el literal al que apunta el primer elemento; no amplía «Ann» en el mismo espacio de almacenamiento. En la forma de filas de 2 dimensiones, un elemento es un array de caracteres de ancho fijo y no puede reasignarse de esa manera. Elige filas modificables para texto que se edita en el mismo espacio de almacenamiento y referencias a literales para una lista de opciones de texto fijo. El parámetro de la línea de comandos argv tiene forma de array de punteros, aunque los caracteres de los argumentos proporcionados son modificables.

Puntos clave

  • Los parámetros de tabla necesitan todas las dimensiones posteriores a la primera.
  • Una dimensión pasada por separado debe coincidir con la tabla real.
  • Cada fila de cadena necesita su propio terminador dentro de su ancho fijo.
  • Un array de punteros almacena referencias, no filas de caracteres de igual ancho.
5

Procesar argumentos de la línea de comandos

Una línea de comandos proporciona texto a un programa al iniciarse. El número de argumentos, la agrupación del intérprete de comandos y las operaciones con cadenas permiten que el programa interprete ese texto sin suponer que existe un argumento o que ya contiene un número. Sigue un proceso con comprobaciones desde argv hasta un valor utilizable: verifica su presencia, conviértelo por completo, detecta errores de conversión y aplica el rango que el programa acepta realmente.

5.1 Leer argc y argv

Los argumentos de la línea de comandos permiten que quien llama a un programa le proporcione texto al iniciarlo. El entorno de ejecución pone esas cadenas a disposición de main. Comprueba cuántas llegaron antes de acceder a ellas mediante índices.

Leer la lista de argumentos
int main(int argc, char *argv[]) {
    for (int i = 1; i < argc; i++) {
        printf("%d: %s\n", i, argv[i]);
    }
    return 0;
}

Incluye <stdio.h> para la salida. argc es el número de argumentos y argv es la lista de cadenas. La forma equivalente del parámetro char **argv describe la misma interfaz. El bucle empieza en 1 porque el índice 0 normalmente contiene el nombre con el que se inició el programa.

En un intérprete de comandos habitual, ejecutar ./greet Ada Lovelace proporciona tres cadenas: «./greet», «Ada» y «Lovelace». Así, argc es 3 y el último argumento del usuario es argv[2]. Hay un puntero nulo en argv[argc], aquí argv[3]; es un valor centinela después de las cadenas de argumentos reales.

Dejar que el intérprete agrupe el texto

Las comillas afectan a cómo el intérprete construye los argumentos. Ejecutar ./greet "Ada Lovelace" proporciona un argumento del usuario que contiene un espacio. Sus comillas agrupan las palabras y no pasan a formar parte del texto del argumento. argc es ahora 2.

Los intérpretes de tipo Unix también pueden expandir patrones antes de que comience el programa. En ./count *.txt, el programa suele recibir como argumentos separados los nombres que coinciden con el patrón según el intérprete. Recibe las cadenas resultantes, no una petición para expandir el patrón por sí mismo. El comportamiento exacto de la expansión depende del intérprete y de su configuración.

Rechazar la falta o el exceso de argumentos

Si un programa necesita exactamente un argumento del usuario, exige argc == 2 antes de usarlo:

if (argc != 2) {
    printf("Usage: %s name\n", argv[0]);
    return 1;
}
printf("Hello, %s\n", argv[1]);

Cuando argc es 1, argv[1] es el puntero nulo centinela y no puede pasarse a strlen ni imprimirse con %s. Los índices posteriores al centinela están fuera del array de argumentos.

Devolver cero desde main indica éxito. Un estado distinto de cero, como el 1 anterior, indica al intérprete que el programa no terminó correctamente. <stdlib.h> también proporciona EXIT_SUCCESS y EXIT_FAILURE.

Tratar cada argumento como una cadena

argv[i][0] accede al primer carácter del argumento; strlen(argv[i]) mide sus bytes de texto; strcmp(argv[i], "-v") == 0 comprueba una opción completa. Un argumento que parece numérico sigue siendo texto y debe convertirse antes de realizar operaciones aritméticas.

argv[0] suele registrar cómo se inició el programa, pero puede estar vacío cuando esa información no está disponible. Es útil para mensajes, no como comprobación de identidad fija. Los caracteres de los argumentos pueden modificarse durante la ejecución del programa, pero el almacenamiento proporcionado no permite suponer que quepa un reemplazo más largo. Para añadir una extensión o alargar el texto de otro modo, cópialo primero en un búfer de tamaño adecuado.

Puntos clave

  • argc cuenta las cadenas de argumentos proporcionadas, incluida la entrada habitual con el nombre de inicio.
  • Comprueba el número de argumentos antes de leer un argumento del usuario.
  • argv[argc] es un puntero nulo, no una cadena de argumento vacía.
  • Las comillas del intérprete pueden mantener espacios dentro de un argumento.
  • Usa operaciones de cadenas para el texto de los argumentos y conversión para los números.

5.2 Convertir y validar argumentos numéricos

Un argumento numérico llega como caracteres, no como un entero. Una conversión fiable comprueba tres cuestiones distintas: si algún dígito formó un número, si la conversión usó el texto requerido y si el valor resultante está permitido.

Evitar perder la información de error

atoi, declarada en <stdlib.h>, realiza una conversión sencilla. Convierte «13» en 13, pero también convierte «12abc» en 12. Devuelve cero tanto para «0» como para texto que no empieza por un número, como «abc». Un resultado fuera de rango produce comportamiento indefinido, por lo que una comprobación de solo dígitos seguida de atoi sigue siendo insegura para entradas muy largas.

Una regla de formato como «solo uno o más dígitos» necesita un recorrido separado. Rechaza una cadena vacía y exige que cada byte cumpla isdigit((unsigned char) c). Esa regla acepta «13» y rechaza «13a», «-5» y «». Comprueba la forma del texto, no si el valor cabe en un entero de la máquina.

Usar strtol y examinar dónde se detuvo
#include <errno.h>
#include <stdlib.h>

char *end;
errno = 0;
long count = strtol(argv[1], &end, 10);
if (end == argv[1] || *end != '\0' || errno == ERANGE) {
    puts("Enter a whole number");
    return 1;
}
if (count < 1 || count > 100) {
    puts("Count must be from 1 to 100");
    return 1;
}

Este fragmento va después de una comprobación del número de argumentos y usa <stdio.h> para puts. El 10 selecciona la conversión decimal. end recibe la ubicación donde se detuvo la conversión; &end permite que la función almacene esa ubicación. *end lee el carácter que hay allí. No se necesita aritmética de punteros.

Para «13a», el valor devuelto es 13, pero *end es 'a', por lo que se rechaza el texto. Para una cadena vacía o «abc», end es igual al inicio del argumento, lo que muestra que no se procesó ningún número. Ambas comprobaciones son necesarias: comprobar solo el cero final aceptaría por error una cadena vacía.

Antes de cada conversión, restablece errno a cero. Un valor fuera del rango de long hace que strtol devuelva un extremo del rango y establezca ERANGE. Sin el restablecimiento, un error anterior podría parecer asociado a esta conversión. Comprueba antes de convertir el resultado a int; usa INT_MIN e INT_MAX de <limits.h> cuando se acepte todo el rango de int.

Aplicar la regla propia del programa

strtol acepta caracteres en blanco iniciales y un signo opcional. Decide si se permiten esas formas. Si la interfaz exige solo dígitos, realiza el recorrido para comprobar que no esté vacía y que solo contenga dígitos, además de las comprobaciones de conversión. Después de una conversión correcta, comprueba el rango permitido por la aplicación antes de usar un valor como cantidad o índice. «0» puede ser texto numérico perfectamente válido y aun así no ser válido para una cantidad que deba ser positiva.

Un programa de César que acepte una clave no negativa comprueba primero argc, luego su formato de solo dígitos y después el resultado completo de strtol y el rango. Reducir una clave válida con % 26 produce un desplazamiento de 0 a 25 antes de las operaciones con letras. La clave 27 tiene el efecto de la clave 1. Comprueba el rango antes de una conversión explícita a un tipo de menor rango o de cualquier operación que pueda desbordarse. Para valores decimales, strtod proporciona la interfaz correspondiente para la posición final y el error de rango.

Puntos clave

  • Los argumentos que parecen numéricos aún necesitan conversión.
  • atoi no puede distinguir un cero válido de un fallo de conversión.
  • Comprueba que strtol haya leído algo y procesado todo el texto requerido.
  • Restablece errno, comprueba ERANGE y luego comprueba el rango permitido por la aplicación.
  • Un recorrido para comprobar que solo hay dígitos no descarta el desbordamiento.
  • Valida un valor antes de convertirlo a un tipo de menor rango o usarlo como índice.
6

Prevenir accesos fuera de los límites y desbordamientos de búfer

Los errores de límites relacionan varias ideas anteriores: un bucle puede dar una pasada de más, el texto puede necesitar un byte adicional y una función puede perder la referencia de la capacidad de un array. Aprende a identificar el acceso no válido antes de predecir sus efectos. Luego lee y copia la entrada dentro de límites conocidos, comprobando por separado el éxito y que esté completa para que un búfer con capacidad suficiente contenga los datos que el programa pretendía aceptar.

6.1 Reconocer los accesos fuera de los límites y sus efectos

Todo acceso a un array necesita un índice válido. C no exige una comprobación de límites en tiempo de ejecución, por lo que el programador debe mantener el índice dentro del objeto. Un acceso fuera de su rango tiene comportamiento indefinido: el lenguaje no garantiza ningún resultado.

Reconocer un acceso no válido

Para int a[5], los índices válidos van de 0 a 4. El índice 5 ya es demasiado grande y -1 es negativo. Un compilador puede diagnosticar un error visible, pero muchos índices no válidos dependen de la entrada en tiempo de ejecución y no se rechazan durante la compilación.

Un programa defectuoso puede parecer que funciona, cerrarse inesperadamente o producir valores imprevistos. Una escritura puede corromper el estado cercano; el síntoma puede aparecer después en un cálculo sin relación. Una lectura puede acceder a bytes fuera del objeto previsto. Ninguna de las dos operaciones tiene un resultado fiable. Repetir una ejecución correcta no puede establecer que el acceso sea válido.

Contar cada escritura, incluido el terminador
char name[8];
strcpy(name, "Christopher");

«Christopher» tiene once bytes de texto. Copiarlo también escribe el terminador, por lo que esta llamada intenta escribir doce bytes en ocho posiciones. Eso es un desbordamiento de búfer. En sistemas reales, un desbordamiento puede dañar otros datos o información de control. Históricamente, ataques como el gusano Morris de 1988 aprovecharon esos defectos en el manejo de entradas.

Un desbordamiento puede ser de un solo byte:

char s[5];
for (int i = 0; i <= 5; i++) {
    s[i] = 'x';
}

El cuerpo se ejecuta seis veces y escribe en el índice 5. Cambiar la condición a i < 5 mantiene cinco escrituras de caracteres dentro del array, pero sigue sin crear una cadena. Para una cadena de cuatro caracteres 'x', escribe solo en los índices de 0 a 3 y luego almacena '\0' en el índice 4.

Validar índices calculados

Una tabla de frecuencias puede convertir un carácter en un índice, pero la resta por sí sola no garantiza el rango:

int counts[26] = {0};
if (c >= 'a' && c <= 'z') {
    counts[c - 'a']++;
}

Con el orden ASCII, esa comprobación limita el resultado de 0 a 25. Para 'A' o un byte de puntuación, usar c - 'a' sin la comprobación podría crear un índice negativo. Comprueba el valor original antes de acceder mediante el índice; comprobarlo después de escribir es demasiado tarde.

Para una tabla, valida la fila y la columna por separado. En int g[2][3], un índice de columna de 3 no es válido aunque la siguiente fila esté contigua en memoria. El almacenamiento de la tabla completa no permite superar el límite de una fila.

Usar comprobaciones adecuadas para cada operación

Un bucle sobre un array usa un límite superior estricto; añadir un elemento exige comprobar el número de elementos utilizados frente a la capacidad; copiar una cadena exige comprobar la longitud del texto más uno. Las funciones reciben las longitudes o capacidades reales de sus funciones llamadoras. Estas comprobaciones se refieren a cantidades diferentes, por lo que un solo tamaño fijo no puede sustituirlas a todas.

Compila con advertencias como -Wall -Wextra. La opción -fsanitize=address -g de GCC y Clang puede informar de muchos accesos no válidos en las rutas ejecutadas y mostrar la ubicación en el código fuente. Estas pruebas ayudan a encontrar errores, pero las rutas no ejecutadas siguen requiriendo razonar sobre los límites. Revisa cada lectura y escritura mediante un índice identificando su objeto, su límite y la condición que mantiene válido ese índice.

Puntos clave

  • Las lecturas y escrituras fuera de los límites tienen comportamiento indefinido.
  • Una ejecución correcta no demuestra que un índice sea válido.
  • Una cadena copiada incluye su terminador en el número de bytes escritos.
  • Valida los datos antes de obtener de ellos un índice de array.
  • Comprueba cada dimensión de una tabla por separado.
  • Las advertencias y los sanitizadores ayudan a detectar defectos en las rutas probadas.

6.2 Leer y copiar texto sin exceder los límites del búfer

La longitud de la entrada la controla quien la proporciona. Indica a las funciones de lectura un límite real del búfer y comprueba lo que devuelven. Luego decide si los datos forman una línea completa y si cumplen las reglas propias del programa.

Leer una palabra con un ancho de campo

Para char word[16];, scanf("%15s", word) lee como máximo quince caracteres que no sean caracteres en blanco y añade '\0'. El ancho deja una de las dieciséis posiciones para el terminador. %s sin ancho no tiene límite de longitud; gets tenía el mismo problema fundamental para las líneas y se eliminó de C en C11.

Comprueba que scanf devuelva 1 antes de usar una palabra. Lee una palabra, no una línea completa con espacios. Los bytes sobrantes de la palabra pueden quedar sin leer, por lo que un ancho protege el almacenamiento sin determinar si toda la entrada es aceptable.

Leer una línea con fgets
char buf[8];
if (fgets(buf, sizeof buf, stdin) == NULL) {
    return 1;
}

Aquí fgets lee como máximo siete caracteres y escribe un byte cero final. Conserva un salto de línea cuando llega a él antes del límite. Una línea que contiene «hello» y un salto de línea se convierte en "hello\n" más el terminador. Una línea final que termina al llegar al fin de archivo sin salto de línea también puede leerse correctamente. Si no se leyó ningún carácter antes del fin de archivo o se produjo un error de lectura, el resultado es NULL; no proceses ese búfer como entrada nueva.

Distinguir un salto de línea almacenado de una línea parcial

Busca un salto de línea antes de eliminarlo:

size_t end = strcspn(buf, "\n");
int had_newline = buf[end] == '\n';
buf[end] = '\0';

Incluye <string.h>. Si no se almacenó ningún salto de línea, end es la longitud del texto y la asignación vuelve a escribir el terminador existente. La ausencia de un salto de línea puede significar una línea parcial, que el búfer se llenó exactamente antes de un salto de línea o que una línea final terminó en el fin de archivo. Por sí sola no demuestra que queden caracteres por leer.

Para la línea de entrada «abcdefghij» y un búfer de ocho bytes, la primera lectura almacena «abcdefg» y la siguiente puede leer «hij\n». Tratar el segundo fragmento como una respuesta separada dividiría una sola entrada lógica. Un programa puede rechazar una línea a la que le falte un salto de línea obligatorio, o leer y descartar el resto antes de volver a preguntar. Define la política de entrada antes de implementarla.

Para nombres de hasta treinta caracteres ASCII más un salto de línea y un terminador, usa 32 bytes. Para UTF-8, ese es un límite de bytes: treinta caracteres visibles pueden necesitar más almacenamiento.

Copiar solo después de comprobar todos los requisitos
char user[10];
if (argc != 2) return 1;
if (strlen(argv[1]) >= sizeof user) {
    puts("Name too long");
    return 1;
}
strcpy(user, argv[1]);

La comprobación del número de argumentos va primero. Después, la comprobación de longitud admite como máximo nueve bytes de texto y deja la décima posición para el cero. Una función que escribe debería recibir la capacidad explícitamente, como en read_name(out, capacity), porque sizeof out dentro de ella mide un parámetro de tipo puntero.

Para una entrada numérica, lee una línea completa y conviértela con strtol, comprobando la posición final, el error de rango y el rango de valores permitido. Una llamada fallida a scanf("%d", &n) puede dejar sin leer el texto problemático; repetirla sin comprobar puede fallar indefinidamente con los mismos bytes. Como mínimo, comprueba su número de asignaciones realizadas y gestiona la entrada no válida antes de reintentar.

Puntos clave

  • Establece el ancho de lectura de una palabra en una unidad menos que la capacidad del búfer.
  • Comprueba el resultado de una lectura antes de usar su búfer.
  • fgets solo conserva un salto de línea cuando llega a él dentro del límite.
  • La ausencia de un salto de línea no demuestra por sí sola que quede entrada por leer.
  • Reserva espacio para los bytes de texto, el salto de línea cuando se conserve y el terminador.
  • Comprueba que el argumento exista antes de medir y copiar su texto.
  • Pasa la capacidad del búfer a una función que escriba en él.

Ejemplos de Arrays y cadenas en C

6 Lecciones Interactivas

1

Seguir un array desde su declaración hasta la llamada a una función

13 pasos
Sigue los elementos inicializados, calcula un promedio y comparte los datos del array con una función.
  1. 1
    IntroducciónDar un nombre a varios valores

    Un array contiene un número fijo de elementos de un mismo tipo. Un bucle selecciona posiciones, mientras que una función puede trabajar con el mismo almacenamiento mediante un parámetro de tipo array.

    Sigue cuatro lecturas de un sensor desde su declaración hasta un total y un promedio. En cada paso, distingue tres cantidades: el número de elementos, el último índice válido y el número de bytes usados para el almacenamiento.

  2. 2
    EjemploLeer las posiciones iniciales
    int readings[4] = {11, 16, 13};
    

    Hay cuatro elementos, con índices de 0 a 3. El inicializador proporciona 11, 16 y 13 en las tres primeras posiciones. El valor omitido pasa a ser cero, por lo que el array es {11, 16, 13, 0}.

    El cero es una regla de inicialización, no una señal de que el array solo tenga tres elementos. Un array local sin inicializador necesitaría que se escribieran sus valores antes de usarlo.

  3. 3
    ExplicaciónContar los elementos en la declaración
    size_t n = sizeof readings / sizeof readings[0];
    

    El numerador mide los cuatro elementos y el denominador mide uno. Donde int ocupa cuatro bytes, esto es dieciséis dividido entre cuatro, lo que da 4. La misma expresión funciona con otro tamaño de entero.

    readings[3] es el último elemento. readings[4] está más allá del final aunque el número de elementos del array sea 4. size_t es el tipo sin signo usado para este resultado de tamaño.

  4. 4
    DesafíoCambiar la última lectura

    La declaración sigue siendo int readings[4] = {11, 16, 13};. ¿Qué asignación reemplaza el cero por 20 y qué número de elementos resulta?

    ---reveal---

    Usa readings[3] = 20;. Los valores resultantes son {11, 16, 13, 20} y el número de elementos sigue siendo 4. Una asignación a un elemento cambia un valor, no el número de posiciones. El índice 4 estaría una posición más allá del límite y no puede añadir almacenamiento a este array.

  5. 5
    EjemploConstruir el total pasada a pasada
    int sum = 0;
    for (size_t i = 0; i < n; i++) {
        sum += readings[i];
    }
    

    Con {11, 16, 13, 20}, la suma acumulada pasa a ser 11, 27, 40 y 60. Después del índice 3, i pasa a ser 4 y la condición deja de cumplirse antes de otro acceso.

    Empezar la suma en cero da al primer valor un punto de partida limpio. El límite superior estricto permite que el bucle visite exactamente los cuatro elementos existentes.

  6. 6
    Idea claveConservar la parte fraccionaria de un promedio

    Para estas cuatro lecturas, (double) sum / n da 15,0. La conversión explícita ocurre antes de la división, por lo que la operación puede producir un resultado fraccionario cuando cambie el total.

    Si el total fuera 61, la división entera sum / 4 produciría 15, mientras que sum / 4.0 produciría 15,25. Asignar a un double el resultado entero ya truncado solo produciría 15,0; no recuperaría la fracción descartada.

  7. 7
    ConsejoElegir un candidato inicial válido

    La búsqueda de un máximo necesita un valor inicial tomado de un array no vacío. Para {-6, -2, -9}, inicializa max en -6 y luego reemplázalo por -2 cuando aparezca ese valor mayor. La última comparación deja -2 sin cambios.

    Inicializar max en cero devolvería cero, que no está presente. Para una búsqueda lineal, la regla de parada correspondiente también importa: una diferencia no demuestra que la búsqueda haya fallado; aún hay que comprobar los elementos posteriores.

  8. 8
    DesafíoMantener la búsqueda en marcha

    Una búsqueda de 13 comprueba {11, 16, 13, 20}. Después de comprobar el índice 0 devuelve -1 porque 11 es distinto de 13. ¿Qué debería cambiar y dónde está la coincidencia?

    ---reveal---

    Sigue comprobando después de una diferencia y devuelve -1 solo cuando fallen todos los candidatos. Aquí el índice 1 también es diferente, pero el índice 2 coincide. Una búsqueda exitosa puede detenerse ahí. La primera comparación fallida describe un elemento, no todo el array.

  9. 9
    ConexiónTrasladar el promedio a una función
    double mean(const int a[], size_t n);
    

    El parámetro de tipo array comparte el acceso a los elementos de la función llamadora. No lleva consigo su número, por lo que el segundo parámetro lo proporciona. const impide escrituras accidentales mediante a en esta operación de solo lectura.

    Calcula el número de elementos donde se declara readings y llama a mean(readings, n). Dentro de mean, sizeof a mide un parámetro de tipo puntero en lugar del array de cuatro elementos de la función llamadora.

  10. 10
    EjemploGestionar el rango vacío antes de dividir
    double mean(const int a[], size_t n) {
        if (n == 0) return 0.0;
        int sum = 0;
        for (size_t i = 0; i < n; i++) sum += a[i];
        return (double) sum / n;
    }
    

    Para las cuatro lecturas pequeñas, el total sigue siendo 60 y el resultado es 15,0. Para n igual a cero, el resultado elegido es 0,0 y no se produce ningún acceso al array ni división. La función llamadora debe pasar un número de elementos utilizables preciso.

  11. 11
    DesafíoExplicar un cambio en el array de la función llamadora

    Otra función recibe int a[], size_t n y establece en cero cada a[i] visitado. ¿Qué ocurre con las lecturas de la función llamadora y cómo podría una función que solo calcula el total impedir esas escrituras?

    ---reveal---

    Los elementos de la función llamadora pasan a ser cero porque la función escribe en el almacenamiento compartido del array. Sus cuatro posiciones siguen existiendo. Una función que solo calcula el total debería usar const int a[], lo que invalida las asignaciones a elementos mediante a. Cambiar una copia escalar de n no cambiaría una variable de cantidad de la función llamadora.

  12. 12
    DesafíoUsar solo las lecturas que existen

    Un búfer tiene capacidad 10, pero solo se usan cuatro lecturas. Hay que añadir una lectura nueva antes de calcular un promedio. ¿Qué comprobaciones y qué número de elementos debería usar el programa?

    ---reveal---

    Comprueba que el número de elementos utilizados, 4, sea menor que la capacidad 10. Escribe en el índice 4 y luego actualiza el número de elementos a 5. Pasa ese número de elementos utilizados a mean para que procese exactamente los cinco valores. Pasar la capacidad incluiría posiciones que no se han proporcionado como lecturas.

  13. 13
    RepasoRelacionar cada cantidad con su función

    Una declaración crea las posiciones, un bucle visita las utilizadas y una función recibe explícitamente el número de elementos utilizables. Un parámetro de solo lectura evita que los cálculos sobrescriban la entrada.

    Al revisar código con arrays, sigue el primer y el último índice visitados y el estado de cada acumulador. Para bucles hacia atrás con size_t, empieza en n, continúa mientras i sea positivo y accede a a[i - 1] para que un rango vacío no requiera restar a cero.

2

Realizar operaciones de texto con los bytes almacenados

16 pasos
Construye y transforma una cadena terminada en nulo y luego sigue un carácter UTF-8 de varios bytes.

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

  1. 1
    IntroducciónObservar tanto el texto como el almacenamiento
  2. 2
    ExplicaciónLeer un carácter como código
  3. 3
    DesafíoVolver al inicio dentro del alfabeto
  4. 4
    EjemploConstruir una cadena modificable
  5. 5
    Idea claveMover el final sin mover el almacenamiento
  6. 6
    DesafíoDejar una posición para el terminador
  7. 7
    EjemploSeparar la lectura de la escritura
  8. 8
    EjemploTerminar la cadena nueva
  9. 9
    DesafíoInvertir letras sin el terminador
  10. 10
    ConsejoClasificar bytes con argumentos válidos
  11. 11
    EjemploDistribuir una letra acentuada en UTF-8
  12. 12
    ExplicaciónReconocer los bytes de continuación
  13. 13
    DesafíoMedir una nueva palabra codificada
  14. 14
    ConexiónElegir operaciones que mantengan los caracteres completos
  15. 15
    DesafíoComprobar un atajo propuesto para el texto
  16. 16
    RepasoLocalizar el final y los límites de los caracteres
3

Crear, buscar y dar formato al texto de forma segura

14 pasos
Comprueba capacidades, interpreta resultados de búsqueda y gestiona el truncamiento y el solapamiento.

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

  1. 1
    IntroducciónIdentificar las condiciones de cada función
  2. 2
    EjemploEmpezar con una copia completa
  3. 3
    EjemploAñadir texto en el final existente
  4. 4
    DesafíoDecidir si cabe otra concatenación
  5. 5
    ExplicaciónUsar cero para reconocer textos iguales
  6. 6
    EjemploLeer desde una ubicación encontrada
  7. 7
    DesafíoExaminar una comprobación de comando basada en un prefijo
  8. 8
    ConsejoTener en cuenta que la división en tokens cambia el texto
  9. 9
    EjemploDar formato dentro de una capacidad conocida
  10. 10
    DesafíoInterpretar el mismo formato con menos espacio
  11. 11
    Idea claveUn límite puede contar bytes de origen
  12. 12
    EjemploMover una región solapada
  13. 13
    DesafíoConservar un resultado completo después de desplazarlo
  14. 14
    RepasoDistinguir los bytes de los resultados de texto
4

Leer una tabla y elegir cómo almacenar una lista de cadenas

10 pasos
Sigue los totales por fila y columna, pasa una tabla y reserva espacio para cada cadena.

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

  1. 1
    IntroducciónDar dos índices a cada posición
  2. 2
    EjemploColocar valores en filas
  3. 3
    EjemploRestablecer el total de una fila al comenzarla
  4. 4
    DesafíoCambiar la agrupación y conservar los índices
  5. 5
    ExplicaciónIndicar a una función el ancho de fila
  6. 6
    EjemploReservar una cadena terminada en nulo por fila
  7. 7
    Idea claveLas referencias permiten cadenas de diferentes longitudes
  8. 8
    DesafíoReemplazar texto en el almacenamiento adecuado
  9. 9
    DesafíoMantener válidas ambas dimensiones en una función
  10. 10
    RepasoMantener visible la forma de la tabla
5

Validar una selección de la línea de comandos

11 pasos
Interpreta argumentos agrupados y convierte un número comprobado en una selección utilizable.

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

  1. 1
    IntroducciónRecibir el texto antes de interpretarlo
  2. 2
    EjemploRelacionar un comando del intérprete con sus entradas
  3. 3
    ExplicaciónComprobar el número de argumentos antes de convertir
  4. 4
    DesafíoAgrupar correctamente un nombre
  5. 5
    ConsejoUn entero devuelto no es suficiente
  6. 6
    EjemploExaminar una conversión completa
  7. 7
    ExplicaciónComprobar el rango propio de la selección
  8. 8
    DesafíoDetectar texto que parece parcialmente numérico
  9. 9
    Idea claveEl formato y el valor siguen reglas distintas
  10. 10
    DesafíoCombinar la agrupación y la validación numérica
  11. 11
    RepasoComprobar la presencia, la conversión y el propósito
6

Corregir una rutina de entrada con límites

13 pasos
Sigue los fragmentos de una línea, conserva espacio para el terminador y copia solo un resultado completo y aceptado.

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

  1. 1
    IntroducciónLimitar la escritura y comprobar el resultado
  2. 2
    EjemploEncontrar la escritura adicional
  3. 3
    Idea claveLa última posición válida puede contener el terminador
  4. 4
    DesafíoComprobar un índice obtenido de un byte
  5. 5
    ExplicaciónLeer una palabra dejando espacio para el terminador
  6. 6
    EjemploLeer un fragmento de línea con límite
  7. 7
    EjemploObservar dónde se divide una línea larga
  8. 8
    ConsejoExaminar un salto de línea antes de eliminarlo
  9. 9
    DesafíoCalcular el espacio para una línea completa
  10. 10
    EjemploComprobar una copia antes de escribir
  11. 11
    DesafíoDistinguir un prefijo seguro de una respuesta aceptada
  12. 12
    ConexiónUsar diagnósticos y razonar sobre los límites
  13. 13
    RepasoMantener separadas cuatro comprobaciones

Ejercicios de Arrays y cadenas en C

60 Preguntas de Práctica en 6 sets

Set 1: Valores de arrays, bucles y llamadas a funciones10 Preguntas de Práctica

Pregunta 1 de 10

¿Qué valor se almacena en a[3] después de int a[5] = {6, 9};?
  1. 6
  2. 9
  3. 0
  4. Un valor indeterminado
Mostrar respuestaOcultar respuesta
Respuesta: C
El inicializador proporciona los dos primeros valores y C inicializa los elementos restantes a cero. Por tanto, el índice 3 contiene 0. Los valores indeterminados serían un problema en un array local declarado sin inicializador, que es distinto de este array parcialmente inicializado.

Pregunta 2 de 10

Se declara un array como int a[7]; donde int ocupa 4 bytes. ¿Qué pareja indica sizeof a y el último índice válido?
  1. 28 bytes; índice 7
  2. 28 bytes; índice 6
  3. 7 bytes; índice 6
  4. 24 bytes; índice 6
Mostrar respuestaOcultar respuesta
Respuesta: B
Siete elementos de cuatro bytes ocupan 28 bytes y, como los índices empiezan en cero, el último es 6. El último índice es una unidad menor que el número de elementos. Usar el índice 7 confunde el número de elementos con la posición del último y accedería más allá de este array.

Pregunta 3 de 10

Para int a[] = {-12, -4, -9};, ¿qué valor debería devolver una búsqueda del máximo?
  1. -4
  2. 0
  3. -12
  4. -9
Mostrar respuestaOcultar respuesta
Respuesta: A
El mayor valor almacenado es -4 porque es mayor que -12 y que -9. Una búsqueda del máximo debe empezar por un elemento real y actualizarlo cuando aparezca uno mayor. Empezar desde cero devolvería un valor que este array de valores negativos no contiene.

Pregunta 4 de 10

¿Qué imprime este código?

int a[] = {3, 8, 5, 2};
int sum = 0;
for (int i = 1; i < 4; i++) sum += a[i];
printf("%d", sum);
  1. 18
  2. 13
  3. 16
  4. 15
Mostrar respuestaOcultar respuesta
Respuesta: D
El bucle empieza en el índice 1, por lo que suma 8, 5 y 2 y obtiene 15. El índice 0 nunca se visita. El total 18 incluye el 3 inicial y solo sería correcto para un bucle que empezara en cero, cosa que este código no hace.

Pregunta 5 de 10

Una búsqueda de 7 en {2, 9, 7, 4} devuelve -1 al encontrar el primer elemento diferente. ¿Dónde debería devolver el valor de fallo?
  1. Dentro del bucle, tras cualquier candidato diferente.
  2. Después del bucle, tras comprobar cada candidato.
  3. Antes de comprobar el último elemento candidato.
  4. Justo después del primer incremento del bucle.
Mostrar respuestaOcultar respuesta
Respuesta: B
El primer valor, 2, no coincide, pero el índice 2 sí contiene 7. El fallo solo está justificado cuando el bucle termina sin encontrar una coincidencia. Devolver el valor tras una diferencia trata la información sobre un elemento como si se refiriera a todo el array.

Pregunta 6 de 10

¿Qué array resulta de intercambiar a[i] con a[5 - 1 - i] para i = 0 e i = 1, partiendo de {4, 7, 1, 8, 6}?
  1. {6, 7, 1, 8, 4}
  2. {4, 7, 1, 8, 6}
  3. {6, 8, 1, 7, 4}
  4. {8, 6, 1, 4, 7}
Mostrar respuestaOcultar respuesta
Respuesta: C
El primer intercambio cambia 4 por 6; el segundo cambia 7 por 8. El valor central 1 permanece en su lugar, lo que produce el array invertido. El resultado parcialmente invertido con 7 y 8 sin cambios solo tiene en cuenta el primer intercambio, aunque se especifican dos.

Pregunta 7 de 10

Una función void set_first(int a[]) ejecuta a[0] = 11;. ¿Qué valor tiene el primer elemento del array de la función llamadora después de la llamada?
  1. Contiene 11.
  2. No cambia.
  3. Está indefinido.
  4. Contiene 0.
Mostrar respuestaOcultar respuesta
Respuesta: A
El parámetro da a la función acceso a los elementos de la función llamadora, por lo que esta asignación cambia el primer elemento original a 11. No hay una copia separada del elemento que se descarte cuando la función retorna. En esto, pasar un array difiere de pasar un valor escalar por sí solo.

Pregunta 8 de 10

Dentro de void f(int a[], size_t n), los punteros ocupan 8 bytes y los enteros 4 bytes. ¿Cuánto vale sizeof a / sizeof a[0]?
  1. n, en cada llamada
  2. 8, independientemente de n
  3. 4, en cada llamada
  4. 2, independientemente de n
Mostrar respuestaOcultar respuesta
Respuesta: D
El parámetro de tipo array se ajusta a un puntero, por lo que su tamaño es de 8 bytes en este entorno. Un elemento ocupa 4 bytes, lo que da 2. Ese resultado no recupera la longitud del array de la función llamadora; la función debe usar la n proporcionada por separado para conocer el número de elementos.

Pregunta 9 de 10

Una función de solo lectura usa size_t para n e i. Debe imprimir un array en orden inverso y funcionar para n = 0. ¿Qué parámetro y bucle cumplen ambos requisitos?
  1. const int a[]; inicia i en n - 1, comprueba i >= 0, accede a a[i].
  2. int a[]; inicia i en n, comprueba i > 0, accede a a[i].
  3. const int a[]; inicia i en n, comprueba i > 0, accede a a[i - 1].
  4. int a[]; inicia i en n - 1, comprueba i > 0, accede a a[i - 1].
Mostrar respuestaOcultar respuesta
Respuesta: C
El parámetro const impide las escrituras mediante a, mientras que empezar en n evita una resta sin signo cuando el rango está vacío. Acceder a i - 1 visita entonces desde el último elemento válido hasta el índice cero. Empezar en n - 1 con una i sin signo y comprobar i >= 0 provoca una reducción modular y nunca proporciona una condición de parada.

Pregunta 10 de 10

¿Qué garantiza C después de que un programa lea a[4] de int a[4]?
  1. No se garantiza ningún resultado.
  2. La lectura devuelve cero.
  3. La lectura devuelve a[3].
  4. La lectura siempre provoca un cierre inesperado.
Mostrar respuestaOcultar respuesta
Respuesta: A
Los índices válidos del array van de 0 a 3, por lo que esta lectura está fuera de los límites y tiene comportamiento indefinido. C no impone ningún resultado obligatorio para esa operación. Un cierre inesperado es posible, pero esperarlo siempre trataría erróneamente un posible síntoma como una garantía del lenguaje.
Set 2: Caracteres, terminadores de cadenas y UTF-810 Preguntas de Práctica

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

Pregunta 1 de 10

¿Cómo debe pasarse un byte c de tipo char a isalpha sin arriesgarse a usar un byte negativo como argumento?
  1. isalpha((signed char) c)
  2. isalpha(c - '0')
  3. isalpha((unsigned char) c)
  4. isalpha((int) c)
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 2 de 10

¿Qué valor numérico produce '6' - '0'?
  1. 54
  2. 48
  3. 0
  4. 6
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 3 de 10

En ASCII, ¿qué carácter resulta de ('W' - 'A' + 5) % 26 + 'A'?
  1. 'B'
  2. 'A'
  3. 'C'
  4. '\\'
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 4 de 10

¿Cuáles son la longitud del texto y el tamaño del array de char color[] = "amber";?
  1. Longitud 6; tamaño 6
  2. Longitud 5; tamaño 6
  3. Longitud 5; tamaño 5
  4. Longitud 6; tamaño 5
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 5 de 10

¿Qué cadena queda después de char s[8] = "planet"; s[3] = '\0'; s[0] = 'P';?
  1. "Pla"
  2. "Planet"
  3. "Pla0et"
  4. "Plan"
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 6 de 10

¿Por qué no es seguro char tag[4] = "east"; printf("%s", tag);?
  1. El inicializador tiene muy pocos caracteres.
  2. El formato imprime solo un byte.
  3. El array no tiene un byte cero como terminador.
  4. Los caracteres del array son de solo lectura.
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 7 de 10

Un filtro conserva solo las letras de "m8n2p" y las escribe en un array de salida suficientemente grande. ¿Qué debe escribir después de la p final?
  1. Un byte cero en el índice de salida 2
  2. Un dígito cero en el índice de salida 3
  3. Un byte cero en el índice de salida 5
  4. Un byte cero en el índice de salida 3
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 8 de 10

Un programa invierte char s[] = "lamp"; intercambiando s[i] con s[len - i] mientras i < len / 2, donde len es strlen(s). ¿Cuáles son el valor final de strlen(s) y la capacidad, que no cambia?
  1. Longitud 4; capacidad 5
  2. Longitud 0; capacidad 5
  3. Longitud 0; capacidad 4
  4. Longitud 5; capacidad 5
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 9 de 10

En UTF-8, la «é» precompuesta usa dos bytes. ¿Cuáles son el valor de strlen y el almacenamiento mínimo para la cadena «écho»?
  1. 5 bytes de texto; 6 bytes de almacenamiento
  2. 4 bytes de texto; 5 bytes de almacenamiento
  3. 5 bytes de texto; 5 bytes de almacenamiento
  4. 6 bytes de texto; 7 bytes de almacenamiento
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 10 de 10

Una cadena UTF-8 válida tiene los bytes 41 C3 B1 42 00. Un programa escribe cero en el índice de byte 2. ¿Cuáles son el nuevo valor de strlen y la validez UTF-8 del texto?
  1. Longitud 2; UTF-8 válido
  2. Longitud 2; UTF-8 no válido
  3. Longitud 3; UTF-8 válido
  4. Longitud 1; UTF-8 no válido
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Set 3: Elegir y comprobar operaciones de biblioteca10 Preguntas de Práctica

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

Pregunta 1 de 10

¿Cuál es la capacidad mínima de un array de char para una copia completa de "silver"?
  1. 7 bytes
  2. 6 bytes
  3. 8 bytes
  4. 5 bytes
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 2 de 10

¿Puede char label[10] = "blue"; strcat(label, "berry"); almacenar su resultado completo?
  1. No; el resultado necesita 11 bytes.
  2. Sí; el resultado necesita 9 bytes.
  3. Sí; el resultado necesita 10 bytes.
  4. No; el resultado necesita 12 bytes.
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 3 de 10

Dentro de una función se usa un array local automático así: char out[20]; strcat(out, "go");. ¿Por qué no es una forma válida de empezar a construir una cadena?
  1. out es demasiado pequeño para el texto añadido.
  2. out no tiene un terminador de cadena establecido.
  3. strcat requiere un array de punteros para out.
  4. strcat no puede copiar de un literal de cadena.
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 4 de 10

¿Qué condición reconoce que el texto de dos cadenas válidas a y b es igual?
  1. a == b
  2. strcmp(a, b) > 0
  3. strlen(a) == strlen(b)
  4. strcmp(a, b) == 0
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 5 de 10

Después de ejecutar correctamente char *p = strrchr("archive.backup.bin", '.');, ¿qué texto comienza en p?
  1. ".bin"
  2. "bin"
  3. ".backup.bin"
  4. "archive.backup"
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 6 de 10

Un array modificable contiene "on,,off;wait". Se llama a strtok(s, ",;") y luego se repite strtok(NULL, ",;") hasta obtener NULL. ¿Qué tokens se producen y se conserva s?
  1. «on», «», «off», «wait»; s se modifica.
  2. «on», «off;wait»; s se modifica.
  3. «on», «off», «wait»; s se modifica.
  4. «on», «off», «wait»; s se conserva.
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 7 de 10

¿Qué almacena y devuelve char b[6]; int r = snprintf(b, sizeof b, "%s:%d", "job", 84);?
  1. Almacena «job:8» y devuelve 5.
  2. Almacena «job:84» y devuelve 6.
  3. Almacena «job:» y devuelve 4.
  4. Almacena «job:8» y devuelve 6.
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 8 de 10

Un array de siete bytes comienza con "AB". El programa ejecuta strncat(b, "123456", sizeof b - strlen(b) - 1). ¿Cuáles son el texto resultante y el número de bytes almacenados, incluido el cero?
  1. «AB12345»; 8 bytes
  2. «AB1234»; 7 bytes
  3. «AB123»; 6 bytes
  4. «AB123456»; 9 bytes
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 9 de 10

Después de char s[] = "wxyz"; memmove(s, &s[1], 4);, ¿qué contiene s como cadena?
  1. "xyzz"
  2. "wxy"
  3. "xyz"
  4. "yz"
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 10 de 10

Dos arrays de double son iguales al compararlos elemento por elemento, pero uno almacena cero positivo donde el otro almacena cero negativo. ¿Qué puede establecer una comparación de bytes?
  1. Si sus representaciones almacenadas coinciden.
  2. Si sus valores numéricos son diferentes.
  3. Si uno tiene más elementos que el otro.
  4. Si ambos valores son cero positivo.
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Set 4: Tablas, listas de texto y entrada al inicio10 Preguntas de Práctica

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

Pregunta 1 de 10

¿Qué parámetro de tipo array de ancho fijo recibe filas con siete enteros cada una?
  1. int g[7][]
  2. int g[][]
  3. int g[][7]
  4. int g[][6]
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 2 de 10

¿Cuál es la última pareja válida de fila y columna para int table[4][6]?
  1. Fila 4, columna 6
  2. Fila 3, columna 5
  3. Fila 3, columna 6
  4. Fila 4, columna 5
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 3 de 10

Donde int ocupa 4 bytes, ¿cuánto vale sizeof table[0] para int table[3][5]?
  1. 12 bytes
  2. 15 bytes
  3. 60 bytes
  4. 20 bytes
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 4 de 10

Una tabla es {{2, 7, 4}, {6, 1, 5}}. Un bucle inicializa sum una sola vez a cero antes de ambos bucles, suma una fila cada vez e imprime después de cada fila sin restablecer sum. ¿Qué valores se imprimen y cómo se corrige?
  1. 13 y luego 25; restablecer sum antes de cada fila.
  2. 13 y luego 12; restablecer sum antes de cada fila.
  3. 13 y luego 25; restablecer sum antes de cada celda.
  4. 8 y luego 16; restablecer sum antes de cada columna.
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 5 de 10

¿Cuál es el ancho mínimo de fila para que char names[3][width] almacene «Iris», «Magnolia» y «Ash» como cadenas ASCII completas?
  1. 8 bytes
  2. 10 bytes
  3. 16 bytes
  4. 9 bytes
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 6 de 10

Un programa tiene char rows[2][5] = {"oak", "elm"}; y const char *refs[] = {"oak", "elm"};. Debe reemplazar la primera palabra de la lista por el texto completo «cedar». ¿Qué tratamiento es válido para cada forma de almacenamiento?
  1. Copiar «cedar» en rows[0]; reasignar refs[0].
  2. Rechazar la copia en la fila; reasignar refs[0] a «cedar».
  3. Rechazar la copia en la fila; copiar «cedar» en refs[0].
  4. Asignar rows[0] = «cedar»; reasignar refs[0].
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 7 de 10

En un intérprete de comandos normal, se ejecuta un programa como ./mark "West Room" 9. ¿Cuáles son argc y argv[2]?
  1. argc es 3; argv[2] es «9».
  2. argc es 4; argv[2] es «Room».
  3. argc es 2; argv[2] es NULL.
  4. argc es 3; argv[2] es «West Room».
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 8 de 10

Un programa requiere dos argumentos del usuario. Comprueba argc == 2 y luego llama a strlen(argv[2]). ¿Qué comprobación del número de argumentos y qué último índice corresponden a su interfaz real?
  1. Comprobar argc == 2; leer argumentos hasta el índice 2.
  2. Comprobar argc == 3; leer argumentos hasta el índice 3.
  3. Comprobar argc == 3; leer argumentos hasta el índice 2.
  4. Comprobar argc == 2; leer argumentos hasta el índice 1.
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 9 de 10

Después de que strtol en base decimal convierta «42rest», ¿qué indican el resultado y el puntero al final de la conversión?
  1. Valor 0; no se procesó ningún carácter.
  2. Valor 42; el texto sin procesar empieza por r.
  3. Valor 42; se procesó toda la entrada.
  4. Valor 42; debió producirse un error de rango.
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 10 de 10

Una clave formada solo por dígitos cabe en long, pero no en int. El programa la convierte explícitamente a int antes de calcular key % 26. ¿Cómo puede conservar el desplazamiento previsto sin convertirla primero a un tipo de menor rango?
  1. Convertir primero a int y luego calcular el resto.
  2. Usar atoi y luego calcular el resto como int.
  3. Calcular el resto como long y convertir el resultado 0–25.
  4. Comprobar de nuevo los dígitos y convertir el valor original.
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Set 5: Límites de entrada y resultados completos10 Preguntas de Práctica

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

Pregunta 1 de 10

¿Qué ancho máximo de %s deja una posición para el terminador en char word[18]?
  1. %18s
  2. %16s
  3. %17s
  4. %19s
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 2 de 10

Una llamada correcta a fgets lee la entrada "pine\n" en un array de 10 bytes. ¿Cuáles son el valor de strlen del resultado y el índice de su terminador?
  1. Longitud 4; índice del terminador 4
  2. Longitud 5; índice del terminador 6
  3. Longitud 6; índice del terminador 5
  4. Longitud 5; índice del terminador 5
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 3 de 10

La línea de entrada es «qrstuvwxy» seguida de un salto de línea. Dos llamadas a fgets usan el mismo char b[6] sin otras lecturas. ¿Qué texto almacena la segunda llamada?
  1. "uvwxy"
  2. "vwxy\n"
  3. "vwxy"
  4. "qrst\n"
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 4 de 10

Un programa permite 12 caracteres de texto ASCII en un nombre introducido como una línea completa terminada en salto de línea. Debe examinar el salto de línea y luego almacenar solo el nombre en un array separado. ¿Qué capacidades mínimas necesitan la entrada y el array final?
  1. Entrada 14 bytes; final 13 bytes
  2. Entrada 13 bytes; final 13 bytes
  3. Entrada 14 bytes; final 12 bytes
  4. Entrada 13 bytes; final 12 bytes
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 5 de 10

Un búfer de 9 bytes recibe un resultado correcto de fgets de longitud 8 sin salto de línea. No se ha examinado más entrada. ¿Qué puede concluirse sobre la línea lógica?
  1. Sin duda tiene más de ocho caracteres.
  2. Puede terminar ahí o quedar más entrada por leer.
  3. Sin duda ya terminó al llegar al fin de archivo.
  4. fgets leyó y eliminó su salto de línea.
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 6 de 10

¿Por qué no es seguro counts[c - 'a']++ para una entrada de caracteres sin comprobar?
  1. El índice obtenido puede estar fuera del array.
  2. El incremento no puede actualizar contadores enteros.
  3. La resta siempre devuelve un valor negativo.
  4. La expresión compara dos cadenas completas.
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 7 de 10

Un array de char de seis bytes se llena con letras en los índices de 0 a 5 y luego se pasa a strlen. ¿Qué propiedad falta?
  1. Un byte libre fuera del array para el cero.
  2. Un salto de línea explícito después de la última letra.
  3. Un terminador dentro de las seis posiciones válidas.
  4. Un entero separado que almacene su capacidad en bytes.
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 8 de 10

Un programa almacena un código completo en char out[6]. A la llamada snprintf(out, sizeof out, "%s%d", "R-", 1234) le sigue if (r > sizeof out) reject();, donde r es su valor de retorno no negativo. ¿Qué se almacena, esa comprobación lo rechaza y qué debe cambiar?
  1. «R-1234»; aceptado; mantener la comprobación actual.
  2. «R-123»; rechazado; mantener la comprobación actual.
  3. «R-12»; aceptado; rechazar cuando r >= capacidad.
  4. «R-123»; aceptado; rechazar cuando r >= capacidad.
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 9 de 10

Un analizador decimal rechaza ERANGE y exige *end == '\0', pero no comprueba end == start. ¿Qué entrada no válida puede seguir aceptándose como cero?
  1. Una cadena vacía
  2. Una palabra alfabética
  3. Un dígito seguido de una letra
  4. Un número fuera del rango de long
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 10 de 10

Una cadena UTF-8 válida tiene los bytes 41 C3 A9 E2 82 AC 00. Un programa la copia en char b[5] y reserva la última posición para el cero. ¿Cuáles son el valor resultante de strlen, el número de bytes iniciales y la validez de la codificación?
  1. Longitud 4; 3 inicios; UTF-8 válido.
  2. Longitud 4; 3 inicios; UTF-8 no válido.
  3. Longitud 3; 3 inicios; UTF-8 válido.
  4. Longitud 4; 2 inicios; UTF-8 no válido.
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Set 6: Seguir y corregir operaciones combinadas en C10 Preguntas de Práctica

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

Pregunta 1 de 10

¿Qué array crea int a[4] = {[2] = 9};?
  1. {0, 9, 0, 0}
  2. {0, 0, 9, 0}
  3. {9, 0, 0, 0}
  4. {0, 0, 0, 9}
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 2 de 10

Un array de ocho posiciones tiene count igual a 5. Se añade un valor en a[count] y se incrementa count. ¿En qué índice se escribió y cuál es el nuevo count?
  1. Índice 5; count 6
  2. Índice 4; count 6
  3. Índice 6; count 6
  4. Índice 5; count 5
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 3 de 10

Para int g[3][4], ¿cuál es la posición de elemento de g[2][1], contando desde cero, en el almacenamiento por filas?
  1. 7
  2. 8
  3. 10
  4. 9
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 4 de 10

Un intérprete de comandos ejecuta ./pick "" blue. ¿En qué se diferencian argv[1] y argv[argc]?
  1. argv[1] es un puntero nulo; argv[argc] es una cadena vacía.
  2. Ambas entradas son cadenas vacías con sus propios terminadores.
  3. argv[1] es una cadena vacía; argv[argc] es un puntero nulo.
  4. Ambas entradas son punteros nulos que indican texto ausente.
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 5 de 10

Una función recibe int a[], size_t n y ejecuta a[0] = 0; n = 0;. La función llamadora proporcionó {5, 8, 2} y el número de elementos 3, y luego calcula la suma entera de sus elementos dividida por ese número. ¿Qué promedio y qué número de elementos de la función llamadora resultan?
  1. Promedio 0; número de elementos 3
  2. Promedio 3; número de elementos 3
  3. Promedio 5; número de elementos 3
  4. División indefinida; número de elementos 0
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 6 de 10

Un analizador divide el texto modificable «run,runner» por las comas mediante strtok. Incrementa un contador cuando strncmp(token, "run", 3) == 0. ¿Qué cuenta resulta, qué cuenta debería dar la coincidencia exacta de comandos y qué comparación lo corrige?
  1. Actual 2; exacta 1; strcmp(token, "run") == 0.
  2. Actual 1; exacta 1; strcmp(token, "run") == 0.
  3. Actual 2; exacta 2; strncmp(token, "run", 2) == 0.
  4. Actual 1; exacta 2; strstr(token, "run") != NULL.
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 7 de 10

Un array b de cinco bytes se llena con strncpy(b, "planet", sizeof b);. El programa requiere la palabra original completa. ¿Qué corrección cumple el requisito?
  1. Escribir cero en b[4] después de copiar.
  2. Añadir cero en b[5] después de copiar.
  3. Pasar sizeof b - 1 y luego usar b sin cambios.
  4. Rechazarla por ser demasiado larga antes de copiar.
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 8 de 10

Un programa usa int values[4] = {14, 28, 35, 49};. Convierte por completo «04» con strtol en base decimal, acepta selecciones de 1 a 4 y luego lee values[selected]. Debe permitir las cuatro selecciones y asignar la selección 1 al primer valor. ¿Qué ocurre y qué regla de indexación lo corrige?
  1. Imprime 49; mantener el índice selected.
  2. Imprime 49; usar el índice selected - 1.
  3. Tiene comportamiento indefinido; usar selected - 1.
  4. Tiene comportamiento indefinido; exigir selection < 4.
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 9 de 10

Una función read_text(char out[], size_t cap) llama a fgets(out, sizeof out, stdin). La función llamadora pasa un array de 30 bytes; el tamaño del puntero es 8. Con «sunflower\n» pendiente, ¿qué fragmento se almacena, qué queda y qué límite debe reemplazar a sizeof out?
  1. «sunflower\n»; no queda nada; usar cap.
  2. «sunflowe»; queda «r\n»; usar cap.
  3. «sunflow»; queda «er\n»; usar strlen(out).
  4. «sunflow»; queda «er\n»; usar cap.
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 10 de 10

Un contador de secuencias de letras ASCII empieza fuera de una palabra y se incrementa cada vez que una letra sigue a un carácter que no es una letra. ¿Cuántas palabras cuenta en «go2fast now!»?
  1. 3
  2. 2
  3. 4
  4. 1
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

¿Te atascas con una pregunta tuya? Ayuda con Tareas

Términos Clave

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

A

Acceso fuera de los límites
Un acceso fuera de los límites lee o escribe una posición indexada fuera del array o la fila real a la que se accede y tiene comportamiento indefinido.
Acceder a a[8] en un array de ocho elementos no es válido aunque una ejecución de prueba parezca devolver un valor útil.
Acumulador
Un acumulador es una variable que reúne un resultado acumulado, como una suma o un recuento, mientras un bucle procesa valores sucesivos.
Empezar sum en cero permite que cada sum += a[i] añada el siguiente elemento del array al total actual.
Ancho de campo
Un ancho de campo en una conversión %s limita el número de caracteres de entrada almacenados antes del byte cero terminador adicional.
Para char word[10], la conversión %9s reserva el décimo byte disponible para el terminador.
Ancho de fila
El ancho de fila es el número de elementos de cada array interior y se necesita en un parámetro de tabla para localizar los elementos mediante índices.
El parámetro int g[][6] acepta filas de seis enteros de ancho, mientras que la función llamadora proporciona el número de filas por separado.
argc
El parámetro argc indica el número de cadenas de argumentos proporcionadas a main, normalmente incluida la cadena usada para iniciar el programa.
Ejecutar ./label red blue en un intérprete normal da a argc el valor 3: el nombre de inicio y dos colores.
Argumento de la línea de comandos
Un argumento de la línea de comandos es una cadena proporcionada al iniciar un programa, a menudo agrupada o expandida por un intérprete de comandos antes de que el programa la reciba.
Poner «North Hall» entre comillas en la línea de comandos puede proporcionar un argumento que contiene un espacio en lugar de dos palabras.
argv
El parámetro argv proporciona acceso a las cadenas de argumentos mediante índices, seguidas de un puntero nulo en la posición indicada por argc.
Después de comprobar argc, un programa puede usar strcmp(argv[1], "-v") para reconocer su primera opción.
Array (arreglo)
Un array es una secuencia de tamaño fijo de elementos contiguos de un mismo tipo, a los que se accede mediante un nombre compartido y un índice entero.
int levels[6] reserva seis enteros; actualizar levels[2] cambia el tercer valor almacenado.
Array bidimensional
Un array bidimensional es un array de arrays de fila de igual ancho, en el que dos índices seleccionan un elemento por su fila y su columna.
int board[4][6] proporciona 24 enteros, a los que se accede mediante cuatro índices de fila válidos y seis índices de columna válidos.
Array de frecuencias
Un array de frecuencias almacena un contador para cada categoría y usa un valor de categoría validado para seleccionar el contador que se incrementará.
Después de convertir un dígito al valor 4, counts[4]++ añade una aparición al recuento de ese dígito.
Array de longitud variable
Un array de longitud variable obtiene su tamaño cuando se ejecuta su declaración y lo conserva hasta que termina su tiempo de vida.
En C17, la declaración local const int n = 6; int a[n]; crea un array de longitud variable porque n no es una expresión constante.
Array de punteros a cadenas
Un array de punteros a cadenas almacena una referencia por cadena, lo que permite que el texto almacenado en otro lugar tenga diferentes longitudes.
const char *names[] = {"Al", "Mina"} almacena dos referencias y permite reemplazar una referencia.
Array de salida
Un array de salida es almacenamiento proporcionado por una función llamadora para que una función lo llene con varios resultados, con una capacidad o un número de elementos acordados.
Una función que calcula cuadrados puede escribir cada resultado en out[i] mientras la función llamadora conserva la propiedad del array.
ASCII
ASCII es una codificación de caracteres que asigna los valores de 0 a 127 a letras básicas, dígitos, signos de puntuación y caracteres de control.
La operación '8' - '0' produce 8 porque los códigos de los caracteres de dígitos son consecutivos.
atoi
La función atoi convierte una parte numérica inicial a int sin informar de errores de forma fiable y tiene comportamiento indefinido en caso de desbordamiento.
La entrada «18x» produce 18 con atoi, por lo que un número devuelto no demuestra que toda la entrada fuera numérica.

B

Bucles anidados
Los bucles anidados colocan un bucle dentro de otro, de modo que el bucle interior se repite para cada valor del bucle exterior, como las columnas dentro de cada fila.
Un recorrido de dos filas y cinco columnas ejecuta el cuerpo interior diez veces cuando ambos índices permanecen dentro de sus límites.
Búfer
Un búfer es una región de almacenamiento reservada para datos que se leen, escriben o transforman, con una capacidad que limita las escrituras válidas.
Un búfer de nombres de 32 bytes puede contener treinta caracteres de texto ASCII, un salto de línea conservado y un byte cero final.
Búsqueda lineal (secuencial)
Una búsqueda lineal comprueba elementos en secuencia hasta encontrar un valor buscado o haber examinado todos los candidatos sin encontrar una coincidencia.
Buscar 2 en {3, 8, 2} encuentra una coincidencia en el índice 2 después de comprobar los elementos anteriores que no coinciden.
Byte de continuación
Un byte de continuación UTF-8 tiene el patrón de bits inicial 10 y sigue a un byte inicial dentro de un punto de código codificado en varios bytes.
El byte 0xA9 continúa la secuencia de «é»; no debe conservarse solo como un carácter UTF-8 completo.

C

Cadena de C
Una cadena de C es una secuencia de caracteres que termina en un carácter nulo, cuyo texto se almacena en un array en lugar de en un tipo de cadena integrado independiente.
El array inicializado mediante char pet[] = "dog" contiene tres letras seguidas de un carácter de valor cero.
Cadena vacía
Una cadena vacía no contiene caracteres de texto y se representa mediante un terminador en su primera posición, lo que le da una longitud de cero.
char message[20] = "" proporciona un destino vacío válido para una concatenación posterior que quepa en su capacidad.
Capacidad
La capacidad de un array es el número de elementos que puede contener el almacenamiento reservado, independientemente de cuántos contengan actualmente valores útiles.
Un array con capacidad 80 y 29 elementos utilizados tiene espacio para 51 valores adicionales si se mantiene correctamente el número de elementos.
Clasificación de caracteres
La clasificación de caracteres comprueba si un código de carácter pertenece a una categoría, como dígito, letra o carácter en blanco, mediante funciones de <ctype.h>.
isdigit((unsigned char) c) da un resultado distinto de cero cuando el byte almacenado representa un dígito decimal.
Codificación de caracteres
Una codificación de caracteres define una correspondencia entre el texto y su representación numérica almacenada, incluidos los bytes usados para su transmisión o almacenamiento.
Una cadena de C que contiene la misma palabra visible puede necesitar diferentes cantidades de bytes con distintas codificaciones.
Columna
Una columna agrupa la misma posición dentro de cada fila de una tabla; su índice es el segundo índice de g[r][c].
Para sumar la columna 2, mantén c igual a 2 y varía r por cada fila mientras lees g[r][c].
Comparación según los valores de los bytes
Una comparación según los valores de los bytes ordena cadenas por los valores sin signo de sus bytes almacenados, lo que puede diferir de las convenciones alfabéticas de un idioma.
En UTF-8, strcmp coloca una letra latina acentuada después de la z ASCII porque su byte inicial tiene un valor mayor.
Comportamiento indefinido
El comportamiento indefinido significa que el estándar de C no impone requisitos sobre el resultado de una operación, por lo que no se garantiza ningún resultado concreto.
Una escritura no válida en un array puede parecer inofensiva en una versión y provocar un cierre inesperado o corromper datos en otra.
Concatenación
La concatenación une secuencias de texto en orden y requiere suficiente espacio en el destino para el texto combinado y su carácter terminador.
Unir «north» y «east» forma «northeast», que necesita diez bytes como cadena ASCII terminada en nulo.
Configuración regional (locale)
Una configuración regional proporciona convenciones utilizadas por las funciones de biblioteca; la configuración regional «C» predeterminada da a la clasificación de un solo byte sus reglas básicas de caracteres.
toupper aplicado byte a byte en la configuración regional «C» no convierte una letra acentuada UTF-8 completa en su forma mayúscula.
Constante de carácter
Una constante de carácter es una expresión de C entre comillas simples, de tipo int, que representa un código de carácter, como el código de una letra.
Con códigos ASCII, printf("%c %d", 'B', 'B') muestra la letra B y el valor numérico 66.
Conversión de texto a número
Una conversión de texto a número interpreta texto como un número y requiere comprobaciones separadas del formato aceptado, el texto procesado y el rango de valores permitido.
Los bytes «27» necesitan una conversión antes de que un programa pueda reducir el valor numérico con % 26.

D

Delimitador
Un delimitador es un carácter usado para separar partes de texto, como los caracteres de coma o espacio proporcionados a una función que divide texto en tokens.
Con los delimitadores «, », el texto «one, two» puede dividirse en los tokens «one» y «two».
Desbordamiento de búfer
Un desbordamiento de búfer es una escritura más allá del almacenamiento disponible de un búfer que puede corromper datos cercanos o información de control del programa.
Copiar seis bytes de texto y un terminador en un array de seis bytes excede el límite del array en un byte.
Desplazamiento de César
Un desplazamiento de César reemplaza cada letra por otra situada a un número fijo de posiciones del alfabeto, volviendo al inicio dentro de su rango de letras.
En texto ASCII, desplazar la mayúscula Y dos posiciones vuelve al inicio y produce A, mientras que un dígito o una coma pueden permanecer sin cambios.
División en tokens
La división en tokens separa el texto en partes mediante delimitadores; strtok lo hace modificando la entrada y recordando una posición.
Una segunda llamada a strtok(NULL, ",") continúa dividiendo la misma entrada modificable en lugar de empezar de nuevo.

E

Elemento
Un elemento es un valor almacenado en un array, con el mismo tipo declarado que los demás valores y su propia posición identificada por un índice.
En int a[3] = {8, 1, 6}, el elemento a[1] contiene 1 y puede recibir otro entero.
ERANGE
La macro ERANGE identifica un error de rango comunicado mediante errno, como un resultado de strtol demasiado grande o pequeño para long.
Establece errno en cero antes de la conversión y luego comprueba si es ERANGE antes de aceptar una secuencia larga de dígitos.
Error de una unidad (off-by-one)
Un error de una unidad usa un límite una posición demasiado lejos o demasiado cerca, a menudo por confundir el número de elementos con su último índice válido.
La condición i <= n hace que un recorrido de n elementos con índices desde cero intente un acceso adicional en el índice n.
Estado de salida
Un estado de salida informa al entorno que llamó al programa de su finalización; devolver cero desde main indica que terminó correctamente.
La ausencia de un argumento obligatorio puede hacer que un programa imprima información de uso y devuelva un estado de fallo distinto de cero.

F

fgets
La función fgets lee una entrada de línea con límite, conserva un salto de línea si llega a él y añade un terminador cuando lee texto correctamente.
Un búfer de ocho bytes puede recibir como máximo siete caracteres de entrada y deja el texto adicional de la línea para lecturas posteriores.
Fila
Una fila es un array interior de un array bidimensional que contiene un número fijo de elementos consecutivos del mismo tipo.
En int table[2][5], table[1] representa la segunda fila, que contiene cinco posiciones de enteros.
Filtrado
El filtrado construye un resultado a partir de caracteres seleccionados de la entrada y usa una posición de escritura separada porque los caracteres rechazados no ocupan espacio en la salida.
Conservar solo las letras de «q2r7» escribe «qr» en la salida y después añade su byte cero terminador.
Fin de archivo
El fin de archivo indica que no hay más entrada disponible; fgets aún puede tener éxito si leyó caracteres antes de llegar a esa condición.
Un archivo que termina con «done» y sin salto de línea aún permite que un búfer suficientemente grande de fgets reciba ese texto final.

Í

Índice
Un índice es una posición entera que se usa para seleccionar un elemento de un array; empieza en cero y termina una unidad por debajo del número de elementos.
En un array de siete elementos, el índice 6 accede al último elemento; el índice 7 ya está fuera del array.

I

Inicializador
Un inicializador proporciona valores iniciales cuando se declara un objeto; los valores que faltan en un inicializador de array pasan a ser cero.
int hits[4] = {3} inicializa el primer contador en 3 y los otros tres contadores en cero.

L

Límite de palabra
Un límite de palabra es la transición que usa un programa para decidir dónde empieza o termina una palabra, según la definición de palabra que haya elegido.
Un contador de secuencias de letras inicia una palabra nueva cuando encuentra una letra después de un espacio, un signo de puntuación o al comienzo de la cadena.
Línea parcial
Una línea parcial es la parte leída antes de que el límite de tamaño de un búfer detenga la entrada, con lo que puede quedar más de esa línea lógica sin leer.
Una respuesta larga puede llegar en dos fragmentos de fgets, por lo que el segundo fragmento no debería convertirse en una respuesta nueva.
Lista de cadenas de capacidad fija por fila
Una lista de cadenas de capacidad fija por fila almacena texto en filas de un array de char de 2 dimensiones, donde cada fila reserva su propia capacidad fija y espacio para el terminador.
En char labels[5][12], cada etiqueta tiene doce bytes de almacenamiento aunque su texto actual sea más corto.
Literal de cadena
Un literal de cadena es texto entre comillas dobles que proporciona un array de caracteres terminado en nulo cuyo contenido no debe modificar el programa en ejecución.
const char *label = "ready" apunta a un literal; una copia modificable puede usar char label[] = "ready".
Longitud de una cadena
La longitud de una cadena es el número de bytes de texto anteriores al primer carácter nulo, sin incluir ese carácter ni la capacidad no utilizada.
Un array de 40 bytes que contiene «June» tiene longitud 4, aunque su almacenamiento disponible sigue siendo de 40 bytes.

M

Máscara de bits
Una máscara de bits selecciona posiciones de bits concretas para comprobarlas; una operación AND conserva solo los bits de entrada marcados con unos en la máscara.
La máscara 0xC0 conserva los dos bits más significativos para que un programa pueda reconocer el patrón de continuación UTF-8.
memcmp
La función memcmp compara un número fijo de bytes e informa de su orden, comprobando representaciones de almacenamiento en lugar de valores numéricos.
El cero positivo y el negativo de coma flotante pueden ser iguales como valores aunque memcmp encuentre bytes almacenados diferentes.
memcpy
La función memcpy copia exactamente el número solicitado de bytes entre regiones que no se solapan, sin interpretar los terminadores de cadenas.
memcpy(b, a, sizeof a) copia todo el almacenamiento del array a cuando b tiene espacio suficiente y es una región separada.
memmove
La función memmove copia una cantidad solicitada de bytes con comportamiento definido incluso cuando las regiones de origen y destino se solapan.
Mover tres bytes dentro del mismo array de caracteres requiere una operación segura para regiones solapadas como memmove.
memset
La función memset rellena una región con un valor de byte repetido, sin asignar ese valor numérico por separado a cada elemento del array.
memset(counts, 0, sizeof counts) pone a cero un array de enteros; el valor de byte 1 no asignaría el entero 1.

N

Número de asignaciones realizadas
El número de asignaciones realizadas es la cantidad de asignaciones de entrada que scanf completó correctamente y se usa para comprobar si los datos solicitados se leyeron realmente.
Una lectura de un número espera un valor de retorno de 1; un fallo de coincidencia puede dejar los mismos bytes no válidos pendientes en la entrada.
Número de elementos utilizados
El número de elementos utilizados registra cuántos elementos del array pertenecen actualmente a los datos activos y determina el límite del bucle de procesamiento.
Si solo se han llenado 12 entradas en un array de 50 elementos, un bucle de procesamiento de datos se detiene en el número de elementos utilizados, 12.

O

Orden por filas
El orden por filas almacena consecutivamente todos los elementos de una fila antes de la siguiente, como se exige para los arrays multidimensionales de C.
El elemento que sigue a grid[0][3] en el almacenamiento de una tabla de cuatro columnas es grid[1][0].

P

Parámetro con calificador const
Un parámetro con calificador const impide modificar los elementos referenciados mediante ese parámetro, aunque permite leerlos.
Una función de suma usa const int a[] porque sumar los valores de entrada no requiere modificarlos.
Parámetro de tipo array
Un parámetro de tipo array se ajusta a un parámetro de tipo puntero, lo que da a una función acceso a los elementos de la función llamadora sin su número.
Una función declarada con int a[] sigue necesitando una longitud separada antes de poder recorrer su entrada con un bucle.
Parámetro de tipo modificado variablemente
Un parámetro de tipo modificado variablemente usa parámetros de tamaño anteriores en su tipo de array para que una función pueda recibir tablas con diferentes anchos de fila.
GCC y Clang aceptan int g[rows][cols] después de parámetros que definen números de filas y columnas positivos y coincidentes.
Puntero al final de la conversión
Un puntero al final de la conversión registra dónde se detuvo la conversión numérica, lo que permite que un programa detecte que no hubo conversión o que quedan caracteres no deseados.
Para la entrada «9z», strtol devuelve 9, pero el puntero al final de la conversión apunta a la z que no se procesó.
Puntero nulo
Un puntero nulo es un valor de puntero que no apunta a ningún objeto; las funciones de búsqueda lo usan para indicar que no existe ninguna ubicación coincidente.
Comprueba strchr(name, '.') != NULL antes de usar su resultado como inicio de una extensión de archivo.
Punto de código Unicode
Un punto de código Unicode es una posición numerada en el espacio de códigos de Unicode, usada para identificar caracteres y otros valores de texto asignados.
La letra precompuesta «é» es U+00E9, aunque su almacenamiento en UTF-8 consta de dos bytes separados.

R

Rango permitido por la aplicación
El rango permitido por la aplicación es el intervalo de valores que un programa permite para un propósito y que puede ser más estrecho que el rango completo del tipo numérico.
Una cantidad convertida de 120 cabe en un entero, pero no cumple una regla del programa que exige una cantidad de 1 a 100.
Reducción modular de enteros sin signo
La reducción modular de enteros sin signo reduce un resultado aritmético sin signo módulo el rango de su tipo, de modo que restar uno de cero da un valor grande.
Una cuenta atrás con size_t no puede detenerse al volverse negativa; usa i > 0 y accede a la posición i - 1.

S

Secuencia de escape
Una secuencia de escape es una forma de escribir en el código fuente, basada en una barra invertida, un carácter que necesita una notación especial, como un salto de línea o una comilla.
El literal "A\nB" contiene un salto de línea entre sus letras; la barra invertida y la n codifican un carácter.
size_t
El tipo size_t es un tipo entero sin signo usado para tamaños de objetos y devuelto por sizeof; se usa habitualmente para longitudes de arrays.
printf("%zu", sizeof data) usa el formato de tamaño para imprimir el número de bytes que ocupa data.
snprintf
La función snprintf da formato al texto dentro de una capacidad proporcionada y devuelve la longitud completa del texto necesario o un resultado negativo de error.
Un valor de retorno de 12 para una capacidad de 10 significa que el resultado almacenado se cortó, aunque siga terminado en nulo.
strcat
La función strcat añade una cadena de origen en el terminador existente del destino y escribe un nuevo terminador después del texto combinado.
Añadir «box» a «red » necesita ocho bytes de almacenamiento de destino, incluido el byte cero final.
strchr
La función strchr encuentra el primer carácter coincidente en una cadena y devuelve su ubicación, o un puntero nulo si no existe ninguna coincidencia.
Buscar un punto en «name.part.ext» con strchr encuentra la parte final «.part.ext», incluido el primer punto.
strcmp
La función strcmp compara cadenas terminadas en nulo como bytes sin signo y devuelve un resultado negativo, cero o positivo según su orden.
strcmp("oak", "oak") == 0 comprueba que el contenido es igual incluso cuando las dos cadenas ocupan almacenamientos separados.
strcpy
La función strcpy copia una cadena de origen completa y su terminador en un destino que no se solapa con ella y tiene suficiente almacenamiento disponible.
Copiar «pine» escribe cinco bytes incluido el cero, por lo que cabe en un destino declarado como char out[5].
strcspn
La función strcspn mide la parte inicial de una cadena que no contiene ninguno de los caracteres proporcionados en su segundo argumento.
strcspn("fir:oak", ":,") devuelve 3 porque el primer separador indicado aparece después de las tres letras de fir.
strlen
La función strlen recorre una cadena válida hasta su terminador y devuelve el número de bytes anteriores como un valor size_t.
Para char tag[30] = "oak", strlen(tag) es 3, mientras que sizeof tag indica la capacidad completa de 30 bytes.
strncat
La función strncat añade como máximo un número especificado de bytes de texto de origen y después escribe un terminador.
Para una cadena de destino válida, el espacio restante para texto es la capacidad menos su longitud actual menos uno.
strncmp
La función strncmp compara como máximo un número especificado de bytes de cadenas y se detiene antes si encuentra un final o una diferencia.
Una coincidencia de tres bytes entre «run-fast» y «run-slow» establece un prefijo compartido, no la igualdad de las cadenas completas.
strncpy
La función strncpy copia hasta un límite y rellena el texto más corto con ceros, pero no añade un terminador cuando el origen llena ese límite.
Copiar «abcd» con límite 3 almacena a, b y c sin un byte cero, por lo que otro paso debe terminar el resultado.
strrchr
La función strrchr encuentra el último carácter coincidente en una cadena y devuelve su ubicación, o un puntero nulo si no existe ninguna coincidencia.
El último punto de «name.part.ext» inicia «.ext», por lo que strrchr puede localizar el separador de la extensión.
strtol
La función strtol convierte texto a long usando una base elegida e informa de la ubicación donde se detuvo y de los errores de rango.
Llamar a strtol(text, &end, 10) permite que el programa rechace una letra restante después de dígitos decimales válidos.
Subcadena
Una subcadena es una secuencia consecutiva de caracteres dentro de otra cadena; strstr encuentra la ubicación inicial de su primera coincidencia.
Buscar «green» en «blue-green» devuelve una ubicación desde la que la cadena restante se lee como «green».

T

Terminador nulo
Un terminador nulo es el carácter de valor cero que marca el final de una cadena de C; ocupa almacenamiento sin aumentar la longitud del texto.
Escribir text[2] = '\0' hace que las funciones de cadenas se detengan después de los dos primeros caracteres de ese array.
Transformación en el mismo espacio de almacenamiento
Una transformación en el mismo espacio de almacenamiento modifica valores en su almacenamiento existente en lugar de construir un array de salida separado.
Convertir a mayúsculas una cadena ASCII modificable puede asignar cada carácter convertido a la misma posición de índice.
Truncamiento
El truncamiento es la pérdida del texto que excede un límite de longitud disponible, lo que puede evitar un desbordamiento y aun así cambiar el significado de los datos.
Una etiqueta de archivo acortada puede referirse a un texto diferente, por lo que una interfaz que exige nombres completos debería rechazar un resultado truncado.

U

UTF-8
UTF-8 es una codificación de Unicode que almacena cada valor escalar en uno a cuatro bytes y conserva las codificaciones ASCII de un byte.
La cadena «café» ocupa cinco bytes de texto UTF-8, más un byte cero cuando se almacena como cadena de C.

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