Arranjos e cadeias de caracteres em C

Aprenda como C armazena vários valores em um arranjo e como representa texto como caracteres terminados em um byte nulo. Percorra arranjos e tabelas com laços, passe-os para funções, processe cadeias de caracteres com seus próprios laços e com as funções padrão, veja como o texto UTF-8 é armazenado em bytes e leia os argumentos que um programa recebe na linha de comando. Ao longo do curso, aprenda por que C nunca verifica os limites dos arranjos e como ler e copiar dados para que sempre caibam no espaço disponível.

BásicoAula 3 de 8 do curso Curso de Programação em C

O que você vai aprender

  1. Declare arranjos de tamanho fixo, acesse cada elemento pelo índice, percorra-os com laços sem ultrapassar o fim e compartilhe-os com funções.
    • Declare, inicialize e acesse um arranjo por índices
      Crie arranjos com uma quantidade fixa de elementos, atribua-lhes valores iniciais e leia ou altere elementos individuais pelo índice.
    • Percorra arranjos com laços sem ultrapassar seus limites
      Visite cada elemento com um laço de contagem e use esse padrão para somar, encontrar o maior valor, buscar, contar e inverter.
    • Passe arranjos para funções
      Escreva funções que recebam arranjos como parâmetros, passe o comprimento junto e saiba quando a função pode alterar os dados da função chamadora.
  2. Trate caracteres como pequenos códigos inteiros, armazene texto em arranjos de char terminados por um caractere nulo, processe-o um caractere por vez e veja como o texto UTF-8 é armazenado em bytes.
    • Trate caracteres como códigos numéricos
      Use o tipo char e constantes de caractere como números, converta algarismos e letras com operações aritméticas e classifique caracteres com <ctype.h>.
    • Armazene texto em cadeias terminadas em nulo
      Explique como uma cadeia de caracteres em C é disposta em um arranjo de char, por que precisa de um caractere nulo no fim e como literais de cadeia e arranjos diferem.
    • Processe cadeias de caracteres um caractere por vez
      Percorra uma cadeia até seu terminador para contar, converter, inverter, filtrar e criptografar texto e termine cada nova cadeia com '\0'.
    • Explique como o texto UTF-8 é armazenado em arranjos de char
      Relacione caracteres, pontos de código Unicode e bytes UTF-8 e preveja o que funções de C baseadas em bytes fazem com texto além de ASCII.
  3. Meça, copie, concatene, compare e busque cadeias com <string.h> e garanta que cada destino tenha espaço para o resultado e seu terminador.
    • Meça, copie e concatene cadeias de caracteres
      Use strlen, strcpy e strcat corretamente e calcule quanto espaço cada destino precisa.
    • Compare cadeias de caracteres e faça buscas nelas
      Compare cadeias com strcmp e strncmp, encontre caracteres e subcadeias e divida texto em tokens.
    • Copie respeitando os limites e trabalhe diretamente com bytes de memória
      Use corretamente as funções com limite strncpy, strncat e snprintf, detecte truncamento e copie, preencha e compare arranjos inteiros com as funções de memória.
  4. Armazene tabelas e listas de cadeias de caracteres em arranjos com duas ou mais dimensões, percorra-os com laços aninhados e passe-os para funções.
    • Declare e percorra matrizes bidimensionais
      Declare e inicialize tabelas de valores, explique como suas linhas são dispostas na memória e processe linhas e colunas com laços aninhados.
    • Passe tabelas para funções e armazene listas de cadeias de caracteres
      Escreva funções que recebam matrizes bidimensionais e mantenha várias cadeias em um arranjo 2D de char ou em um arranjo de ponteiros para cadeias.
  5. Leia as palavras digitadas após o nome de um programa por meio de argc e argv, verifique quantas são e converta-as com segurança em números e opções.
    • Leia argc e argv
      Declare main com seus dois parâmetros, descreva o que cada elemento de argv contém e verifique a contagem de argumentos antes de usar qualquer argumento.
    • Converta e valide argumentos numéricos
      Converta cadeias de argumentos em números com verificação de erros, rejeite entradas inválidas e verifique a faixa dos valores antes de usá-los.
  6. Reconheça como programas em C leem e gravam fora de seus arranjos, por que isso é perigoso e como ler e copiar texto para que ele sempre caiba.
    • Reconheça acessos fora dos limites e seus efeitos
      Explique por que C não impede acessos fora dos limites, o que pode acontecer quando ocorrem e quais erros os causam.
    • Leia e copie texto respeitando os limites do buffer
      Leia entradas com limites de tamanho, detecte linhas longas demais e copie texto somente quando ele couber no destino.

Guia de estudo de Arranjos e cadeias de caracteres em C

6 capítulos

1

Armazene e processe coleções de valores com arranjos

Arranjos permitem que um laço trabalhe com vários valores relacionados. O arranjo fixa o espaço de armazenamento disponível, enquanto índices selecionam elementos individuais e uma contagem registra quantos estão em uso. Aprenda como declarações estabelecem valores iniciais, como laços calculam resultados sem ultrapassar um limite e como funções compartilham os elementos da função chamadora. Pequenos exemplos relacionam o código às posições exatas que ele lê ou grava.

1.1 Declare, inicialize e acesse um arranjo por índices

Um arranjo dá um nome a uma quantidade fixa de valores do mesmo tipo. Seus elementos ficam lado a lado na memória. Use um arranjo quando a mesma operação se aplicar a muitos valores, como atualizar um conjunto de notas.

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

A primeira declaração cria cinco inteiros. O compilador conta quatro valores no segundo inicializador, portanto primes tem quatro elementos. No terceiro, o primeiro elemento é explicitamente zero e cada inicializador ausente também se torna zero. Da mesma forma, int a[5] = {1, 2}; contém 1, 2, 0, 0, 0. Um inicializador designado, disponível desde C99, escolhe uma posição: int b[5] = {[2] = 7}; coloca 7 no terceiro elemento e zero nos demais.

Uma declaração local automática como int data[5]; reserva espaço sem fornecer valores iniciais. Grave cada elemento antes de lê-lo. Arranjos declarados fora de todas as funções e arranjos declarados como static são inicializados com zeros quando nenhum inicializador explícito é fornecido.

Acesse um valor pelo índice

Os índices começam em zero. Para cinco elementos, as posições válidas vão de 0 a 4. scores[1] significa a segunda nota, não a primeira. Um elemento pode ser usado em qualquer lugar em que uma variável comum de seu tipo possa ser usada:

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

A segunda nota agora é 80. A última continua sendo 99. Um índice pode ser uma variável ou uma expressão, portanto scores[n - 1] acessa o último elemento quando n é a quantidade de elementos e é positivo. scores[5] não corresponde a nenhum elemento.

Conte elementos e bytes

sizeof scores mede todo o arranjo em bytes. Em um sistema com inteiros de 4 bytes, os cinco elementos ocupam 20 bytes. Dividir pelo tamanho de um elemento elimina a dependência do tamanho do inteiro:

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

Isso imprime 5. size_t é o tipo inteiro sem sinal usado para tamanhos; %zu o imprime. Esse cálculo funciona onde scores nomeia o arranjo propriamente dito. Um parâmetro de função não contém o tamanho completo do arranjo.

Mantenha tamanho e valores separados

Um nome como #define NUM_SCORES 5 permite que a declaração e seus laços compartilhem um tamanho. Em C17, uma declaração local const int n = 5; int a[n]; cria um arranjo de tamanho variável: const não transforma n em uma expressão constante inteira. Seu tamanho é escolhido quando a declaração é executada e permanece fixo durante o tempo de vida desse arranjo. Ele não pode usar uma lista de inicialização. O suporte a VLA é opcional em C11 e C17; C23 acrescenta constexpr para constantes em tempo de compilação.

Arranjos não podem ser atribuídos como um todo: b = a; é inválido. Copie seus elementos com um laço ou uma função de memória adequada. Para dois arranjos distintos, a == b compara endereços, não valores dos elementos. Compare os elementos correspondentes para testar a igualdade de valores. Uma comparação de bytes com memcmp testa suas representações armazenadas; valores de ponto flutuante iguais, como zero positivo e negativo, podem ter representações diferentes.

Principais conclusões

  • Os índices de um arranjo vão de 0 até uma unidade a menos que a quantidade de elementos.
  • Valores ausentes em inicializadores tornam-se zero; elementos locais não inicializados devem ser gravados antes do uso.
  • Use sizeof a / sizeof a[0] somente onde a é um arranjo propriamente dito.
  • Um arranjo mantém seu tamanho durante seu tempo de vida.
  • Copie e compare valores de arranjos elemento por elemento.

1.2 Percorra arranjos com laços sem ultrapassar seus limites

Um laço de contagem relaciona as posições de um arranjo a uma operação. Determine quais elementos contêm dados úteis e visite exatamente essas posições. O mesmo padrão permite calcular totais, fazer buscas e atualizar valores.

Visite os elementos em uso
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;

O laço usa os índices 0, 1, 2, 3 e 4. O total passa por 12, 27, 36, 50 e 60, portanto a média é 12,0. Escrever i <= 5 acrescenta um acesso inválido no índice 5. Essa passagem extra é um erro de uma unidade.

Para uma média, faça a divisão em ponto flutuante. Com um total de 61, 61 / 5 resulta no inteiro 12; 61 / 5.0 resulta em 12,2. Armazenar depois o primeiro resultado em um double não recupera a fração já descartada.

Encontre um máximo ou uma correspondência

Para um arranjo não vazio, inicialize o máximo com seu primeiro valor:

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

A primeira comparação substitui 12 por 15. Os valores restantes são menores, portanto 15 permanece. Começar por zero falharia para uma lista como {-8, -3, -11}: zero é maior que todos os valores armazenados, mas não está na lista.

Uma busca sequencial verifica os valores em ordem:

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

Aqui found torna-se 3. O valor -1 indica ausência de correspondência porque não pode ser um índice válido. Informe a falha somente após verificar todos os candidatos. Retornar -1 após o primeiro elemento diferente deixa de encontrar correspondências posteriores no arranjo.

Conte e reorganize

Um vetor de frequências armazena um contador por categoria. Para algarismos, int counts[10] = {0}; fornece contadores de 0 a 9. Após verificar d >= 0 && d < 10, counts[d]++ aumenta o contador do algarismo d. A verificação protege o arranjo quando o valor do algarismo vem da entrada.

Para inverter um arranjo, troque pares a partir de suas extremidades:

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

A primeira troca permuta 12 e 10. A segunda permuta 15 e 14. O 9 do meio permanece no lugar, resultando em {10, 14, 9, 15, 12}. Pare na metade: continuar pelas cinco posições trocaria cada par duas vezes.

Acompanhe a quantidade em uso separadamente da capacidade

Um arranjo com espaço para 100 valores pode conter apenas 37 entradas úteis no momento. Sua capacidade é 100; sua quantidade em uso é 37. Processe as posições abaixo de count. Para acrescentar um valor, verifique primeiro count < CAPACITY, armazene-o em a[count] e depois incremente count. Percorrer até a capacidade pode levar à leitura de posições que nunca receberam valores úteis, embora estejam fisicamente dentro do arranjo.

Principais conclusões

  • Use o limite estrito < no laço.
  • Use divisão em ponto flutuante quando uma média puder ter uma fração.
  • Inicialize o máximo de um arranjo não vazio com um elemento, inclusive quando todos os dados forem negativos.
  • Uma busca só é considerada malsucedida após a verificação de todos os candidatos.
  • Valide um valor antes de usá-lo como índice.
  • Inverta trocando apenas a primeira metade dos pares.
  • A quantidade em uso é o número de elementos utilizados; a capacidade é o espaço disponível.

1.3 Passe arranjos para funções

Uma função pode processar um arranjo sem receber uma cópia separada de cada elemento. A chamada compartilha o acesso ao armazenamento da função chamadora. Passe a quantidade de elementos junto com ele para que a função saiba onde parar.

Forneça os dados e seu comprimento
#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;
}

A função chamadora pode declarar int values[] = {7, 8, 10}; e chamar average(values, sizeof values / sizeof values[0]). As três adições produzem 25; converter sum para double antes da divisão produz aproximadamente 8,33. Retornar 0,0 quando não há valores é o comportamento escolhido para entrada vazia nessa função. Os valores pequenos deste exemplo mantêm a soma inteira dentro da faixa representável.

Em uma declaração de parâmetro, int a[] é ajustado para int *a. Ele recebe o endereço do primeiro elemento. Mesmo int a[10] não verifica nem exige dez elementos. A função chamadora deve fornecer um comprimento que corresponda aos dados disponíveis.

Por que sizeof perde o comprimento

No local da chamada, sizeof values mede o arranjo. Dentro da função, sizeof a mede o parâmetro ponteiro. Onde ponteiros ocupam 8 bytes e inteiros ocupam 4, dividir esses tamanhos resulta em 2, independentemente da quantidade de elementos da função chamadora. Esse resultado não descreve uma cópia de dois elementos nem o arranjo original. Mantenha o comprimento como parâmetro explícito.

size_t, declarado por cabeçalhos que incluem <stddef.h>, é o tipo sem sinal produzido por sizeof. Ele pode representar tamanhos de objetos, e %zu o imprime. Usá-lo para o índice do laço evita comparar um índice com sinal a um comprimento sem sinal.

Decida se os elementos podem mudar

O const em const int a[] impede atribuições aos elementos por meio desse parâmetro. Ele é adequado a uma função que calcula um resultado a partir dos dados. Uma função destinada a atualizar elementos o omite:

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

Após zero(values, 3), os três elementos da função chamadora contêm zero. O arranjo continua com três elementos. Um parâmetro escalar como n é uma cópia separada; alterar essa cópia não alteraria uma variável de contagem da função chamadora.

Para produzir vários resultados, deixe que a função chamadora forneça um arranjo de saída e seu comprimento. Uma função pode preencher out[i] com cada resultado. Funções em C não podem retornar um arranjo por valor, e retornar o endereço de um arranjo local falha porque esse arranjo deixa de existir quando a função retorna.

Conte de trás para a frente com um índice sem sinal

Um índice sem sinal não pode se tornar negativo. Em for (size_t i = n - 1; i >= 0; i--), o teste é sempre verdadeiro; após zero, ocorre redução modular para um número positivo grande. Com n == 0, até a subtração inicial sofre redução modular. Comece pela quantidade em uso:

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

Com três elementos, os índices acessados são 2, 1 e 0. Com zero elementos, o corpo nunca é executado. Inclua <stdio.h> para printf.

Principais conclusões

  • Passe o comprimento de um arranjo separadamente; não é possível recuperar a quantidade de elementos a partir de um parâmetro.
  • Gravações por meio de um parâmetro de arranjo alteram os elementos da função chamadora.
  • Use const para um parâmetro que apenas lê elementos.
  • A função chamadora fornece armazenamento para vários valores de saída.
  • Conte de trás para a frente a partir de n e acesse i - 1 ao usar size_t.
2

Represente texto com caracteres e cadeias terminadas em nulo

O texto começa com códigos de caracteres e termina com uma regra de armazenamento: uma cadeia em C para em seu primeiro byte zero. Aprenda como caracteres, literais e arranjos modificáveis diferem e depois use laços para contar e transformar texto. UTF-8 acrescenta uma distinção essencial entre um byte e um ponto de código completo. Acompanhar o texto e seu armazenamento explica por que uma palavra visível pode precisar de mais posições no arranjo do que suas letras sugerem.

2.1 Trate caracteres como códigos numéricos

Um caractere em C é armazenado como um código numérico. Isso permite que um programa imprima um símbolo, compare-o com outro ou faça operações aritméticas com seu código. O conjunto de caracteres determina qual número representa cada símbolo.

Leia o código e o caractere

Um char ocupa um byte: sizeof(char) é 1 por definição. Uma constante de caractere como 'A' tem tipo int. No conjunto de caracteres baseado em ASCII usado aqui, seu código é 65:

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

A saída é “A 65”. %c exibe o caractere; %d exibe seu número. ASCII atribui códigos de 0 a 127. Os algarismos vão de 48 a 57, as letras maiúsculas de 65 a 90, as minúsculas de 97 a 122 e o espaço tem código 32.

Aspas simples e aspas duplas criam coisas diferentes. 'A' é um código de caractere. "A" é um arranjo que contém 'A' e um byte zero terminador. Armazene um caractere com char c = 'A'; e compare-o usando c == 'A'.

Escreva caracteres que precisam de escapes
Escrita em CCaractere
'\n'Nova linha, código ASCII 10
'\t'Tabulação, código ASCII 9
'\\'Barra invertida
'\''Aspas simples
'\0'Caractere nulo, código 0

O caractere nulo é diferente do algarismo visível '0', cujo código ASCII é 48. Uma cadeia de caracteres usa o caractere nulo para marcar seu fim.

Transforme códigos em valores úteis

O padrão C garante códigos consecutivos para algarismos. Após verificar que c é um algarismo, c - '0' produz seu valor. Assim, '7' - '0' é 7, não 55. Subtrair o código inicial elimina a posição do algarismo dentro do conjunto de caracteres.

As letras ASCII também são consecutivas, portanto subtrair 'A' converte uma letra maiúscula em uma posição de 0 a 25. Um deslocamento de César de três posições usa:

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

Para 'X', subtrair 65 resulta em 23; somar três resulta em 26; o resto da divisão por 26 é zero; somar 65 retorna 'A'. Apenas 'X' + 3 resulta no código 91, que é '['. A aritmética com letras depende da ordem de letras do ASCII; C garante algarismos consecutivos, mas não exige letras consecutivas.

Classifique e converta com ctype.h

Inclua <ctype.h> para usar isdigit, isalpha, isalnum, isspace, isupper, islower, toupper e tolower. Os testes retornam um valor diferente de zero quando há correspondência e zero caso contrário. toupper('q') retorna 'Q'; toupper('3') deixa o algarismo inalterado. isspace reconhece separadores como espaço, tabulação e nova linha.

Um char sem qualificador pode ter sinal. Um byte acima de 127 pode então se tornar um valor negativo, que não é um argumento válido para essas funções, a menos que seja o marcador especial de entrada EOF. Ao processar bytes de uma cadeia, converta primeiro:

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

Na configuração regional “C” padrão, essas funções reconhecem as letras básicas, não caracteres UTF-8 completos com vários bytes. A conversão explícita torna o byte um argumento válido; ela não decodifica UTF-8.

Principais conclusões

  • Constantes de caractere usam aspas simples; literais de cadeia de caracteres usam aspas duplas.
  • \0 é um caractere de valor zero, não o algarismo 0.
  • Subtraia '0' de um caractere de algarismo para obter seu valor.
  • A aritmética de posições de letras aqui usa a ordem ASCII.
  • Converta um byte de cadeia para unsigned char antes de chamar uma função ctype.
  • Um teste ctype retorna um valor diferente de zero quando há correspondência, não necessariamente 1.

2.2 Armazene texto em cadeias terminadas em nulo

Uma cadeia de caracteres em C é uma sequência de caracteres terminada em '\0'. O terminador faz parte de seu armazenamento, mas não do comprimento do texto. C não tem um tipo interno separado para cadeias de caracteres: os caracteres ocupam um arranjo.

Veja os bytes no arranjo
char word[] = "cat";
char name[20] = "Ada";

word contém quatro bytes: 'c', 'a', 't', '\0'. O tamanho de seu arranjo é 4 e strlen(word) é 3. Inclua <string.h> para usar strlen. O arranjo maior name contém as três letras, seu terminador e mais dezesseis bytes zero. Sua capacidade é 20, enquanto seu comprimento atual é 3.

Um inicializador caractere por caractere pode criar a mesma cadeia:

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

Omitir o último elemento cria três caracteres sem um terminador de cadeia. C também permite char word[3] = "cat";, que preenche as três posições e omite o byte zero. Esse arranjo não é uma cadeia de caracteres em C. Uma função como strlen ou printf com %s continua lendo até encontrar um byte zero, portanto não pode consumir com segurança esse arranjo de três bytes como texto.

Escolha um arranjo modificável ou um literal
DeclaraçãoOnde está o textoSeus caracteres podem mudar?
char a[] = "hello";Uma cópia no arranjo aSim
const char *p = "hello";Um literal de cadeia ao qual p se refereNão

a[0] = 'H'; altera a cópia para “Hello”. Um literal não deve ser modificado. Mesmo que seja atribuído a um char * sem qualificador, gravar por esse ponteiro causa comportamento indefinido. Escrever const char * torna essa restrição visível ao compilador.

O compilador une literais de cadeia adjacentes: "Hello, " "world" produz o mesmo texto que "Hello, world". A cadeia resultante continua tendo um único terminador final. Isso é útil para dividir um literal longo entre linhas do código-fonte.

Imprima e altere o texto
printf("%s\n", word);
printf("%c\n", word[0]);

A primeira chamada imprime a cadeia inteira. A segunda imprime seu primeiro caractere. Um literal de cadeia em printf fica entre aspas duplas porque é um texto que contém um formato; %c recebe um código de caractere.

Uma atribuição a um elemento altera um caractere armazenado. Com char name[20] = "Ada";, definir name[0] = 'E'; produz “Eda”. Definir name[1] = '\0'; em seguida produz “E” como cadeia. Os bytes de 'a' e do terminador antigo ainda existem mais adiante no arranjo, mas as operações de cadeia param no primeiro zero.

Um arranjo não pode receber texto substituto usando name = "Bob"; após a declaração. Copie caracteres para ele, usando uma chamada verificada de strcpy ou um laço, e inclua o terminador. O comprimento do texto substituto deve ser menor que a capacidade.

Represente a ausência de texto

A cadeia vazia "" ainda ocupa um byte para '\0'. Ela tem comprimento zero e pode ser testada com name[0] == '\0'. Uma cadeia vazia é uma cadeia real com um terminador; um ponteiro nulo, por sua vez, não aponta para nenhum armazenamento de cadeia.

Principais conclusões

  • Uma cadeia de caracteres em C termina em seu primeiro '\0'.
  • A capacidade inclui o terminador; o comprimento da cadeia não o inclui.
  • Um arranjo de caracteres sem terminador não pode ser passado como uma cadeia de caracteres em C.
  • Cópias de literais em arranjos são modificáveis; os próprios literais não são.
  • Gravar um terminador antes no arranjo encurta o texto sem redimensionar o arranjo.

2.3 Processe cadeias de caracteres um caractere por vez

O terminador de uma cadeia fornece a um laço seu ponto de parada. Visite os bytes anteriores a ele para contar, converter ou filtrar texto. Ao criar uma nova cadeia, o próprio programa deve gravar um novo terminador.

Percorra até o fim uma vez
for (size_t i = 0; s[i] != '\0'; i++) {
    /* Process s[i]. */
}

A cada passagem, o teste verifica se o byte atual pertence ao texto. Ele para antes do terminador. Chamar strlen(s) nesse teste pode percorrer novamente toda a cadeia a cada passagem. Teste o byte atual diretamente ou calcule size_t len = strlen(s); uma vez, quando o comprimento permanecerá inalterado.

Para converter para maiúsculas, use um arranjo modificável e <ctype.h>:

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

“Hello, World 42” torna-se “HELLO, WORLD 42”. A conversão preserva a vírgula, os espaços e os algarismos. A conversão para um tipo sem sinal torna cada byte armazenado um argumento válido para ctype.

Conte categorias e inícios de palavras

O original “Hello, World 42” tem 15 bytes de texto: 10 letras, dois algarismos, dois espaços e uma vírgula. Contadores separados podem usar isalpha, isdigit e isspace. Decida o que é uma palavra antes de contá-la. Para uma sequência de letras, uma nova palavra começa quando uma letra vem após um caractere que não é letra ou é o primeiro caractere:

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;
}

O exemplo tem duas sequências de letras, “Hello” e “World”. Os algarismos não formam uma palavra segundo essa definição. Atualizar in_word a cada passagem permite que a pontuação encerre uma sequência.

Inverta apenas o texto

Para uma cadeia de caracteres de um byte, obtenha len uma vez e depois troque s[i] por s[len - 1 - i] enquanto i < len / 2. Inverter “stressed” troca sua primeira e última letras, depois o par seguinte, até se tornar “desserts”. O byte zero permanece no índice len. Começar uma troca em s[len] moveria esse zero para o início. Inverter bytes dessa forma é adequado para texto ASCII; caracteres de vários bytes precisam ser decodificados antes de serem reorganizados.

Crie um resultado filtrado

Use índices diferentes para leitura e escrita, pois alguns bytes de entrada são ignorados:

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';

O resultado é “abc”. O destino tem espaço suficiente porque a filtragem não pode aumentar essa entrada. Sem out[j] = '\0';, a saída não tem um fim de cadeia definido.

Para uma cifra de César, desloque letras maiúsculas em relação a 'A' e letras minúsculas em relação a 'a', depois copie os outros bytes sem alteração. Com chave 13, “Hello, World!” torna-se “Uryyb, Jbeyq!”. Repetir esse deslocamento restaura o original porque dois deslocamentos totalizam 26. Uma comparação como s[i] == 'a' testa um caractere; use strcmp para comparar o texto completo.

Principais conclusões

  • Teste o byte atual ou guarde o comprimento para evitar percursos completos repetidos.
  • Mantenha índices de leitura e escrita separados ao filtrar.
  • Grave um terminador após criar uma nova cadeia.
  • Inverta os bytes do texto sem mover o terminador.
  • Defina um limite de palavra antes de escrever um contador de palavras.
  • Aplique deslocamentos de letras ASCII separadamente a letras maiúsculas e minúsculas.

2.4 Explique como o texto UTF-8 é armazenado em arranjos de char

O comprimento de um texto pode significar uma contagem de bytes ou uma contagem de pontos de código Unicode. Essas contagens frequentemente diferem. Um arranjo de char em C expõe bytes individuais, portanto seus índices e funções de cadeia não acompanham automaticamente caracteres não ASCII completos.

Separe caracteres, pontos de código e bytes

Uma codificação de caracteres descreve como o texto é armazenado. ASCII usa códigos de 0 a 127 para letras básicas, algarismos, pontuação e caracteres de controle. Unicode atribui pontos de código a caracteres de muitos sistemas de escrita: “é” tem o ponto de código U+00E9 e “€” tem U+20AC. UTF-8 codifica valores escalares Unicode usando de um a quatro bytes.

Os pontos de código ASCII mantêm suas codificações de um byte. O “é” pré-composto usa dois bytes, 0xC3 e 0xA9; “€” usa três. Muitos pontos de código individuais de emoji usam quatro bytes. Um símbolo exibido pode conter vários pontos de código, portanto mesmo uma contagem de pontos de código nem sempre é uma contagem de símbolos visíveis.

Meça uma cadeia UTF-8
char s[] = "café";
printf("%zu %zu\n", strlen(s), sizeof s);

Com texto de execução em UTF-8, isso imprime “5 6”. As três primeiras letras usam três bytes e “é” usa dois, totalizando cinco bytes de texto. O terminador acrescenta o sexto byte de armazenamento. s[3] é o primeiro byte de “é”, não a letra inteira.

Um corte após quatro bytes manteria 0xC3, mas removeria seu byte de continuação 0xA9. O resultado deixaria de ser UTF-8 válido. Portanto, um limite de buffer expresso em bytes precisa de uma verificação de limite de caractere se o programa truncar texto UTF-8.

Conte bytes iniciais em UTF-8 válido

Um byte de continuação começa com os bits 10. Seu valor está entre 0x80 e 0xBF. Um byte inicial ou byte ASCII não tem esse padrão:

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++;
}

A máscara 0xC0 mantém apenas os dois bits mais significativos; 0x80 representa o padrão 10 nessas posições. O teste conta “café” como quatro pontos de código ao contar seus quatro bytes iniciais. Ele pressupõe UTF-8 válido e não valida sequências malformadas nem combina pontos de código em símbolos visíveis.

Escolha operações que preservem a codificação

Copiar uma cadeia UTF-8 completa preserva seus bytes. Concatenar cadeias completas, comparar suas sequências de bytes para verificar igualdade e buscar um caractere ASCII ou uma subcadeia UTF-8 completa também funcionam. Bytes de continuação UTF-8 não podem ser confundidos com caracteres ASCII ou bytes iniciais.

strcmp ordena valores de bytes sem sinal, portanto sua ordem difere da ordem alfabética específica de cada idioma. Em UTF-8, letras latinas acentuadas comuns têm bytes iniciais acima do ASCII 'z'. Uma comparação de bytes, portanto, coloca essas letras depois de 'z', independentemente das convenções de dicionário.

Na configuração regional “C” padrão, toupper e isalpha processam um byte e não decodificam “é” como uma letra. Converter para unsigned char torna a chamada válida, mas não a transforma em uma conversão Unicode entre maiúsculas e minúsculas. Funções de caracteres largos ou uma biblioteca Unicode são necessárias para essa tarefa. Um laço de César restrito a ASCII pode preservar letras acentuadas alterando apenas bytes de A–Z ou a–z.

O byte zero é a codificação UTF-8 de U+0000; outros pontos de código codificados não contêm byte zero. Isso se ajusta à convenção de terminador de C. UTF-16 pode colocar bytes zero dentro de texto comum codificado, portanto um arranjo de bytes UTF-16 não pode ser processado como uma cadeia comum de char.

Principais conclusões

  • strlen conta bytes, inclusive em texto UTF-8.
  • Um ponto de código Unicode pode ocupar vários bytes.
  • Um índice de char pode apontar para o interior de um caractere codificado.
  • Contar bytes que não são de continuação só conta pontos de código em UTF-8 válido.
  • A igualdade byte a byte funciona para texto codificado de forma idêntica; a ordem dos bytes não é a ordem de dicionário.
  • Chamadas ctype que operam em um byte não decodificam UTF-8.
3

Use as funções padrão para cadeias de caracteres

A biblioteca padrão fornece operações comuns de texto, mas cada função espera um tipo específico de entrada e um significado específico para seu argumento de tamanho. Aprenda a medir, copiar, acrescentar, comparar e buscar cadeias de caracteres e depois escolha formatação com limite ou operações com bytes quando forem adequadas. Verificar a capacidade e interpretar os valores de retorno transforma uma chamada curta de função em uma operação previsível sobre um espaço de armazenamento conhecido.

3.1 Meça, copie e concatene cadeias de caracteres

As funções de <string.h> trabalham com texto terminado em nulo. Elas podem encontrar seu comprimento, copiá-lo ou acrescentar mais texto, mas as funções de cópia não descobrem a capacidade do destino. Calcule o espaço necessário antes de gravar.

Meça o comprimento sem o terminador

strlen(s) retorna um size_t que indica a quantidade de bytes antes do primeiro byte zero. Assim, strlen("hello") é 5 e strlen("") é zero. Imprima o resultado com %zu.

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

Isso imprime “2 64”. O primeiro resultado mede o texto atual; o segundo mede o espaço disponível no arranjo. Uma cópia posterior pode alterar o primeiro sem alterar o segundo. As duas operações são úteis aqui porque buf é um arranjo propriamente dito e já contém uma cadeia válida.

Copie uma cadeia completa

strcpy(dest, src) copia o texto e seu byte zero final. A capacidade necessária é o comprimento da origem mais um:

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

“Maple” usa cinco bytes de texto e um terminador, portanto cabe. O < estrito é importante: uma origem cujo comprimento seja igual à capacidade não deixa espaço para seu byte zero. Se a verificação falhar, o programa deve tratar o erro antes de usar o destino como uma nova cadeia.

Uma atribuição de arranjo como dest = src; não pode substituir seu conteúdo. Atribuir uma variável char * a outra é permitido, mas apenas copia uma referência ao mesmo espaço de armazenamento. Isso não cria outra cópia dos caracteres.

Acrescente a uma cadeia existente

strcat(dest, src) primeiro encontra o terminador que já está em dest. Ela substitui esse terminador pelo primeiro byte da origem e grava um novo terminador após o texto acrescentado. A capacidade do destino deve comportar os dois comprimentos atuais mais um.

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

Os comprimentos crescem de 3 para 4 e depois para 12. O resultado final “Ada Lovelace” precisa de 13 bytes de armazenamento, deixando três bytes sem uso. Um destino de 12 bytes seria pequeno demais por um byte, apesar de corresponder ao comprimento do texto visível.

Nunca acrescente texto a um buffer local não inicializado. char out[20]; strcat(out, "hi"); não fornece a strcat um fim de texto definido para encontrar. Comece com char out[20] = "";, grave out[0] = '\0'; ou faça da primeira operação uma cópia verificada.

Considere a sobreposição e o custo dos percursos

A origem e o destino não devem se sobrepor em strcpy ou strcat. Mover texto dentro do mesmo buffer pode criar uma sobreposição mesmo quando as duas posições são válidas individualmente. memmove é a função destinada a regiões de bytes sobrepostas.

strlen precisa percorrer a cadeia até o terminador. strcat precisa percorrer o destino atual antes de acrescentar texto. Portanto, acréscimos repetidos em um laço percorrem novamente o texto já criado. Ao montar várias partes, mantenha o comprimento atual da saída e escreva cada parte nesse índice, verificando a capacidade restante e mantendo o terminador. Um comprimento conhecido só é útil enquanto corresponder ao conteúdo real.

Principais conclusões

  • strlen mede o texto atual; sizeof aplicado a um arranjo mede sua capacidade.
  • Uma cópia precisa do comprimento da origem mais um byte.
  • Um acréscimo precisa dos comprimentos das duas cadeias mais um byte.
  • Inicialize o destino como uma cadeia antes de acrescentar texto.
  • Copiar ou acrescentar com regiões de cadeia sobrepostas é inválido.

3.2 Compare cadeias de caracteres e faça buscas nelas

A comparação de cadeias responde a perguntas sobre o texto, enquanto a busca encontra uma posição dentro dele. Nenhuma das duas tarefas é realizada comparando nomes de arranjos com ==. Use o resultado da biblioteca de acordo com o que ele representa: o sinal de uma comparação, um comprimento ou uma posição correspondente.

Compare texto e prefixos

strcmp(a, b) compara os bytes correspondentes como valores unsigned char até encontrar uma diferença ou o fim da cadeia. Seu resultado é zero para textos iguais, negativo quando a vem primeiro e positivo quando vem depois. Apenas o sinal é especificado; não espere exatamente -1 ou 1.

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

O teste explícito de zero significa igualdade. Um simples if (strcmp(command, "quit")) entra no bloco quando os textos são diferentes, pois um valor diferente de zero é verdadeiro. Comparar command == "quit" compararia posições de armazenamento.

Na ordem ASCII, “apple” vem antes de “banana” pela primeira letra. “Zebra” vem antes de “apple” porque o 'Z' maiúsculo tem código 90 e o 'a' minúsculo tem código 97. “app” vem antes de “apple” porque seu byte zero é encontrado antes do 'l' da outra cadeia. Essa é uma ordenação por bytes, não uma comparação de dicionário.

strncmp(a, b, n) compara no máximo n bytes. strncmp(s, "--", 2) == 0 reconhece um prefixo de dois hífens, mas não significa que toda a cadeia seja apenas “--”. O C padrão não tem uma função de comparação de cadeias inteiras que ignore maiúsculas e minúsculas. Um programa pode comparar os bytes convertidos por conta própria; POSIX fornece strcasecmp em <strings.h>.

Busque uma posição

Use strchr para a primeira ocorrência de um caractere, strrchr para a última e strstr para a primeira subcadeia completa. Elas retornam um ponteiro para a posição correspondente ou NULL se não houver correspondência.

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

Isso imprime “.txt”, incluindo o ponto encontrado. Começar pelo primeiro ponto com strchr imprimiria “.final.txt”. strstr(name, "final") encontra o trecho final que começa com “final.txt”; buscar “draft” retorna NULL. Verifique se o resultado é NULL antes de imprimi-lo ou lê-lo.

Localize um separador ou divida texto

strcspn(s, set) conta os bytes iniciais que não correspondem a nenhum dos caracteres em set. Com "oak:pine,birch" e o conjunto ":,", ela para nos dois-pontos. Para uma cadeia de entrada válida, este padrão remove a primeira nova linha:

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

Se não houver nova linha, o índice retornado é o comprimento da cadeia, portanto a atribuição regrava o terminador que já está lá.

strtok divide uma cadeia modificável usando qualquer caractere de um conjunto de delimitadores:

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

Os tokens são “red”, “green” e “blue”. A primeira chamada fornece o arranjo; as chamadas seguintes passam NULL para continuar. A função grava bytes zero na entrada e mantém estado oculto entre chamadas. Use armazenamento modificável e não espere que a cadeia original com seus separadores permaneça intacta. Delimitadores consecutivos não produzem tokens vazios.

Principais conclusões

  • Use strcmp(a, b) == 0 para verificar se os textos são iguais.
  • Interprete um resultado de comparação pelo sinal, não pelo valor exato.
  • Uma correspondência de prefixo não estabelece a igualdade das cadeias inteiras.
  • Resultados de busca precisam ser verificados quanto a ponteiro nulo.
  • strcspn retorna o comprimento de um prefixo; strtok modifica sua entrada e mantém sua posição.
  • A ordenação de cadeias segue os códigos dos bytes sem sinal.

3.3 Copie respeitando os limites e trabalhe diretamente com bytes de memória

Um limite de comprimento só é útil quando descreve a quantidade correta. Algumas funções limitam os bytes da origem, outras limitam a capacidade do destino, e as funções de memória lidam diretamente com bytes sem terminadores de cadeia. Leia o contrato de cada função antes de escolher seu limite.

Saiba o que n significa

strncpy(dest, src, n) grava exatamente n posições quando a origem é menor, preenchendo o restante com bytes zero. Quando a origem tem pelo menos n bytes de texto, ela copia esses bytes sem um terminador. Para um arranjo com capacidade positiva, uma cópia truncada e terminada é:

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

A atribuição final é essencial quando a origem preenche o limite. Truncar um nome ou comando ainda pode mudar seu significado, portanto rejeite entradas longas demais quando for necessária uma cópia completa.

strncat(dest, src, n) acrescenta no máximo n bytes de texto e depois um terminador. Ela não recebe a capacidade total. Para uma cadeia válida que já está em um arranjo de destino, o espaço restante para texto é sizeof dest - strlen(dest) - 1. Passar sizeof dest como n pode causar estouro.

Formate e detecte truncamento

snprintf, declarada em <stdio.h>, recebe a capacidade total do destino. Com capacidade maior que zero, grava no máximo uma unidade a menos de bytes de texto e depois um 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");
}

O resultado completo “item-2026” tem nove bytes de texto. O buffer armazena “item-20” e seu byte zero, enquanto needed é 9. O valor de retorno conta o texto que teria sido produzido, sem incluir o terminador. Verifique se o resultado é negativo antes de convertê-lo para size_t.

Copie e preencha bytes

memcpy(dest, src, n) copia exatamente n bytes, incluindo quaisquer bytes zero, sem procurar um terminador. Para dois arranjos de inteiros do mesmo tamanho, memcpy(b, a, sizeof a) copia todo o espaço que ocupam quando as regiões não se sobrepõem. Onde cada um de cinco inteiros ocupa quatro bytes, essa quantidade é 20 bytes. memmove copia bytes com comportamento definido para regiões sobrepostas.

memset(a, 0, sizeof a) preenche cada byte com zero e zera um arranjo de inteiros. Seu segundo argumento descreve cada byte, não cada elemento. Com inteiros de quatro bytes, preencher com 1 produz os bytes 01 01 01 01, resultando em 16.843.009 na representação usual, não no inteiro 1. Use um laço para atribuir valores inteiros diferentes de zero.

memcmp(a, b, n) compara bytes armazenados. Zero significa que essas sequências de bytes são iguais. Para verificar a igualdade de valores, compare os elementos: 0.0 e -0.0 em ponto flutuante são iguais na comparação, embora suas representações possam ser diferentes.

Reconheça outras famílias de bibliotecas

strlcpy e strlcat do BSD foram incorporadas ao POSIX em 2024, mas não são funções principais de C17. Elas retornam o comprimento que tentaram criar; um resultado pelo menos igual ao tamanho fornecido indica truncamento. strlcpy termina o destino quando o tamanho é positivo. strlcat só o termina se o texto existente terminar dentro desse tamanho. Elas estão disponíveis na glibc 2.38 e posteriores. Funções opcionais do Anexo K, como strcpy_s, estão ausentes de muitas bibliotecas, incluindo a glibc.

Principais conclusões

  • strncpy pode omitir o terminador; strncat precisa de espaço para um novo.
  • snprintf retorna o comprimento completo necessário para o texto ou um erro negativo.
  • Um resultado não negativo de formatação pelo menos igual à capacidade indica truncamento.
  • Funções de memória contam bytes e não param no zero.
  • Use memmove para regiões sobrepostas.
  • memset preenche bytes; memcmp compara representações.
4

Organize dados em arranjos multidimensionais

Tabelas organizam valores por mais de uma posição. Uma tabela em C armazena linhas de mesma largura em sequência, e laços aninhados permitem que um programa trabalhe por linha ou por coluna. Aprenda a declarar esse formato, passá-lo para uma função e manter ambos os índices válidos. Listas de texto apresentam uma escolha útil: linhas de caracteres modificáveis reservam espaço fixo, enquanto arranjos de ponteiros se referem a cadeias armazenadas em outro lugar.

4.1 Declare e percorra matrizes bidimensionais

Uma tabela precisa de um índice de linha e um índice de coluna. Em C, uma matriz bidimensional é um arranjo cujos elementos também são arranjos. Cada linha tem a mesma quantidade de elementos, e cada índice tem seu próprio limite.

Declare linhas e colunas
int grid[3][4];
int m[2][3] = {{1, 2, 3}, {4, 5, 6}};

grid contém três linhas com quatro inteiros cada: doze inteiros no total. Seus índices de linha vão de 0 a 2; seus índices de coluna vão de 0 a 3. grid[1][2] acessa o terceiro valor da segunda linha.

As chaves aninhadas do inicializador mostram o agrupamento por linhas. Elementos sem inicialização explícita tornam-se zero. Você pode deixar o compilador contar as linhas em int m[][3] = {{1, 2, 3}, {4, 5, 6}};, mas a quantidade de colunas deve ser informada. Sem ela, o compilador não poderia determinar onde cada linha termina.

Acompanhe o armazenamento por linhas

C armazena uma linha completa antes de começar a próxima. Em grid, os primeiros quatro inteiros armazenados formam a linha 0, os quatro seguintes formam a linha 1 e os últimos quatro formam a linha 2. grid[1][2] é o sétimo inteiro a partir do início, na posição de elemento 6 com indexação a partir de zero: uma linha completa de quatro, seguida de mais dois elementos.

Onde inteiros ocupam quatro bytes, uma linha usa dezesseis bytes e a tabela inteira usa quarenta e oito. No local da declaração:

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

A primeira divisão resulta em 3 e a segunda em 4. Uma linha é, por si só, um arranjo, por isso sizeof grid[0] mede mais de um inteiro.

Percorra e some a tabela
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;
}

A primeira linha produz 11 e a segunda, 8. Somar esses totais das linhas resulta em 19. Reinicializar row_sum dentro do laço externo inicia um novo total para cada linha. Se ela fosse inicializada apenas uma vez antes dos dois laços, o segundo valor impresso também incluiria a primeira linha.

Para totais por coluna, coloque o laço de colunas fora e o de linhas dentro, continuando a acessar sales[r][c]. Os três totais passam a ser 9, 2 e 8, que novamente somam 19. Alterar a ordem do percurso não exige alterar o significado dos dois índices. Percorrer primeiro por linha segue o armazenamento contíguo e frequentemente melhora o uso do cache em tabelas grandes.

Mantenha cada dimensão dentro dos limites

grid[c][r] troca os significados dos índices. Se c chegar a 3, solicita uma quarta linha que não existe. O fato de o objeto completo conter doze inteiros não permite ultrapassar o limite de uma linha com um índice de coluna.

Escreva duas operações de indexação separadas: grid[1][2]. Em C17, grid[1, 2] avalia a expressão com vírgula e seleciona grid[2], uma linha, em vez de um elemento. Mais dimensões aplicam a mesma ideia repetidamente: int cube[2][3][4] tem 24 elementos inteiros, selecionados por três índices.

Principais conclusões

  • Uma matriz bidimensional é um arranjo de linhas de mesma largura.
  • As linhas são armazenadas uma após a outra, em armazenamento por linhas.
  • Mantenha separados os limites das linhas e das colunas.
  • Reinicialize um acumulador de linha ou coluna ao começar um novo grupo.
  • Somente a primeira dimensão pode ser inferida a partir de um inicializador.

4.2 Passe tabelas para funções e armazene listas de cadeias de caracteres

Uma função que recebe uma tabela precisa de informações suficientes para identificar cada linha. Uma lista de cadeias de caracteres acrescenta outra escolha: armazenar os caracteres em linhas de mesma largura ou armazenar referências a cadeias de comprimentos diferentes.

Informe a largura da linha em um parâmetro de tabela
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;
        }
    }
}

A primeira dimensão pode ser omitida porque a quantidade de linhas é passada separadamente. A largura 4 define o tipo de cada linha. Sem ela, g[r][c] não teria um espaçamento conhecido entre linhas. Uma declaração com int g[][] é inválida. Alterações feitas por meio desse parâmetro permanecem na tabela da função chamadora; um parâmetro de tabela somente para leitura pode usar const int g[][4].

Desde C99, compiladores que aceitam parâmetros de tipo variavelmente modificado aceitam dimensões declaradas anteriormente na lista de parâmetros:

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

GCC e Clang aceitam essa forma de C17. Ela permite que uma função receba linhas de larguras diferentes; passe dimensões positivas que correspondam à tabela real. C23 exige suporte a esses tipos de parâmetro, enquanto objetos automáticos de arranjos de tamanho variável continuam sendo um recurso opcional separado. Parâmetros de largura fixa são úteis quando o suporte do compilador é limitado.

Armazene cadeias em linhas
char days[7][10] = {
    "Monday", "Tuesday", "Wednesday", "Thursday",
    "Friday", "Saturday", "Sunday"
};

Cada linha tem espaço para dez caracteres, independentemente do comprimento atual de seu texto. “Wednesday” precisa das dez posições: nove letras e um terminador. days[2] seleciona essa linha inteira como uma cadeia; days[2][0] seleciona 'W'.

Uma linha pode ser impressa com %s, medida com strlen ou comparada com strcmp. Ela também pode receber uma cópia verificada. strcpy(days[0], "Mon"); cabe em dez bytes e altera o texto da linha 0. A linha continua com dez bytes de largura, e a linha seguinte permanece na mesma posição. Um texto substituto de dez bytes precisaria de onze bytes e não caberia.

Armazene referências a literais
const char *names[] = {"Ann", "Bartholomew", "Li"};

Isso declara um arranjo de ponteiros. Cada elemento se refere a um literal com seu próprio comprimento mais um terminador. names[1] refere-se a “Bartholomew”, e names[1][0] é 'B'. Os literais não devem ser modificados.

Os elementos ponteiro podem mudar, embora os caracteres dos literais não possam. names[0] = "Anna"; altera o literal ao qual o primeiro elemento se refere; isso não amplia “Ann” no próprio local. Na forma com linhas 2D, um elemento é um arranjo de caracteres de largura fixa e não pode receber uma nova atribuição dessa maneira. Escolha linhas modificáveis para texto editado no próprio local e referências a literais para uma lista de opções de texto fixas. O parâmetro de linha de comando argv usa uma estrutura de arranjo de ponteiros, embora os caracteres dos argumentos fornecidos sejam modificáveis.

Principais conclusões

  • Parâmetros de tabela precisam de todas as dimensões após a primeira.
  • Uma dimensão passada separadamente deve corresponder à tabela real.
  • Cada linha de cadeia precisa de seu próprio terminador dentro de sua largura fixa.
  • Um arranjo de ponteiros armazena referências, não linhas de caracteres de mesma largura.
5

Processe argumentos de linha de comando

Uma linha de comando fornece texto a um programa na inicialização. A contagem de argumentos, o agrupamento pelo shell e as operações com cadeias permitem que o programa interprete esse texto sem supor que um argumento exista ou já contenha um número. Siga um caminho com verificações de argv até um valor utilizável: verifique sua presença, faça a conversão completa, detecte erros de conversão e respeite a faixa que o programa realmente aceita.

5.1 Leia argc e argv

Argumentos de linha de comando permitem que a função chamadora forneça texto ao iniciar um programa. O ambiente de execução disponibiliza essas cadeias para main. Verifique quantas chegaram antes de acessá-las por índices.

Leia a 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;
}

Inclua <stdio.h> para a saída. argc é a contagem de argumentos e argv é a lista de cadeias. A forma equivalente do parâmetro char **argv descreve a mesma interface. O laço começa em 1 porque o índice 0 normalmente contém o nome usado para iniciar o programa.

Em um shell comum, executar ./greet Ada Lovelace fornece três cadeias: “./greet”, “Ada” e “Lovelace”. Assim, argc é 3 e o último argumento do usuário é argv[2]. Um ponteiro nulo fica em argv[argc], aqui argv[3]; ele é uma sentinela após as cadeias de argumentos propriamente ditas.

Deixe o shell agrupar o texto

Aspas afetam a forma como o shell monta os argumentos. Executar ./greet "Ada Lovelace" fornece um argumento do usuário contendo um espaço. As aspas agrupam as palavras e não se tornam parte do texto do argumento. argc agora é 2.

Shells semelhantes ao Unix também podem expandir padrões antes de o programa começar. Em ./count *.txt, o programa geralmente recebe os nomes encontrados pelo shell como argumentos separados. Ele recebe as cadeias resultantes, não uma solicitação para expandir o padrão por conta própria. O comportamento exato da expansão depende do shell e de suas configurações.

Rejeite um argumento ausente ou extra

Se um programa precisar de exatamente um argumento do usuário, exija argc == 2 antes de usá-lo:

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

Quando argc é 1, argv[1] é o ponteiro nulo sentinela e não pode ser passado para strlen nem impresso com %s. Índices além da sentinela estão fora do arranjo de argumentos.

Retornar zero de main indica sucesso. Um código diferente de zero, como o 1 acima, informa ao shell que o programa não foi concluído com sucesso. <stdlib.h> também fornece EXIT_SUCCESS e EXIT_FAILURE.

Trate cada argumento como uma cadeia

argv[i][0] acessa o primeiro caractere do argumento; strlen(argv[i]) mede seus bytes de texto; strcmp(argv[i], "-v") == 0 verifica uma opção completa. Um argumento com aparência numérica ainda é texto e deve ser convertido antes de operações aritméticas.

argv[0] geralmente registra como o programa foi iniciado, mas pode estar vazio quando essa informação não está disponível. Ele é útil para mensagens, não como verificação de uma identidade fixa. Os caracteres dos argumentos podem ser modificados durante o programa, mas o espaço fornecido para eles não permite supor que um texto substituto mais longo caberá. Para acrescentar uma extensão ou aumentar o texto de outra forma, copie-o primeiro para um buffer dimensionado separadamente.

Principais conclusões

  • argc conta as cadeias de argumentos fornecidas, incluindo a entrada normal com o nome usado para iniciar o programa.
  • Verifique a contagem de argumentos antes de ler um argumento do usuário.
  • argv[argc] é um ponteiro nulo, não uma cadeia de argumento vazia.
  • Aspas no shell podem manter espaços dentro de um argumento.
  • Use operações de cadeia para o texto dos argumentos e conversão para números.

5.2 Converta e valide argumentos numéricos

Um argumento numérico chega como caracteres, não como um inteiro. Uma conversão confiável verifica três questões distintas: algum algarismo formou um número, a conversão usou o texto necessário e o valor resultante é permitido?

Evite perder informações sobre erros

atoi, declarada em <stdlib.h>, faz uma conversão simples. Ela transforma “13” em 13, mas também transforma “12abc” em 12. Ela retorna zero tanto para “0” quanto para um texto como “abc” que não começa com um número. Um resultado fora da faixa representável causa comportamento indefinido, portanto um teste que aceita apenas algarismos seguido de atoi ainda é inseguro para entradas muito longas.

Uma regra de formato como “somente um ou mais algarismos” precisa de um percurso separado. Rejeite uma cadeia vazia e exija que cada byte satisfaça isdigit((unsigned char) c). Essa regra aceita “13” e rejeita “13a”, “-5” e “”. Ela verifica a forma escrita, não se o valor cabe em um inteiro da máquina.

Use strtol e examine onde ela parou
#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;
}

Esse trecho vem após uma verificação da contagem de argumentos e usa <stdio.h> para puts. O 10 seleciona a conversão decimal. end recebe a posição em que a conversão parou; &end permite que a função armazene essa posição. *end lê o caractere nessa posição. Não é necessária aritmética de ponteiros.

Para “13a”, o valor retornado é 13, mas *end é 'a', portanto o texto é rejeitado. Para uma cadeia vazia ou “abc”, end é igual ao início do argumento, mostrando que nenhum número foi consumido. Ambas as verificações são necessárias: testar apenas o zero final aceitaria erroneamente uma cadeia vazia.

Antes de cada conversão, redefina errno como zero. Um valor fora da faixa de long faz strtol retornar um extremo da faixa e definir ERANGE. Sem a redefinição, um erro antigo poderia parecer pertencer a essa conversão. Verifique antes de converter o resultado para a faixa menor de int; use INT_MIN e INT_MAX de <limits.h> quando toda a faixa de int for o domínio aceito.

Aplique a regra do próprio programa

strtol aceita caracteres de espaço em branco no início e um sinal opcional. Decida se essas formas são permitidas. Se a interface exigir apenas algarismos, faça o percurso que verifica se a cadeia não está vazia e contém apenas algarismos, além das verificações de conversão. Após uma conversão bem-sucedida, verifique a faixa permitida pela aplicação antes de usar um valor como quantidade ou índice. “0” pode ser um texto numérico perfeitamente válido e ainda assim ser inválido para uma quantidade que deve ser positiva.

Um programa de César que aceita uma chave não negativa verifica primeiro argc, depois se ela contém apenas algarismos e, em seguida, o resultado completo de strtol e sua faixa. Reduzir uma chave válida com % 26 produz um deslocamento de 0 a 25 antes da aritmética com letras. A chave 27 tem o efeito da chave 1. Faça a verificação de faixa antes de uma conversão explícita para um tipo de faixa menor ou de qualquer operação aritmética que possa causar estouro. Para valores decimais, strtod oferece a interface correspondente para posição final e erro de faixa.

Principais conclusões

  • Argumentos com aparência numérica ainda precisam de conversão.
  • atoi não consegue distinguir zero válido de uma falha de conversão.
  • Verifique se strtol leu algo e consumiu todo o texto necessário.
  • Redefina errno, verifique ERANGE e depois verifique a faixa permitida pela aplicação.
  • Um percurso que verifica apenas algarismos não descarta a possibilidade de estouro.
  • Valide um valor antes de convertê-lo para um tipo de faixa menor ou usá-lo como índice.
6

Evite acessos fora dos limites e estouros de buffer

Erros de limites relacionam várias ideias anteriores: um laço pode fazer uma passagem extra, um texto pode precisar de um byte extra e uma função pode perder a informação sobre a capacidade de um arranjo. Aprenda a identificar o acesso inválido antes de prever seus efeitos. Depois, leia e copie entradas dentro de limites conhecidos, verificando separadamente o sucesso e a completude, para que um buffer que comporta os dados também contenha aqueles que o programa pretendia aceitar.

6.1 Reconheça acessos fora dos limites e seus efeitos

Todo acesso a um arranjo precisa de um índice válido. C não exige uma verificação de limites em tempo de execução, portanto o programador deve manter o índice dentro do objeto. Um acesso fora de sua faixa tem comportamento indefinido: a linguagem não garante nenhum resultado.

Reconheça um acesso inválido

Para int a[5], os índices válidos vão de 0 a 4. O índice 5 já é grande demais e -1 é negativo. Um compilador pode diagnosticar um erro visível, mas muitos índices inválidos dependem da entrada em tempo de execução e não são rejeitados durante a compilação.

Um programa com defeito pode parecer funcionar, encerrar inesperadamente ou produzir valores imprevistos. Uma gravação pode corromper dados próximos; o sintoma pode ocorrer mais tarde em um cálculo sem relação aparente. Uma leitura pode acessar bytes fora do objeto pretendido. Nenhuma das operações tem um resultado confiável. Repetir uma execução bem-sucedida não pode estabelecer que o acesso é válido.

Conte cada gravação, incluindo o terminador
char name[8];
strcpy(name, "Christopher");

“Christopher” tem onze bytes de texto. Copiá-lo também grava o terminador, portanto essa chamada tenta gravar doze bytes em oito posições. Isso é um estouro de buffer. Em sistemas reais, um estouro pode danificar outros dados ou informações de controle. Historicamente, ataques incluindo o worm Morris de 1988 exploraram esses defeitos no tratamento de entrada.

Um estouro pode ter apenas um byte:

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

O corpo é executado seis vezes e grava no índice 5. Alterar o teste para i < 5 mantém cinco gravações de caracteres dentro do arranjo, mas ainda não cria uma cadeia. Para uma cadeia de quatro caracteres 'x', grave apenas nos índices de 0 a 3 e depois armazene '\0' no índice 4.

Valide índices calculados

Uma tabela de frequências pode transformar um caractere em índice, mas a subtração por si só não garante a faixa:

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

Com a ordem ASCII, esse teste limita o resultado de 0 a 25. Para 'A' ou um byte de pontuação, usar c - 'a' sem o teste poderia criar um índice negativo. Teste o valor original antes de indexar; verificar após a gravação é tarde demais.

Para uma tabela, valide a linha e a coluna separadamente. Em int g[2][3], um índice de coluna 3 é inválido mesmo que a linha seguinte esteja ao lado na memória. Não use o armazenamento da tabela inteira como justificativa para ultrapassar o limite de uma linha.

Use verificações adequadas à operação

Um laço sobre arranjo usa um limite superior estrito; um acréscimo verifica a quantidade em uso em relação à capacidade; uma cópia de cadeia verifica o comprimento do texto mais um. Funções recebem comprimentos ou capacidades reais de suas funções chamadoras. Essas verificações tratam de quantidades diferentes, portanto um único tamanho fixo no código não pode substituir todas elas.

Compile com avisos como -Wall -Wextra. A opção -fsanitize=address -g de GCC e Clang pode informar muitos acessos inválidos em caminhos executados e mostrar o local no código-fonte. Esses testes ajudam a encontrar erros, mas caminhos não executados ainda exigem raciocínio sobre os limites. Revise cada leitura e gravação indexada, identificando seu objeto, seu limite e a condição que mantém o índice válido.

Principais conclusões

  • Leituras e gravações fora dos limites têm comportamento indefinido.
  • Uma execução bem-sucedida não é prova de que um índice é válido.
  • Uma cadeia copiada inclui seu terminador na quantidade de bytes gravados.
  • Valide os dados antes de obter um índice de arranjo a partir deles.
  • Verifique cada dimensão de uma tabela independentemente.
  • Avisos e sanitizadores ajudam a detectar defeitos nos caminhos testados.

6.2 Leia e copie texto respeitando os limites do buffer

O comprimento da entrada é controlado por quem a fornece. Informe às funções de leitura o limite real do buffer e verifique o que elas retornam. Depois, decida se os dados formam uma linha completa e se atendem às regras do próprio programa.

Leia uma palavra com uma largura

Para char word[16];, scanf("%15s", word) lê no máximo quinze caracteres que não são de espaço em branco e acrescenta '\0'. A largura deixa uma das dezesseis posições para o terminador. %s sem largura não tem limite de comprimento; gets tinha o mesmo problema fundamental para linhas e foi removida de C em C11.

Verifique se scanf retorna 1 antes de usar uma palavra. Ela lê uma palavra, não uma linha inteira com espaços. Bytes excedentes da palavra podem permanecer sem leitura, portanto uma largura protege o armazenamento sem decidir se toda a entrada é aceitável.

Leia uma linha com fgets
char buf[8];
if (fgets(buf, sizeof buf, stdin) == NULL) {
    return 1;
}

fgets lê aqui no máximo sete caracteres e grava um byte zero final. Ela mantém uma nova linha quando a encontra antes do limite. Uma linha contendo “hello” e uma nova linha torna-se "hello\n" mais o terminador. Uma linha final que termina no fim de arquivo sem uma nova linha ainda pode ser lida com sucesso. Se nenhum caractere tiver sido lido antes do fim de arquivo, ou se ocorrer um erro de leitura, o resultado será NULL; não processe esse buffer como nova entrada.

Distinga uma nova linha armazenada de uma linha parcial

Encontre uma nova linha antes de removê-la:

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

Inclua <string.h>. Se nenhuma nova linha tiver sido armazenada, end será o comprimento do texto e a atribuição regravará o terminador existente. A ausência de uma nova linha pode significar uma linha parcial, um preenchimento exato do buffer antes de uma nova linha ou uma linha final que terminou no fim de arquivo. Só isso não permite concluir que restam caracteres.

Para a linha de entrada “abcdefghij” e um buffer de oito bytes, a primeira leitura armazena “abcdefg” e a próxima pode ler “hij\n”. Tratar o segundo trecho como uma resposta separada dividiria uma única entrada lógica. Um programa pode rejeitar uma linha sem uma nova linha obrigatória ou ler e descartar seu restante antes de perguntar novamente. Defina a política de entrada antes de implementá-la.

Para até trinta caracteres ASCII de um nome mais uma nova linha e um terminador, use 32 bytes. Para UTF-8, esse é um limite de bytes: trinta caracteres exibidos podem precisar de mais espaço.

Copie somente após verificar todos os 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]);

A verificação da contagem de argumentos vem primeiro. A verificação do comprimento admite então no máximo nove bytes de texto, deixando a décima posição para o zero. Uma função de gravação deve receber a capacidade explicitamente, como em read_name(out, capacity), pois sizeof out dentro dela mede um parâmetro ponteiro.

Para entrada numérica, leia uma linha completa e converta-a com strtol, verificando a posição final, o erro de faixa e a faixa de valores permitida. Uma chamada malsucedida de scanf("%d", &n) pode deixar o texto problemático sem leitura; repeti-la sem verificação pode falhar para sempre nos mesmos bytes. No mínimo, verifique sua contagem de atribuições e trate a entrada inválida antes de tentar novamente.

Principais conclusões

  • Defina a largura de leitura de uma palavra como uma unidade a menos que a capacidade do buffer.
  • Verifique o resultado de uma leitura antes de usar seu buffer.
  • fgets mantém uma nova linha somente quando a alcança dentro do limite.
  • A ausência de uma nova linha, por si só, não estabelece que ainda há entrada.
  • Reserve espaço para os bytes do texto, a nova linha quando mantida e o terminador.
  • Verifique a presença do argumento antes de medir e copiar seu texto.
  • Passe a capacidade do buffer para uma função que grava nele.

Exercícios resolvidos de Arranjos e cadeias de caracteres em C

6 Lições Interativas

1

Acompanhe um arranjo da declaração à chamada de função

13 passos
Rastreie elementos inicializados, calcule uma média e compartilhe dados do arranjo com uma função.
  1. 1
    IntroduçãoDê um nome a vários valores

    Um arranjo contém uma quantidade fixa de elementos de um mesmo tipo. Um laço seleciona posições, enquanto uma função pode trabalhar no mesmo armazenamento por meio de um parâmetro de arranjo.

    Acompanhe quatro leituras de sensor desde a declaração até um total e uma média. Em cada etapa, mantenha três quantidades distintas: a quantidade de elementos, o último índice válido e a quantidade de bytes usada para armazenamento.

  2. 2
    ExemploLeia as posições iniciais
    int readings[4] = {11, 16, 13};
    

    Há quatro elementos, com índices de 0 a 3. O inicializador fornece 11, 16 e 13 nas três primeiras posições. O valor omitido torna-se zero, portanto o arranjo é {11, 16, 13, 0}.

    O zero é uma regra de inicialização, não um sinal de que o arranjo tem apenas três elementos. Um arranjo local sem inicializador precisaria ter seus valores gravados antes do uso.

  3. 3
    ExplicaçãoConte os elementos na declaração
    size_t n = sizeof readings / sizeof readings[0];
    

    O numerador mede os quatro elementos e o denominador mede um. Onde int ocupa quatro bytes, isso é dezesseis dividido por quatro, resultando em 4. A mesma expressão funciona com outro tamanho de inteiro.

    readings[3] é o último elemento. readings[4] está após o fim, embora a quantidade de elementos do arranjo seja 4. size_t é o tipo sem sinal usado para esse resultado de tamanho.

  4. 4
    DesafioAltere a última leitura

    A declaração continua sendo int readings[4] = {11, 16, 13};. Qual atribuição substitui o zero por 20 e qual é a quantidade resultante?

    ---reveal---

    Use readings[3] = 20;. Os valores resultantes são {11, 16, 13, 20} e a quantidade permanece 4. Uma atribuição a um elemento altera um valor, não a quantidade de posições. O índice 4 estaria uma posição além do limite e não pode adicionar espaço a esse arranjo.

  5. 5
    ExemploConstrua o total uma passagem por vez
    int sum = 0;
    for (size_t i = 0; i < n; i++) {
        sum += readings[i];
    }
    

    Usando {11, 16, 13, 20}, a soma acumulada passa a ser 11, 27, 40 e 60. Após o índice 3, i torna-se 4 e o teste falha antes de outro acesso.

    Começar a soma em zero fornece um ponto de partida para o primeiro valor. O limite superior estrito permite que o laço visite exatamente os quatro elementos existentes.

  6. 6
    Ideia-chavePreserve uma média fracionária

    Para essas quatro leituras, (double) sum / n resulta em 15,0. A conversão explícita ocorre antes da divisão, portanto a operação pode produzir um resultado fracionário quando o total muda.

    Se o total fosse 61, a divisão inteira sum / 4 produziria 15, enquanto sum / 4.0 produziria 15,25. Atribuir o resultado inteiro já truncado a um double produziria apenas 15,0; isso não recuperaria a fração descartada.

  7. 7
    DicaEscolha um candidato inicial válido

    Uma busca do máximo precisa de um valor inicial obtido de um arranjo não vazio. Para {-6, -2, -9}, inicialize max com -6 e depois substitua-o por -2 quando esse valor maior aparecer. A última comparação deixa -2 inalterado.

    Inicializar max com zero retornaria zero, que não está presente. Para uma busca sequencial, a regra de parada correspondente também importa: uma diferença não estabelece a falha; os elementos posteriores ainda precisam ser verificados.

  8. 8
    DesafioContinue a busca

    Uma busca por 13 verifica {11, 16, 13, 20}. Após verificar o índice 0, ela retorna -1 porque 11 é diferente de 13. O que deve mudar e onde está a correspondência?

    ---reveal---

    Continue verificando após uma diferença e retorne -1 somente depois que todos os candidatos falharem. Aqui o índice 1 também é diferente, mas o índice 2 corresponde. Uma busca bem-sucedida pode parar aí. A primeira comparação malsucedida descreve um elemento, não o arranjo inteiro.

  9. 9
    ConexãoPasse o cálculo da média para uma função
    double mean(const int a[], size_t n);
    

    O parâmetro de arranjo compartilha o acesso aos elementos da função chamadora. Ele não contém a quantidade de elementos, portanto o segundo parâmetro a fornece. const impede gravações acidentais por meio de a nessa operação somente de leitura.

    Calcule a quantidade onde readings é declarado e chame mean(readings, n). Dentro de mean, sizeof a mede um parâmetro ponteiro em vez do arranjo de quatro elementos da função chamadora.

  10. 10
    ExemploTrate a faixa vazia 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 as quatro leituras pequenas, o total continua sendo 60 e o resultado é 15,0. Para n igual a zero, o resultado escolhido é 0,0 e não ocorre acesso ao arranjo nem divisão. A função chamadora deve passar uma quantidade utilizável e correta.

  11. 11
    DesafioExplique uma alteração no arranjo da função chamadora

    Outra função recebe int a[], size_t n e define cada a[i] visitado como zero. O que acontece com as leituras da função chamadora e como uma função que apenas calcula o total poderia impedir essas gravações?

    ---reveal---

    Os elementos da função chamadora tornam-se zero porque a função grava no armazenamento compartilhado do arranjo. Suas quatro posições continuam existindo. Uma função que apenas calcula o total deve usar const int a[], tornando inválidas as atribuições a elementos por meio de a. Alterar uma cópia escalar de n não mudaria uma variável de contagem da função chamadora.

  12. 12
    DesafioUse apenas as leituras existentes

    Um buffer tem capacidade 10, mas apenas quatro leituras estão em uso. Uma nova leitura deve ser acrescentada antes de calcular uma média. Quais verificações e qual quantidade o programa deve usar?

    ---reveal---

    Verifique se a quantidade em uso, 4, é menor que a capacidade 10. Grave no índice 4 e depois atualize a quantidade para 5. Passe essa quantidade em uso para mean para que ela processe exatamente os cinco valores. Passar a capacidade incluiria posições que não receberam leituras.

  13. 13
    RecapitulaçãoRelacione cada contagem à sua função

    Uma declaração cria as posições, um laço visita as que estão em uso e uma função recebe explicitamente a quantidade utilizável. Um parâmetro somente de leitura impede que cálculos sobrescrevam a entrada.

    Ao revisar código de arranjos, acompanhe o primeiro e o último índice visitados e o estado de cada acumulador. Para laços regressivos com size_t, comece em n, continue enquanto i for positivo e acesse a[i - 1], para que uma faixa vazia não exija subtração a partir de zero.

2

Transforme bytes armazenados em operações de texto

16 passos
Crie e transforme uma cadeia terminada em nulo e depois rastreie um caractere UTF-8 de vários bytes.

Salve esta aula para seguir cada Lição Interativa passo a passo.

  1. 1
    IntroduçãoAcompanhe o texto e o armazenamento
  2. 2
    ExplicaçãoLeia um caractere como código
  3. 3
    DesafioRetorne ao início do alfabeto
  4. 4
    ExemploCrie uma cadeia modificável
  5. 5
    Ideia-chaveMova o fim sem mover o armazenamento
  6. 6
    DesafioDeixe uma posição para o fim
  7. 7
    ExemploSepare leitura e escrita
  8. 8
    ExemploTermine a nova cadeia
  9. 9
    DesafioInverta as letras sem o terminador
  10. 10
    DicaClassifique bytes com argumentos válidos
  11. 11
    ExemploVisualize uma letra acentuada em UTF-8
  12. 12
    ExplicaçãoReconheça bytes de continuação
  13. 13
    DesafioMeça uma nova palavra codificada
  14. 14
    ConexãoEscolha operações que mantenham os caracteres inteiros
  15. 15
    DesafioVerifique um atalho proposto para texto
  16. 16
    RecapitulaçãoLocalize o fim e os limites dos caracteres
3

Monte, busque e formate texto com segurança

14 passos
Verifique capacidades, interprete resultados de busca e trate truncamento e sobreposição.

Salve esta aula para seguir cada Lição Interativa passo a passo.

  1. 1
    IntroduçãoIdentifique o contrato de cada função
  2. 2
    ExemploComece com uma cópia completa
  3. 3
    ExemploAcrescente no fim existente
  4. 4
    DesafioDecida se outro acréscimo cabe
  5. 5
    ExplicaçãoUse zero para reconhecer textos iguais
  6. 6
    ExemploLeia a partir de uma posição encontrada
  7. 7
    DesafioExamine uma verificação de comando baseada em prefixo
  8. 8
    DicaEspere que a tokenização altere o texto
  9. 9
    ExemploFormate dentro de uma capacidade conhecida
  10. 10
    DesafioInterprete a mesma formatação com menos espaço
  11. 11
    Ideia-chaveUm limite pode contar bytes da origem
  12. 12
    ExemploMova uma região sobreposta
  13. 13
    DesafioPreserve um resultado completo após o deslocamento
  14. 14
    RecapitulaçãoSepare bytes de resultados de texto
4

Leia uma tabela e escolha uma disposição para a lista de cadeias

10 passos
Rastreie totais de linhas e colunas, passe uma tabela e reserve espaço para cada cadeia.

Salve esta aula para seguir cada Lição Interativa passo a passo.

  1. 1
    IntroduçãoDê dois índices a cada posição
  2. 2
    ExemploColoque valores nas linhas
  3. 3
    ExemploReinicialize o total no início de cada linha
  4. 4
    DesafioAltere o agrupamento, mantenha os índices
  5. 5
    ExplicaçãoInforme à função a largura da linha
  6. 6
    ExemploReserve uma cadeia terminada em nulo por linha
  7. 7
    Ideia-chaveReferências permitem cadeias de comprimentos diferentes
  8. 8
    DesafioSubstitua texto no armazenamento adequado
  9. 9
    DesafioMantenha ambas as dimensões válidas em uma função
  10. 10
    RecapitulaçãoMantenha o formato da tabela visível
5

Valide uma seleção na linha de comando

11 passos
Interprete argumentos agrupados e converta um número verificado em uma seleção utilizável.

Salve esta aula para seguir cada Lição Interativa passo a passo.

  1. 1
    IntroduçãoReceba o texto antes de interpretá-lo
  2. 2
    ExemploRelacione um comando do shell às entradas
  3. 3
    ExplicaçãoVerifique a contagem antes da conversão
  4. 4
    DesafioAgrupe um nome corretamente
  5. 5
    DicaUm inteiro retornado não é suficiente
  6. 6
    ExemploExamine uma conversão completa
  7. 7
    ExplicaçãoVerifique a faixa da própria seleção
  8. 8
    DesafioDetecte texto parcialmente numérico
  9. 9
    Ideia-chaveFormato e valor são regras separadas
  10. 10
    DesafioCombine agrupamento e validação numérica
  11. 11
    RecapitulaçãoVerifique presença, conversão e finalidade
6

Corrija uma rotina de entrada com limite

13 passos
Rastreie trechos de linha, reserve espaço para o terminador e copie apenas um resultado completo e aceito.

Salve esta aula para seguir cada Lição Interativa passo a passo.

  1. 1
    IntroduçãoLimite a gravação e verifique o resultado
  2. 2
    ExemploEncontre a gravação extra
  3. 3
    Ideia-chaveUma última posição válida pode conter o fim
  4. 4
    DesafioVerifique um índice obtido de um byte
  5. 5
    ExplicaçãoLeia uma palavra com espaço para o terminador
  6. 6
    ExemploLeia um trecho de linha com limite
  7. 7
    ExemploObserve onde uma linha longa é dividida
  8. 8
    DicaExamine uma nova linha antes de removê-la
  9. 9
    DesafioReserve espaço para uma linha completa
  10. 10
    ExemploVerifique uma cópia antes de gravar
  11. 11
    DesafioSepare um prefixo seguro de uma resposta aceita
  12. 12
    ConexãoUse diagnósticos junto com o raciocínio sobre limites
  13. 13
    RecapitulaçãoMantenha quatro verificações distintas

Questões de Arranjos e cadeias de caracteres em C

60 Questões de Prática em 6 conjuntos

Conjunto 1: Caracteres, terminadores de cadeia e UTF-810 Questões de Prática

Questão 1 de 10

Qual valor numérico '6' - '0' produz?
  1. 54
  2. 48
  3. 0
  4. 6
Mostrar respostaOcultar resposta
Resposta: D
Os códigos dos algarismos são consecutivos, portanto subtrair o código de zero deixa a diferença correspondente ao algarismo, 6. O valor ASCII 54 é o código do próprio caractere antes da subtração. Essa operação converte um caractere de algarismo verificado em um número, não apenas em outro código de caractere.

Questão 2 de 10

Em ASCII, qual caractere resulta de ('W' - 'A' + 5) % 26 + 'A'?
  1. 'B'
  2. 'A'
  3. 'C'
  4. '\\'
Mostrar respostaOcultar resposta
Resposta: A
W tem posição 22 no alfabeto com indexação a partir de zero; somar 5 resulta em 27 e calcular o resto módulo 26 resulta em 1. A posição 1 corresponde a B após somar o código de A. A corresponderia à posição zero, ignorando a posição restante após retornar ao início.

Questão 3 de 10

Como um byte char c deve ser passado para isalpha sem arriscar um argumento de byte negativo?
  1. isalpha((signed char) c)
  2. isalpha(c - '0')
  3. isalpha((unsigned char) c)
  4. isalpha((int) c)
Mostrar respostaOcultar resposta
Resposta: C
Converter para unsigned char fornece um valor de byte válido mesmo quando char sem qualificador tem sinal. Converter diretamente para int pode preservar um valor negativo de char e, portanto, mantém o problema original. A conversão explícita torna a chamada válida; ela não decodifica um caractere UTF-8 de vários bytes.

Questão 4 de 10

Quais são o comprimento do texto e o tamanho do arranjo de char color[] = "amber";?
  1. Comprimento 6; tamanho 6
  2. Comprimento 5; tamanho 6
  3. Comprimento 5; tamanho 5
  4. Comprimento 6; tamanho 5
Mostrar respostaOcultar resposta
Resposta: B
O literal fornece cinco letras e um byte zero terminador. strlen não inclui o terminador, mas o arranjo inicializado deve armazená-lo, resultando em tamanho 6. Um tamanho de 5 não deixaria espaço para o byte final e descreveria outra declaração.

Questão 5 de 10

Qual cadeia permanece após char s[8] = "planet"; s[3] = '\0'; s[0] = 'P';?
  1. "Pla"
  2. "Planet"
  3. "Pla0et"
  4. "Plan"
Mostrar respostaOcultar resposta
Resposta: A
O zero gravado no índice 3 termina o texto visível após os índices 0, 1 e 2. Alterar o índice 0 faz essas três letras formarem Pla. Os bytes após o zero permanecem armazenados, mas as funções de cadeia não continuam a leitura para imprimir Planet.

Questão 6 de 10

Por que char tag[4] = "east"; printf("%s", tag); não é seguro?
  1. O inicializador tem caracteres insuficientes.
  2. O formato imprime apenas um único byte.
  3. O arranjo não tem um byte zero terminador.
  4. Os caracteres do arranjo são somente de leitura.
Mostrar respostaOcultar resposta
Resposta: C
C permite que o inicializador de quatro caracteres preencha as quatro posições, omitindo seu terminador. A conversão %s não encontra um marcador de fim dentro do arranjo e pode ler além dele. Não se trata de modificação de literal: tag é um arranjo modificável, mas não contém uma cadeia terminada em nulo.

Questão 7 de 10

Um filtro mantém apenas as letras de "m8n2p" e as grava em um arranjo de saída suficientemente grande. O que ele deve gravar após o último p?
  1. Um byte zero no índice de saída 2
  2. Um algarismo zero no índice de saída 3
  3. Um byte zero no índice de saída 5
  4. Um byte zero no índice de saída 3
Mostrar respostaOcultar resposta
Resposta: D
O texto aceito contém m, n e p nos índices de saída 0, 1 e 2. Seu terminador deve ficar na posição seguinte, índice 3. Usar o comprimento da entrada, 5, deixaria bytes intermediários fora do texto construído e não estabeleceria seu fim imediatamente após p.

Questão 8 de 10

Um programa inverte char s[] = "lamp"; trocando s[i] por s[len - i] enquanto i < len / 2, onde len é strlen(s). Quais são o strlen(s) final e a capacidade inalterada?
  1. Comprimento 4; capacidade 5
  2. Comprimento 0; capacidade 5
  3. Comprimento 0; capacidade 4
  4. Comprimento 5; capacidade 5
Mostrar respostaOcultar resposta
Resposta: B
Aqui len é 4, portanto a primeira troca move o terminador do índice 4 para o índice 0. A troca seguinte não altera o índice 0, deixando uma cadeia de comprimento zero no mesmo arranjo de cinco bytes. Uma inversão correta trocaria com len - 1 - i e manteria o terminador fixo.

Questão 9 de 10

Em UTF-8, o “é” pré-composto usa dois bytes. Quais são o strlen e o armazenamento mínimo para a cadeia “écho”?
  1. 5 bytes de texto; 6 bytes de armazenamento
  2. 4 bytes de texto; 5 bytes de armazenamento
  3. 5 bytes de texto; 5 bytes de armazenamento
  4. 6 bytes de texto; 7 bytes de armazenamento
Mostrar respostaOcultar resposta
Resposta: A
A letra acentuada contribui com dois bytes e c, h e o contribuem com um cada, totalizando cinco bytes de texto. Uma cadeia em C precisa de mais um byte para o zero, resultando em seis. Contar quatro pontos de código fornece a quantidade de unidades de texto codificadas aqui, mas não a contagem de bytes retornada por strlen.

Questão 10 de 10

Uma cadeia UTF-8 válida tem os bytes 41 C3 B1 42 00. Um programa grava zero no índice de byte 2. Quais são o novo strlen e a validade UTF-8 do texto?
  1. Comprimento 2; UTF-8 válido
  2. Comprimento 2; UTF-8 inválido
  3. Comprimento 3; UTF-8 válido
  4. Comprimento 1; UTF-8 inválido
Mostrar respostaOcultar resposta
Resposta: B
O novo zero termina a cadeia após os bytes 41 e C3, portanto strlen é 2. C3 é um byte inicial de uma sequência de vários bytes cujo byte de continuação B1 foi removido do texto. O terminador mantém uma cadeia em C delimitada, mas não torna a codificação restante um UTF-8 válido.
Conjunto 2: Valores de arranjos, laços e chamadas de função10 Questões de Prática

Salve esta aula para ver todas as respostas e praticar cada conjunto.

Questão 1 de 10

Qual valor está armazenado em a[3] após int a[5] = {6, 9};?
  1. 6
  2. 9
  3. 0
  4. Um valor indeterminado
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 2 de 10

Um arranjo é declarado como int a[7];, onde int ocupa 4 bytes. Qual par fornece sizeof a e o ú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 respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 3 de 10

Para int a[] = {-12, -4, -9};, qual valor uma busca do máximo deve retornar?
  1. -4
  2. 0
  3. -12
  4. -9
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 4 de 10

O que este código imprime?

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 respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 5 de 10

Uma busca por 7 em {2, 9, 7, 4} retorna -1 no primeiro elemento diferente. Onde deve ocorrer o retorno que indica falha?
  1. Dentro do laço, após qualquer candidato diferente.
  2. Após o laço verificar todos os candidatos.
  3. Antes de verificar o último elemento candidato.
  4. Logo após o primeiro incremento do laço.
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 6 de 10

Qual arranjo resulta após trocar a[i] por a[5 - 1 - i] para i = 0 e i = 1, começando por {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 respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 7 de 10

Uma função void set_first(int a[]) executa a[0] = 11;. Qual é o primeiro elemento do arranjo da função chamadora após a chamada?
  1. Ele contém 11.
  2. Ele permanece inalterado.
  3. Ele é indefinido.
  4. Ele contém 0.
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 8 de 10

Dentro de void f(int a[], size_t n), ponteiros ocupam 8 bytes e inteiros, 4 bytes. Qual é o resultado de sizeof a / sizeof a[0]?
  1. n, em toda chamada
  2. 8, independentemente de n
  3. 4, em toda chamada
  4. 2, independentemente de n
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 9 de 10

Uma função somente de leitura usa size_t para n e i. Ela deve imprimir um arranjo de trás para a frente e funcionar para n = 0. Qual parâmetro e laço atendem aos dois requisitos?
  1. const int a[]; inicie i em n - 1, teste i >= 0, acesse a[i].
  2. int a[]; inicie i em n, teste i > 0, acesse a[i].
  3. const int a[]; inicie i em n, teste i > 0, acesse a[i - 1].
  4. int a[]; inicie i em n - 1, teste i > 0, acesse a[i - 1].
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 10 de 10

O que C garante após um programa ler a[4] de int a[4]?
  1. Nenhum resultado é garantido.
  2. A leitura retorna zero.
  3. A leitura retorna a[3].
  4. A leitura sempre encerra o programa.
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Conjunto 3: Escolha e verifique operações de biblioteca10 Questões de Prática

Salve esta aula para ver todas as respostas e praticar cada conjunto.

Questão 1 de 10

Após char s[] = "wxyz"; memmove(s, &s[1], 4);, o que s contém como cadeia?
  1. "xyzz"
  2. "wxy"
  3. "xyz"
  4. "yz"
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 2 de 10

Qual é a menor capacidade de arranjo de char para uma cópia completa de "silver"?
  1. 7 bytes
  2. 6 bytes
  3. 8 bytes
  4. 5 bytes
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 3 de 10

char label[10] = "blue"; strcat(label, "berry"); pode armazenar seu resultado completo?
  1. Não; o resultado precisa de 11 bytes.
  2. Sim; o resultado precisa de 9 bytes.
  3. Sim; o resultado precisa de 10 bytes.
  4. Não; o resultado precisa de 12 bytes.
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 4 de 10

Dentro de uma função, um arranjo local automático é usado como char out[20]; strcat(out, "go");. Por que essa é uma forma inválida de começar a construir uma cadeia?
  1. out é pequeno demais para o texto acrescentado.
  2. out não tem um terminador de cadeia definido.
  3. strcat exige um arranjo de ponteiros para out.
  4. strcat não pode copiar de um literal de cadeia.
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 5 de 10

Qual condição reconhece textos iguais em duas cadeias válidas a e b?
  1. a == b
  2. strcmp(a, b) > 0
  3. strlen(a) == strlen(b)
  4. strcmp(a, b) == 0
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 6 de 10

Após uma chamada bem-sucedida char *p = strrchr("archive.backup.bin", '.');, qual texto começa em p?
  1. ".bin"
  2. "bin"
  3. ".backup.bin"
  4. "archive.backup"
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 7 de 10

Um arranjo modificável contém "on,,off;wait". As chamadas usam strtok(s, ",;") e depois repetem strtok(NULL, ",;") até NULL. Quais tokens são produzidos e s é preservado?
  1. “on”, “”, “off”, “wait”; s é modificado.
  2. “on”, “off;wait”; s é modificado.
  3. “on”, “off”, “wait”; s é modificado.
  4. “on”, “off”, “wait”; s é preservado.
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 8 de 10

O que char b[6]; int r = snprintf(b, sizeof b, "%s:%d", "job", 84); armazena e retorna?
  1. Armazena “job:8” e retorna 5.
  2. Armazena “job:84” e retorna 6.
  3. Armazena “job:” e retorna 4.
  4. Armazena “job:8” e retorna 6.
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 9 de 10

Um arranjo de sete bytes começa com "AB". O programa executa strncat(b, "123456", sizeof b - strlen(b) - 1). Qual é o texto resultante e sua quantidade de bytes armazenados, incluindo o zero?
  1. “AB12345”; 8 bytes
  2. “AB1234”; 7 bytes
  3. “AB123”; 6 bytes
  4. “AB123456”; 9 bytes
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 10 de 10

Dois arranjos de double são iguais na comparação elemento por elemento, mas um armazena zero positivo onde o outro armazena zero negativo. O que uma comparação de bytes pode estabelecer?
  1. Se suas representações armazenadas são iguais.
  2. Se seus valores numéricos são diferentes.
  3. Se um tem maior quantidade de elementos.
  4. Se ambos os valores são zero positivo.
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Conjunto 4: Tabelas, listas de texto e entrada na inicialização10 Questões de Prática

Salve esta aula para ver todas as respostas e praticar cada conjunto.

Questão 1 de 10

Qual é o último par válido de linha e coluna para int table[4][6]?
  1. Linha 4, coluna 6
  2. Linha 3, coluna 5
  3. Linha 3, coluna 6
  4. Linha 4, coluna 5
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 2 de 10

Onde int ocupa 4 bytes, qual é o resultado de sizeof table[0] para int table[3][5]?
  1. 12 bytes
  2. 15 bytes
  3. 60 bytes
  4. 20 bytes
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 3 de 10

Uma tabela é {{2, 7, 4}, {6, 1, 5}}. Um laço inicializa sum uma vez com zero antes dos dois laços, soma uma linha por vez e imprime após cada linha sem reinicializar sum. Quais valores são impressos e como isso é corrigido?
  1. 13 e depois 25; reinicialize sum antes de cada linha.
  2. 13 e depois 12; reinicialize sum antes de cada linha.
  3. 13 e depois 25; reinicialize sum antes de cada célula.
  4. 8 e depois 16; reinicialize sum antes de cada coluna.
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 4 de 10

Qual parâmetro de arranjo de largura fixa recebe linhas com sete inteiros cada?
  1. int g[7][]
  2. int g[][]
  3. int g[][7]
  4. int g[][6]
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 5 de 10

Qual é a menor largura de linha para char names[3][width] armazenar “Iris”, “Magnolia” e “Ash” como cadeias ASCII completas?
  1. 8 bytes
  2. 10 bytes
  3. 16 bytes
  4. 9 bytes
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 6 de 10

Um programa tem char rows[2][5] = {"oak", "elm"}; e const char *refs[] = {"oak", "elm"};. Ele deve substituir a primeira palavra listada pelo texto completo “cedar”. Qual tratamento é válido para cada disposição?
  1. Copiar “cedar” para rows[0]; alterar refs[0].
  2. Rejeitar a cópia na linha; atribuir “cedar” a refs[0].
  3. Rejeitar a cópia na linha; copiar “cedar” para refs[0].
  4. Atribuir rows[0] = “cedar”; alterar refs[0].
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 7 de 10

Em um shell comum, um programa é executado como ./mark "West Room" 9. Quais são argc e argv[2]?
  1. argc é 3; argv[2] é “9”.
  2. argc é 4; argv[2] é “Room”.
  3. argc é 2; argv[2] é NULL.
  4. argc é 3; argv[2] é “West Room”.
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 8 de 10

Um programa exige dois argumentos do usuário. Ele verifica argc == 2 e depois chama strlen(argv[2]). Qual verificação de contagem e último índice correspondem à sua interface real?
  1. Verificar argc == 2; ler argumentos até o índice 2.
  2. Verificar argc == 3; ler argumentos até o índice 3.
  3. Verificar argc == 3; ler argumentos até o índice 2.
  4. Verificar argc == 2; ler argumentos até o índice 1.
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 9 de 10

Após strtol converter “42rest” na base decimal, o que indicam o resultado e o ponteiro de fim da conversão?
  1. Valor 0; nenhum caractere foi consumido.
  2. Valor 42; o texto não consumido começa com r.
  3. Valor 42; toda a entrada foi consumida.
  4. Valor 42; deve ter ocorrido um erro de faixa.
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 10 de 10

Uma chave formada apenas por algarismos cabe em long, mas não em int. O programa a converte explicitamente para int antes de calcular key % 26. Como ele pode preservar o deslocamento pretendido sem antes convertê-la para um tipo de faixa menor?
  1. Converter para int primeiro e depois calcular o resto.
  2. Usar atoi e depois calcular o resto como int.
  3. Calcular o resto como long e depois converter o resultado de 0 a 25.
  4. Verificar os algarismos novamente e converter o valor original.
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Conjunto 5: Limites de entrada e resultados completos10 Questões de Prática

Salve esta aula para ver todas as respostas e praticar cada conjunto.

Questão 1 de 10

Qual é a maior largura de %s que deixa uma posição para o terminador em char word[18]?
  1. %18s
  2. %16s
  3. %17s
  4. %19s
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 2 de 10

Uma chamada bem-sucedida de fgets lê a entrada "pine\n" para um arranjo de 10 bytes. Quais são o strlen do resultado e o índice de seu terminador?
  1. Comprimento 4; índice do terminador 4
  2. Comprimento 5; índice do terminador 6
  3. Comprimento 6; índice do terminador 5
  4. Comprimento 5; índice do terminador 5
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 3 de 10

A linha de entrada é “qrstuvwxy” seguida de uma nova linha. Duas chamadas de fgets usam o mesmo char b[6] sem outras leituras. Qual texto a segunda chamada armazena?
  1. "uvwxy"
  2. "vwxy\n"
  3. "vwxy"
  4. "qrst\n"
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 4 de 10

Um programa permite 12 caracteres de texto ASCII em um nome digitado como uma linha completa terminada em nova linha. Ele deve examinar a nova linha e depois armazenar apenas o nome em um arranjo separado. Quais capacidades mínimas de entrada e final são necessárias?
  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 respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 5 de 10

Um buffer de 9 bytes recebe um resultado bem-sucedido de fgets com comprimento 8 e sem nova linha. Nenhuma entrada posterior foi examinada. O que se pode concluir sobre a linha lógica?
  1. Ela tem certamente mais de oito caracteres.
  2. Ela pode terminar ali ou ter mais entrada aguardando.
  3. Ela certamente já terminou no fim de arquivo.
  4. Sua nova linha foi lida e removida por fgets.
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 6 de 10

Por que counts[c - 'a']++ não é seguro para entrada de caractere não verificada?
  1. O índice obtido pode estar fora do arranjo.
  2. O incremento não pode atualizar contadores inteiros.
  3. A subtração sempre retorna um valor negativo.
  4. A expressão compara duas cadeias inteiras.
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 7 de 10

Um arranjo de char de seis bytes é preenchido com letras nos índices de 0 a 5 e depois passado para strlen. Qual propriedade está ausente?
  1. Um byte não usado além do arranjo para o zero.
  2. Uma nova linha explícita após a última letra.
  3. Um terminador dentro das seis posições válidas.
  4. Um inteiro separado com sua capacidade em bytes.
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 8 de 10

Um programa armazena um código completo em char out[6]. A chamada snprintf(out, sizeof out, "%s%d", "R-", 1234) é seguida de if (r > sizeof out) reject();, onde r é seu retorno não negativo. O que é armazenado, esse teste o rejeita e o que deve mudar?
  1. “R-1234”; aceito; manter o teste existente.
  2. “R-123”; rejeitado; manter o teste existente.
  3. “R-12”; aceito; rejeitar quando r >= capacidade.
  4. “R-123”; aceito; rejeitar quando r >= capacidade.
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 9 de 10

Um analisador decimal rejeita ERANGE e exige *end == '\0', mas não verifica end == start. Qual entrada inválida ainda pode ser aceita como zero?
  1. Uma cadeia vazia
  2. Uma palavra alfabética
  3. Um algarismo seguido de uma letra
  4. Um número fora da faixa de long
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 10 de 10

Uma cadeia UTF-8 válida tem os bytes 41 C3 A9 E2 82 AC 00. Um programa a copia para char b[5], reservando sua última posição para zero. Quais são o strlen resultante, a contagem de bytes iniciais e a validade da codificação?
  1. Comprimento 4; 3 bytes iniciais; UTF-8 válido.
  2. Comprimento 4; 3 bytes iniciais; UTF-8 inválido.
  3. Comprimento 3; 3 bytes iniciais; UTF-8 válido.
  4. Comprimento 4; 2 bytes iniciais; UTF-8 inválido.
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Conjunto 6: Rastreie e corrija operações combinadas em C10 Questões de Prática

Salve esta aula para ver todas as respostas e praticar cada conjunto.

Questão 1 de 10

Qual arranjo é criado por 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 respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 2 de 10

Um arranjo de oito posições tem quantidade em uso 5. Um valor é acrescentado em a[count], e count é incrementado. Em qual índice foi feita a gravação e qual é a nova quantidade?
  1. Índice 5; quantidade 6
  2. Índice 4; quantidade 6
  3. Índice 6; quantidade 6
  4. Índice 5; quantidade 5
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 3 de 10

Para int g[3][4], qual é a posição de elemento de g[2][1], contada a partir de zero, no armazenamento por linhas?
  1. 7
  2. 8
  3. 10
  4. 9
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 4 de 10

Um shell executa ./pick "" blue. Como argv[1] e argv[argc] diferem?
  1. argv[1] é um ponteiro nulo; argv[argc] é uma cadeia vazia.
  2. Ambas as entradas são cadeias vazias com seus próprios terminadores.
  3. argv[1] é uma cadeia vazia; argv[argc] é um ponteiro nulo.
  4. Ambas as entradas são ponteiros nulos que marcam texto ausente.
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 5 de 10

Uma função recebe int a[], size_t n e executa a[0] = 0; n = 0;. A função chamadora forneceu {5, 8, 2} e a quantidade 3 e depois calcula a soma inteira de seus elementos dividida por sua quantidade. Qual média e qual quantidade da função chamadora resultam?
  1. Média 0; quantidade da função chamadora 3
  2. Média 3; quantidade da função chamadora 3
  3. Média 5; quantidade da função chamadora 3
  4. Divisão indefinida; quantidade da função chamadora 0
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 6 de 10

Um analisador divide o texto modificável “run,runner” nas vírgulas usando strtok. Ele incrementa um contador quando strncmp(token, "run", 3) == 0. Qual contagem resulta, qual deveria ser a contagem para comandos exatos e qual comparação corrige isso?
  1. Atual 2; exata 1; strcmp(token, "run") == 0.
  2. Atual 1; exata 1; strcmp(token, "run") == 0.
  3. Atual 2; exata 2; strncmp(token, "run", 2) == 0.
  4. Atual 1; exata 2; strstr(token, "run") != NULL.
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 7 de 10

Um arranjo b de cinco bytes é preenchido por strncpy(b, "planet", sizeof b);. O programa exige a palavra original completa. Qual correção satisfaz o requisito?
  1. Gravar zero em b[4] após copiar.
  2. Acrescentar zero usando b[5] após copiar.
  3. Passar sizeof b - 1 e usar b sem alteração.
  4. Rejeitá-la como longa demais antes de copiar.
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 8 de 10

Um programa usa int values[4] = {14, 28, 35, 49};. Ele converte completamente “04” com strtol na base decimal, aceita seleções de 1 a 4 e depois lê values[selected]. Deve permitir as quatro seleções e mapear a seleção 1 para o primeiro valor. O que acontece e qual regra de índice corrige isso?
  1. Imprime 49; manter o índice selected.
  2. Imprime 49; usar o índice selected - 1.
  3. Tem comportamento indefinido; usar o índice selected - 1.
  4. Tem comportamento indefinido; exigir seleção < 4.
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 9 de 10

Uma função read_text(char out[], size_t cap) chama fgets(out, sizeof out, stdin). A função chamadora passa um arranjo de 30 bytes; o tamanho do ponteiro é 8. Com “sunflower\n” aguardando, qual trecho é armazenado, o que resta e qual limite deve substituir sizeof out?
  1. “sunflower\n”; nada resta; usar cap.
  2. “sunflowe”; resta “r\n”; usar cap.
  3. “sunflow”; resta “er\n”; usar strlen(out).
  4. “sunflow”; resta “er\n”; usar cap.
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 10 de 10

Um contador de sequências de letras ASCII começa fora de uma palavra e incrementa sempre que uma letra vem após um caractere que não é letra. Quantas palavras ele conta em “go2fast now!”?
  1. 3
  2. 2
  3. 4
  4. 1
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Travou em uma questão sua? Ajuda com Tarefas

Termos-Chave

85 Termos-Chave, cada um com sua definição

A

Acesso fora dos limites
Um acesso fora dos limites lê ou grava uma posição indexada fora do arranjo ou da linha realmente acessada e tem comportamento indefinido.
Acessar a[8] em um arranjo de oito elementos é inválido mesmo que uma execução de teste pareça retornar um valor útil.
Acumulador
Um acumulador é uma variável que reúne um resultado progressivo, como uma soma ou contagem, enquanto um laço processa valores sucessivos.
Começar sum em zero permite que cada sum += a[i] some o próximo elemento do arranjo ao total atual.
argc
O parâmetro argc fornece a contagem de cadeias de argumentos fornecidas a main, normalmente incluindo a cadeia usada para iniciar o programa.
Executar ./label red blue em um shell comum resulta em argc igual a 3: o nome usado para iniciar o programa e duas cores.
Argumento de linha de comando
Um argumento de linha de comando é uma cadeia fornecida quando um programa é iniciado, frequentemente agrupada ou expandida por um shell antes de o programa recebê-la.
Colocar “North Hall” entre aspas na linha de comando pode fornecer um argumento contendo um espaço em vez de duas palavras.
argv
O parâmetro argv fornece acesso por índice às cadeias de argumentos, seguidas por um ponteiro nulo na posição indicada por argc.
Após verificar argc, um programa pode usar strcmp(argv[1], "-v") para reconhecer sua primeira opção.
Armazenamento por linhas
O armazenamento por linhas armazena todos os elementos de uma linha consecutivamente antes da linha seguinte, conforme exigido para arranjos multidimensionais em C.
O elemento após grid[0][3] no armazenamento de uma tabela de quatro colunas é grid[1][0].
Arranjo
Um arranjo é uma sequência de tamanho fixo de elementos vizinhos de um mesmo tipo, acessados por um nome compartilhado e um índice inteiro.
int levels[6] reserva seis inteiros; atualizar levels[2] altera o terceiro valor armazenado.
Arranjo de ponteiros para cadeias de caracteres
Um arranjo de ponteiros para cadeias de caracteres armazena uma referência por cadeia, permitindo que textos armazenados em outro lugar tenham comprimentos diferentes.
const char *names[] = {"Al", "Mina"} armazena duas referências e permite substituir uma referência.
Arranjo de saída
Um arranjo de saída é um espaço fornecido por uma função chamadora para que uma função o preencha com vários resultados, com capacidade ou quantidade acordada.
Uma função de quadrados pode gravar cada resultado em out[i] enquanto a função chamadora mantém a responsabilidade pelo arranjo.
Arranjo de tamanho variável
Um arranjo de tamanho variável recebe seu tamanho quando sua declaração é executada e o mantém até o fim de seu tempo de vida.
Em C17, uma declaração local const int n = 6; int a[n]; cria um VLA porque n não é uma expressão constante.
ASCII
ASCII é uma codificação de caracteres que atribui valores de 0 a 127 a letras básicas, algarismos, pontuação e caracteres de controle.
A operação '8' - '0' produz 8 porque os códigos dos caracteres de algarismos são consecutivos.
atoi
A função atoi converte uma parte numérica inicial para int sem um relatório confiável de erros e tem comportamento indefinido em caso de estouro.
A entrada “18x” produz 18 com atoi, portanto um número retornado não estabelece que toda a entrada era numérica.

B

Buffer
Um buffer é uma região de armazenamento reservada para dados que estão sendo lidos, gravados ou transformados, com uma capacidade que limita gravações válidas.
Um buffer de nome de 32 bytes pode conter trinta caracteres de texto ASCII, uma nova linha mantida e um byte zero final.
Busca sequencial
Uma busca sequencial verifica elementos em sequência até encontrar um alvo ou examinar todos os candidatos sem correspondência.
Buscar 2 em {3, 8, 2} encontra o valor no índice 2 após verificar os elementos anteriores que são diferentes.
Byte de continuação
Um byte de continuação UTF-8 tem o padrão inicial de bits 10 e vem após um byte inicial dentro de um ponto de código codificado com vários bytes.
O byte 0xA9 continua a sequência de “é”; ele não deve ser mantido sozinho como um caractere UTF-8 completo.

C

Cadeia de caracteres em C
Uma cadeia de caracteres em C é uma sequência de caracteres que termina em um caractere nulo, com texto armazenado em um arranjo, e não em um tipo interno separado para cadeias.
O arranjo inicializado por char pet[] = "dog" contém três letras seguidas de um caractere de valor zero.
Cadeia vazia
Uma cadeia vazia não contém caracteres de texto e é representada por um terminador em sua primeira posição, tendo comprimento zero.
char message[20] = "" fornece um destino vazio válido para um acréscimo posterior que caiba em sua capacidade.
Capacidade
A capacidade de um arranjo é a quantidade de elementos que seu armazenamento alocado pode conter, independentemente de quantos tenham valores úteis no momento.
Um arranjo com capacidade 80 e quantidade em uso 29 tem espaço para 51 valores adicionais se a contagem for mantida corretamente.
Classificação de caracteres
A classificação de caracteres testa se um código de caractere pertence a uma categoria, como algarismo, letra ou caractere de espaço em branco, usando funções de <ctype.h>.
isdigit((unsigned char) c) fornece um resultado diferente de zero quando o byte armazenado representa um algarismo decimal.
Codificação de caracteres
Uma codificação de caracteres define um mapeamento entre o texto e sua representação numérica armazenada, incluindo os bytes usados para transmissão ou armazenamento.
Uma cadeia em C que contém a mesma palavra visível pode precisar de quantidades diferentes de bytes em codificações diferentes.
Código de saída
Um código de saída informa a conclusão do programa ao ambiente que o chamou; retornar zero de main indica conclusão bem-sucedida.
Um argumento obrigatório ausente pode fazer um programa imprimir informações de uso e retornar um código de falha diferente de zero.
Coluna
Uma coluna agrupa a mesma posição dentro de cada linha de uma tabela; seu índice é o segundo índice em g[r][c].
Para somar a coluna 2, mantenha c igual a 2 e varie r por todas as linhas enquanto lê g[r][c].
Comparação pela ordem dos valores dos bytes
Uma comparação pela ordem dos valores dos bytes ordena cadeias pelos valores sem sinal dos bytes armazenados, o que pode diferir das convenções alfabéticas de um idioma.
Em UTF-8, strcmp coloca uma letra latina acentuada depois do z ASCII porque seu byte inicial tem valor maior.
Comportamento indefinido
Comportamento indefinido significa que o padrão C não impõe requisitos ao resultado de uma operação, portanto nenhum resultado específico é garantido.
Uma gravação inválida em arranjo pode parecer inofensiva em uma versão compilada e encerrar o programa ou corromper dados em outra.
Comprimento da cadeia de caracteres
O comprimento da cadeia de caracteres é a quantidade de bytes de texto antes do primeiro caractere nulo, sem incluir esse caractere nem a capacidade não utilizada.
Um arranjo de 40 bytes que contém “June” tem comprimento 4, embora seu armazenamento disponível permaneça em 40 bytes.
Concatenação
A concatenação une sequências de texto em ordem, exigindo espaço suficiente no destino para o texto combinado e seu caractere terminador.
Unir “north” e “east” forma “northeast”, que precisa de dez bytes como cadeia ASCII terminada em nulo.
Configuração regional
Uma configuração regional fornece convenções usadas por funções de biblioteca; a configuração “C” padrão fornece as regras básicas de caracteres para classificação de um byte.
toupper, que opera em bytes, na configuração regional “C” não transforma uma letra acentuada UTF-8 completa em sua forma maiúscula.
Constante de caractere
Uma constante de caractere é uma expressão em C entre aspas simples, com tipo int, que representa um código de caractere, como o código de uma letra.
Com códigos ASCII, printf("%c %d", 'B', 'B') exibe a letra B e o valor numérico 66.
Contagem de atribuições
Uma contagem de atribuições é a quantidade de atribuições de entrada que scanf concluiu com sucesso, usada para verificar se os dados solicitados foram realmente lidos.
Uma leitura de um número espera um retorno de 1; uma falha de correspondência pode deixar os mesmos bytes inválidos aguardando na entrada.
Conversão numérica
Uma conversão numérica interpreta texto como número, exigindo verificações separadas do formato aceito, do texto consumido e da faixa de valores permitida.
Os bytes “27” precisam de conversão antes que um programa possa reduzir o valor numérico com % 26.

D

Delimitador
Um delimitador é um caractere usado para separar partes de texto, como os caracteres de vírgula ou espaço fornecidos a uma função de tokenização.
Com os delimitadores “, ”, o texto “one, two” pode ser dividido nos tokens “one” e “two”.
Deslocamento de César
Um deslocamento de César substitui cada letra por outra a uma quantidade fixa de posições no alfabeto, retornando ao início dentro de sua faixa de letras.
Em texto ASCII, deslocar Y maiúsculo em duas posições retorna ao início e resulta em A, enquanto um algarismo ou uma vírgula pode permanecer inalterado.

E

Elemento
Um elemento é um valor armazenado em um arranjo, com o mesmo tipo declarado dos demais valores e sua própria posição indexada.
Em int a[3] = {8, 1, 6}, o elemento a[1] contém 1 e pode receber outro inteiro.
ERANGE
A macro ERANGE identifica um erro de faixa informado por meio de errno, como um resultado de strtol grande ou pequeno demais para long.
Defina errno como zero antes da conversão e depois verifique se é ERANGE antes de aceitar uma sequência longa de algarismos.
Erro de uma unidade
Um erro de uma unidade usa um limite uma posição além ou aquém do correto, frequentemente por confundir a quantidade de elementos com seu último índice válido.
A condição i <= n faz um percurso de n elementos indexado a partir de zero tentar um acesso extra no índice n.
Estouro de buffer
Um estouro de buffer é uma gravação além do armazenamento disponível de um buffer, podendo corromper dados próximos ou informações de controle do programa.
Copiar seis bytes de texto e um terminador para um arranjo de seis bytes ultrapassa esse arranjo em um byte.

F

Faixa de valores permitida pela aplicação
Uma faixa de valores permitida pela aplicação é o intervalo de valores que um programa permite para uma finalidade, podendo ser mais restrito que a faixa completa do tipo numérico.
Uma quantidade convertida de 120 cabe em um inteiro, mas não atende a uma regra do programa que exige uma quantidade de 1 a 100.
fgets
A função fgets lê uma entrada de linha com limite de tamanho, mantendo uma nova linha alcançada e acrescentando um terminador quando lê texto com sucesso.
Um buffer de oito bytes pode receber no máximo sete caracteres de entrada, deixando texto adicional da linha para leituras posteriores.
Filtragem
A filtragem constrói um resultado a partir de caracteres de entrada selecionados, usando uma posição de escrita separada porque caracteres rejeitados não ocupam espaço na saída.
Manter apenas as letras de “q2r7” grava “qr” na saída e depois acrescenta seu byte zero terminador.
Fim de arquivo
O fim de arquivo marca que não há mais entrada disponível; fgets ainda pode ter sucesso se tiver lido caracteres antes de alcançar essa condição.
Um arquivo que termina com “done” e sem nova linha ainda permite que um buffer fgets suficientemente grande receba esse texto final.

Í

Índice
Um índice é uma posição inteira usada para selecionar um elemento de arranjo, começando em zero e terminando uma unidade abaixo da quantidade de elementos.
Para um arranjo de sete elementos, o índice 6 acessa o último elemento; o índice 7 já está fora dele.

I

Inicializador
Um inicializador fornece valores iniciais quando um objeto é declarado; valores ausentes em um inicializador de arranjo tornam-se zero.
int hits[4] = {3} inicializa o primeiro contador com 3 e os outros três contadores com zero.

L

Laços aninhados
Laços aninhados colocam um laço dentro de outro para que o laço interno se repita para cada valor do laço externo, como colunas dentro de cada linha.
Um percurso de duas linhas e cinco colunas executa o corpo interno dez vezes quando ambos os índices permanecem dentro de seus limites.
Largura da linha
A largura da linha é a quantidade de elementos em cada arranjo interno e é necessária em um parâmetro de tabela para localizar elementos indexados.
O parâmetro int g[][6] aceita linhas com seis inteiros de largura, enquanto a função chamadora fornece a quantidade de linhas separadamente.
Largura de campo
Uma largura de campo em uma conversão %s limita a quantidade de caracteres de entrada armazenados antes do byte zero terminador adicional.
Para char word[10], a conversão %9s reserva o décimo byte disponível para o terminador.
Limite de palavra
Um limite de palavra é a transição usada por um programa para decidir onde uma palavra começa ou termina, de acordo com a definição de palavra escolhida.
Um contador de sequências de letras inicia uma nova palavra quando encontra uma letra após um espaço, um sinal de pontuação ou o início da cadeia.
Linha
Uma linha é um arranjo interno em uma matriz bidimensional, contendo uma quantidade fixa de elementos consecutivos do mesmo tipo.
Em int table[2][5], table[1] designa a segunda linha, que contém cinco posições de inteiros.
Linha parcial
Uma linha parcial é a parte lida antes que o limite de tamanho de um buffer interrompa a entrada, podendo deixar mais dessa linha lógica sem leitura.
Uma resposta longa pode chegar em dois trechos de fgets, portanto o segundo trecho não deve se tornar uma nova resposta.
Lista de cadeias de caracteres com capacidade fixa por cadeia
Uma lista de cadeias de caracteres com capacidade fixa por cadeia armazena texto em linhas de um arranjo 2D de char, com cada linha reservando sua própria capacidade fixa e espaço para o terminador.
Em char labels[5][12], cada rótulo tem doze bytes de armazenamento, mesmo quando seu texto atual é menor.
Literal de cadeia de caracteres
Um literal de cadeia de caracteres é um texto entre aspas duplas que fornece um arranjo de caracteres terminado em nulo cujo conteúdo o programa em execução não deve modificar.
const char *label = "ready" refere-se a um literal; uma cópia modificável pode usar char label[] = "ready".

M

Máscara de bits
Uma máscara de bits seleciona posições específicas de bits para teste; uma operação AND mantém apenas os bits de entrada marcados por uns na máscara.
A máscara 0xC0 mantém os dois bits mais significativos para que um programa possa reconhecer o padrão de continuação UTF-8.
Matriz bidimensional
Uma matriz bidimensional é um arranjo de arranjos de linhas de mesma largura, com dois índices que selecionam um elemento por sua linha e coluna.
int board[4][6] fornece 24 inteiros, acessados por quatro índices válidos de linha e seis índices válidos de coluna.
memcmp
A função memcmp compara uma quantidade fixa de bytes e informa sua ordem, testando representações armazenadas em vez de valores numéricos.
Zeros positivo e negativo de ponto flutuante podem ser iguais na comparação de valores enquanto memcmp encontra bytes armazenados diferentes.
memcpy
A função memcpy copia exatamente uma quantidade solicitada de bytes entre regiões que não se sobrepõem, sem interpretar terminadores de cadeia.
memcpy(b, a, sizeof a) copia todo o armazenamento do arranjo a quando b tem espaço suficiente e é uma região separada.
memmove
A função memmove copia uma quantidade solicitada de bytes com comportamento definido mesmo quando as regiões de origem e destino se sobrepõem.
Mover três bytes dentro do mesmo arranjo de caracteres exige uma operação que aceite sobreposição, como memmove.
memset
A função memset preenche uma região com um valor de byte repetido, sem atribuir esse valor numérico separadamente a cada elemento do arranjo.
memset(counts, 0, sizeof counts) zera um arranjo de inteiros; o valor de byte 1 não atribuiria o inteiro 1.

P

Parâmetro de arranjo
Um parâmetro de arranjo é ajustado para um parâmetro ponteiro, dando a uma função acesso aos elementos da função chamadora sem sua contagem.
Uma função declarada com int a[] ainda precisa de um comprimento separado antes de poder percorrer sua entrada com um laço.
Parâmetro de tipo variavelmente modificado
Um parâmetro de tipo variavelmente modificado usa parâmetros de tamanho anteriores em seu tipo de arranjo para que uma função possa receber tabelas com larguras de linha diferentes.
GCC e Clang aceitam int g[rows][cols] após parâmetros que definem quantidades positivas e correspondentes de linhas e colunas.
Parâmetro qualificado com const
Um parâmetro qualificado com const impede a modificação dos elementos referenciados por meio desse parâmetro, mas ainda permite sua leitura.
Uma função que calcula um total usa const int a[] porque somar os valores de entrada não exige alterá-los.
Ponteiro de fim da conversão
Um ponteiro de fim da conversão registra onde a conversão numérica parou, permitindo que um programa detecte ausência de conversão ou caracteres restantes indesejados.
Para a entrada “9z”, strtol retorna 9, mas o ponteiro de fim da conversão se refere ao z que não foi consumido.
Ponteiro nulo
Um ponteiro nulo é um valor de ponteiro que não se refere a nenhum objeto; funções de busca o usam para indicar que não existe posição correspondente.
Verifique strchr(name, '.') != NULL antes de usar seu resultado como o início de um sufixo de nome de arquivo.
Ponto de código Unicode
Um ponto de código Unicode é uma posição numerada no espaço de códigos Unicode, usada para identificar caracteres e outros valores de texto atribuídos.
A letra pré-composta “é” é U+00E9, embora seu armazenamento em UTF-8 consista em dois bytes separados.

Q

Quantidade de elementos em uso
A quantidade de elementos em uso registra quantos elementos do arranjo pertencem atualmente aos dados ativos e determina o limite do laço de processamento.
Se apenas 12 entradas tiverem sido preenchidas em um arranjo de 50 elementos, um laço de processamento de dados para na contagem 12.

R

Redução modular sem sinal
A redução modular sem sinal reduz um resultado aritmético sem sinal módulo a faixa de seu tipo, de modo que subtrair um de zero produz um valor grande.
Uma contagem regressiva com size_t não pode parar ao se tornar negativa; use i > 0 e acesse a posição i - 1.

S

Sequência de escape
Uma sequência de escape é uma forma de escrever no código-fonte, baseada em barra invertida, um caractere que precisa de notação especial, como nova linha ou aspas.
O literal "A\nB" contém uma nova linha entre suas letras; a barra invertida e o n codificam um caractere.
size_t
O tipo size_t é um tipo inteiro sem sinal usado para tamanhos de objetos e retornado por sizeof; ele é comumente usado para comprimentos de arranjos.
printf("%zu", sizeof data) usa o formato de tamanho para imprimir a quantidade de bytes ocupados por data.
snprintf
A função snprintf formata texto dentro de uma capacidade fornecida e retorna o comprimento completo necessário para o texto ou um resultado negativo de erro.
Um retorno de 12 para capacidade 10 significa que o resultado armazenado foi truncado, embora continue terminado em nulo.
strcat
A função strcat acrescenta uma cadeia de origem no terminador existente do destino e grava um novo terminador após o texto combinado.
Acrescentar “box” a “red ” precisa de oito bytes de armazenamento no destino, incluindo o byte zero final.
strchr
A função strchr encontra o primeiro caractere correspondente em uma cadeia e retorna sua posição, ou um ponteiro nulo se não houver correspondência.
Buscar um ponto em “name.part.ext” com strchr encontra o trecho final “.part.ext”, incluindo o primeiro ponto.
strcmp
A função strcmp compara cadeias terminadas em nulo como bytes sem sinal e retorna um resultado negativo, zero ou positivo conforme sua ordem.
strcmp("oak", "oak") == 0 testa conteúdos iguais mesmo quando as duas cadeias ocupam espaços de armazenamento separados.
strcpy
A função strcpy copia uma cadeia de origem completa e seu terminador para um destino que não se sobrepõe à origem e tem armazenamento disponível suficiente.
Copiar “pine” grava cinco bytes incluindo o zero, portanto cabe em um destino declarado como char out[5].
strcspn
A função strcspn mede a parte inicial de uma cadeia que não contém nenhum dos caracteres fornecidos em seu segundo argumento.
strcspn("fir:oak", ":,") retorna 3 porque o primeiro separador listado ocorre após as três letras de fir.
strlen
A função strlen percorre uma cadeia válida até seu terminador e retorna a quantidade de bytes anteriores como um valor size_t.
Para char tag[30] = "oak", strlen(tag) é 3, enquanto sizeof tag informa a capacidade completa de 30 bytes.
strncat
A função strncat acrescenta no máximo uma quantidade especificada de bytes de texto da origem e depois grava um terminador após eles.
Para uma cadeia de destino válida, o espaço restante para texto é a capacidade menos seu comprimento atual menos um.
strncmp
A função strncmp compara no máximo uma quantidade especificada de bytes de cadeia, parando antes ao encontrar um fim ou uma diferença.
Uma correspondência de três bytes entre “run-fast” e “run-slow” estabelece um prefixo compartilhado, não que as cadeias completas sejam iguais.
strncpy
A função strncpy copia até um limite e preenche textos menores com zeros, mas não acrescenta terminador quando a origem preenche esse limite.
Copiar “abcd” com limite 3 armazena a, b e c sem um byte zero, portanto outra etapa deve terminar o resultado.
strrchr
A função strrchr encontra o último caractere correspondente em uma cadeia e retorna sua posição, ou um ponteiro nulo se não houver correspondência.
O último ponto em “name.part.ext” inicia “.ext”, portanto strrchr pode localizar o separador da extensão.
strtol
A função strtol converte texto para long usando uma base escolhida e informa uma posição de parada e informações sobre erro de faixa.
Chamar strtol(text, &end, 10) permite que o programa rejeite uma letra restante após algarismos decimais válidos.
Subcadeia
Uma subcadeia é uma sequência consecutiva de caracteres dentro de outra cadeia; strstr encontra sua primeira posição inicial correspondente.
Buscar “green” em “blue-green” retorna uma posição a partir da qual a cadeia restante é “green”.

T

Terminador nulo
Um terminador nulo é o caractere de valor zero que marca o fim de uma cadeia em C, ocupando armazenamento sem aumentar o comprimento do texto.
Gravar text[2] = '\0' faz as funções de cadeia pararem após os dois primeiros caracteres desse arranjo.
Tokenização
A tokenização divide texto em partes separadas por delimitadores; strtok faz isso modificando a entrada e mantendo uma posição.
Uma segunda chamada strtok(NULL, ",") continua dividindo a mesma entrada modificável em vez de reiniciá-la.
Transformação no próprio local
Uma transformação no próprio local altera valores em seu armazenamento existente em vez de construir um arranjo de saída separado.
Converter uma cadeia ASCII modificável para maiúsculas pode atribuir cada caractere convertido de volta à mesma posição indexada.
Truncamento
O truncamento é a perda de texto além de um limite de comprimento disponível, o que pode evitar um estouro e ainda assim alterar o significado dos dados.
Um rótulo de arquivo encurtado pode corresponder a outro texto, portanto uma interface que exige o nome completo deve rejeitar um resultado truncado.

U

UTF-8
UTF-8 é uma codificação Unicode que armazena cada valor escalar em um a quatro bytes e preserva as codificações ASCII de um byte.
A cadeia “café” ocupa cinco bytes de texto UTF-8, mais um byte zero quando armazenada como cadeia em C.

V

Vetor de frequências
Um vetor de frequências armazena um contador para cada categoria, usando um valor de categoria validado para selecionar o contador a incrementar.
Após converter um algarismo no valor 4, counts[4]++ acrescenta uma ocorrência à contagem desse algarismo.

Sobre esta aula

Por StudeliRelatar um erro

Crie seus próprios Materiais de Estudo

Transforme suas anotações, slides e documentos em Guias de Estudo, Questões de Prática, Flashcards e mais 6 ferramentas.

Começar