# Funções, arrays e texto

Contratos, passagem de argumentos, arrays C, estruturas, strings e leitura de linhas.

Página: https://resumos.rgo.pt/cadeiras/p/funcoes-arrays/

Uma função de contagem pode receber as notas já lidas e devolver um total. Não trata a entrada nem imprime.

O contrato diz que dados recebe, se os pode alterar e que resultado devolve. Separar estas decisões permite testar a contagem sem fornecer dados pelo teclado.

## Parâmetros e argumentos

Os **parâmetros** são os nomes declarados na função: `a` e `b` em `int maximo(int a, int b)`. Os **argumentos** são as expressões fornecidas na chamada: `4` e `9` em `maximo(4, 9)`. O parâmetro recebe uma cópia ou acesso conforme o contrato; o argumento diz qual o valor ou objeto usado nessa chamada.

## Declarar e chamar funções

A declaração `int maximo(int a, int b);` anuncia o tipo do resultado e dos parâmetros. A definição acrescenta o corpo. A declaração tem de estar visível antes da chamada; a definição necessária tem de existir quando o programa é ligado.

```
// Excerto: definição de uma função.
int maximo(int a, int b) {
    return a > b ? a : b;
}
```

O operador condicional `condicao ? a : b` escolhe `a` se a condição é verdadeira e `b` caso contrário. Aqui `4 > 9` é falso, por isso `maximo(4, 9)` devolve `9`; `return` termina a chamada sem imprimir, por isso o chamador decide se guarda, calcula ou imprime. Uma função `void` não devolve valor.

Uma **sobrecarga** usa o mesmo nome com listas de parâmetros diferentes. O compilador escolhe pela chamada; o tipo de retorno, por si só, não distingue duas sobrecargas. Argumentos por defeito permitem omitir os últimos argumentos, por exemplo `void imprimir(int n, char separador = ' ');`. O valor por defeito tem de estar visível no ponto da chamada. Não o repitas na definição se já o declaraste num cabeçalho.

## Valor, referência e const

**main.cpp**

```cpp
#include <iostream>
#include <string>

void incrementar_copia(int x) { ++x; }
void incrementar(int& x) { ++x; }
std::size_t comprimento(const std::string& texto) { return texto.size(); }

int main() {
    int n = 4;
    incrementar_copia(n);
    std::cout << n << "\n";
    incrementar(n);
    std::cout << n << " " << comprimento("Ana Maria") << "\n";
}
```

Saída:

```
4
5 9
```

`n` começa em `4`. `incrementar_copia(n)` cria um parâmetro independente com o valor `4`. Incrementar esse parâmetro não toca em `n`, que continua `4`.

`incrementar(n)` recebe uma referência ao original e deixa `n` com `5`. `comprimento("Ana Maria")` conta 9 unidades: 3 de `Ana`, 1 espaço e 5 de `Maria`.

Uma referência é um nome alternativo para um objeto. Tem de ser inicializada e nunca muda de destino. Atribuir à referência altera o objeto referido.

| Parâmetro | O que a função recebe | Uso habitual |
| --- | --- | --- |
| `int n` | Um valor independente | Números pequenos, só de leitura |
| `T& objeto` | Acesso ao original | Alteração faz parte da operação |
| `const T& objeto` | Acesso de leitura ao original | Evitar copiar uma string ou coleção |
| `T* objeto` | Um endereço, que pode ser nulo | Opcionalidade ou acesso a um intervalo |

`const T&` impede alterações através dessa referência; não torna constantes todas as outras formas de acesso ao objeto. Evitar uma cópia de um `int` raramente justifica uma referência.

Retornar um `std::string` por valor é seguro e permite ao compilador evitar cópias. Retornar uma referência a uma variável local deixa acesso a um objeto que já morreu.

## Parâmetros apontadores

A secção seguinte passa arrays como `const int* valores`, que se lê assim: `valores` guarda o endereço do primeiro inteiro e `valores[i]` acede ao elemento nessa posição. Ainda não precisas de aritmética de apontadores, que a página [Apontadores e memória](https://resumos.rgo.pt/cadeiras/p/apontadores-memoria/#endere%C3%A7o-de-e-dereferencia%C3%A7%C3%A3o) explica depois com objetos e setas.

## Arrays C e dimensões

Um array `int v[4]` contém quatro inteiros contíguos. Os índices válidos são `0`, `1`, `2` e `3`. `v[4]` está fora do array. Um array não aumenta de tamanho e não transporta automaticamente o seu comprimento quando é passado como argumento.

Numa lista de parâmetros, `const int valores[]` é ajustado para `const int* valores`. Por isso, a função abaixo recebe também `n`. `std::size_t`, de `<cstddef>`, é o tipo inteiro sem sinal usado para tamanhos e índices. Não representa valores negativos.

**main.cpp**

```cpp
#include <cstddef>
#include <iostream>

int soma(const int* valores, std::size_t n) {
    int total = 0;
    for (std::size_t i = 0; i < n; ++i) total += valores[i];
    return total;
}

int main() {
    int valores[] = {4, -2, 7, 1};
    std::cout << soma(valores, 4) << "\n";
    std::cout << soma(valores + 1, 2) << "\n";
    std::cout << soma(nullptr, 0) << "\n";
}
```

Saída:

```
10
5
0
```

O contrato de `soma` exige `n` elementos válidos e que todas as somas parciais caibam em `int`. Na segunda chamada, o intervalo contém `-2` e `7`, por isso soma `5`. Na chamada vazia, o ciclo não desreferencia o apontador.

| Intervalo | Índices originais | Evolução de `total` |
| --- | --- | --- |
| Array completo | 0, 1, 2, 3 | 0 → 4 → 2 → 9 → 10 |
| Dois valores a partir de -2 | 1, 2 | 0 → -2 → 5 |
| Vazio | nenhum | 0 |

O índice `i` é relativo ao início recebido. Na segunda chamada, `valores[0]` dentro da função é o `-2` que está no índice 1 do array do chamador.

`sizeof(valores)` dentro desta função mede o apontador, não o array do chamador. Para um tamanho fixo com interface que o conserva, usa `std::array<T, N>`. Para tamanho variável, usa `std::vector<T>`. Um `std::vector<int>` guarda uma sequência de inteiros; `std::vector<int> v{4, -2, 7}` cria três elementos. A página [Templates e STL](https://resumos.rgo.pt/cadeiras/p/templates-stl/#vector-o-array-que-cresce) desenvolve esta coleção.

Um **for por intervalo**, como `for (int x : valores)`, visita cada elemento de um array cujo tamanho é conhecido ou de uma coleção. `x` recebe uma cópia de cada inteiro; alterar `x` não altera o elemento. Com `for (int& x : valores)`, `x` refere-se ao elemento e permite alterá-lo. Um apontador isolado não fornece o tamanho para este percurso.

Para percorrer índices de trás para a frente com `std::size_t`, que não representa negativos, começa em `size()` e repete enquanto `i > 0`. Decrementa primeiro e lê `v[i]`; em alternativa, lê `v[i - 1]` e só depois decrementa. Qualquer das ordens evita subtrair um a zero e evita testar `i >= 0`, que num tipo sem sinal nunca falha.

```
for (std::size_t i = n; i > 0; ) {
    --i;
    std::cout << valores[i] << '\n';
}
```

O exercício de inverter texto treina este percurso com uma string.

Um array C multidimensional tem os elementos da última dimensão contíguos. Em `int matriz[2][3]`, há duas linhas de três inteiros. Percorre com dois índices, um por dimensão:

```
// Excerto: soma todos os elementos de uma matriz 2x3.
int soma_matriz(const int matriz[][3], int linhas) {
    int total = 0;
    for (int l = 0; l < linhas; ++l)
        for (int c = 0; c < 3; ++c) total += matriz[l][c];
    return total;
}
```

Com `{{1, 2, 3}, {4, 5, 6}}`, a soma é `21`. Uma função que recebe `const int matriz[][3]` precisa de conhecer a dimensão interior para avançar entre linhas. Isto não equivale a `int**`: um apontador para linhas de três inteiros e um apontador para apontadores têm representações e tipos diferentes.

## Arrays de entrada e de saída

Uma função pode escrever o resultado num array reservado pelo chamador e devolver quantas posições ficaram válidas. O tamanho físico e o número de resultados são diferentes: se há espaço para seis inteiros mas só quatro foram escritos, só essas quatro posições pertencem à saída.

Vamos conservar todas as ocorrências dos valores repetidos, pela ordem original. Em `4, 9, 4, 2, 9, 7`, o resultado é `4, 9, 4, 9`, não apenas os valores distintos `4, 9`. A ocorrência final de 4 também conta, mesmo sem outro 4 depois dela: existe uma ocorrência anterior.

O contrato exige `n >= 0`, uma entrada com `n` elementos e uma saída com capacidade para pelo menos `n`. Os arrays não se sobrepõem e a origem fica intacta. Para `n == 0`, a função não lê nem escreve, por isso aceita apontadores nulos. Neste algoritmo, usamos só ciclos e arrays, como num enunciado que não permite contentores ou funções da biblioteca.

**main.cpp**

```cpp
#include <iostream>

// n >= 0; entrada e saida nao se sobrepoem, com n posicoes validas.
int filtrar_repetidos(const int entrada[], int n, int saida[]) {
    int usados = 0;
    for (int i = 0; i < n; ++i) {
        bool repetido = false;
        for (int j = 0; j < n; ++j) {
            if (i != j && entrada[i] == entrada[j]) {
                repetido = true;
                break;
            }
        }
        if (repetido) saida[usados++] = entrada[i];
    }
    return usados;
}

int main() {
    int entrada[] = {4, 9, 4, 2, 9, 7};
    int saida[6];
    int usados = filtrar_repetidos(entrada, 6, saida);
    std::cout << usados << ":";
    for (int i = 0; i < usados; ++i) std::cout << " " << saida[i];
    std::cout << "\n";
    std::cout << filtrar_repetidos(nullptr, 0, nullptr) << "\n";
}
```

Saída:

```
4: 4 9 4 9
0
```

Para cada índice `i`, procuramos um índice diferente `j` com o mesmo valor. Não basta comparar o elemento consigo próprio, nem procurar só à sua direita. Ao encontrar uma correspondência, copiamos `entrada[i]` uma única vez para `saida[usados]` e avançamos `usados`.

| `i` | Valor | Existe noutra posição? | Prefixo de saída |
| --- | --- | --- | --- |
| 0 | 4 | Sim, em 2 | 4 |
| 1 | 9 | Sim, em 4 | 4, 9 |
| 2 | 4 | Sim, em 0 | 4, 9, 4 |
| 3 | 2 | Não | 4, 9, 4 |
| 4 | 9 | Sim, em 1 | 4, 9, 4, 9 |
| 5 | 7 | Não | 4, 9, 4, 9 |

Antes da iteração `i`, o prefixo de `saida` com índices em `[0, usados)` contém exatamente as ocorrências que passaram o critério nos índices anteriores. Percorrer `i` por ordem conserva a ordem de entrada. O pior caso faz uma procura linear para cada elemento, com custo quadrático em `n`; a memória adicional da função é constante, além do destino recebido. A capacidade disponível não inicializa a cauda nem a transforma em resultados. O chamador usa o número devolvido para a percorrer.

## Structs e tipos compostos

```
// Excerto: tipo e acesso aos seus campos.
struct Ponto {
    double x;
    double y;
};
Ponto p{2.0, 3.0};
```

A `struct` reúne campos que descrevem um valor: `Ponto` é um tipo, `p` é um objeto desse tipo e `p.x` vale `2.0`. O compilador pode introduzir **padding**, bytes entre campos ou no fim para alinhar os objetos. Por isso, `sizeof(Ponto)` não deve ser deduzido somando tamanhos de campos. Não graves a memória bruta de uma struct como formato portátil de ficheiro.

`struct` e `class` permitem membros e métodos. A diferença sintática central é o acesso por defeito: público na `struct`, privado na `class`. Uma enumeração com âmbito, como `enum class Estado { livre, ocupado };`, cria valores nomeados e evita confundir um estado com um inteiro qualquer.

## Strings e linhas

`std::string` guarda uma sequência de unidades `char`. `size()` conta essas unidades, não caracteres Unicode visuais. Para ler ou alterar unidades do texto, usa índices de `0` a `size() - 1`; numa string vazia não há nenhuma. `at()` verifica esse limite e lança uma exceção. `texto[texto.size()]` permite consultar o terminador nulo, mas não é mais uma unidade do texto nem permite guardar outro carácter nessa posição. `+` concatena strings e `==` compara o conteúdo. `substr(pos, quantidade)` extrai uma parte; `find` devolve uma posição ou `std::string::npos` quando não encontra.

Depois de `>>` ler um número, o fim de linha pode continuar na entrada, por isso um `getline` imediato leria esse resto vazio. Neste formato, a idade ocupa a primeira linha e o nome a segunda. Descarta o resto da primeira linha antes de leres o nome.

**main.cpp**

```cpp
#include <iostream>
#include <limits>
#include <string>

int main() {
    int idade = 0;
    if (!(std::cin >> idade)) {
        std::cout << "Idade invalida\n";
        return 0;
    }
    std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
    std::string nome;
    if (!std::getline(std::cin, nome)) {
        std::cout << "Nome em falta\n";
        return 0;
    }
    std::cout << nome << " | " << idade << " | " << nome.size() << "\n";
}
```

Saída:

```
Ana Maria | 19 | 9
```

`ignore` descarta até ao `\n`, incluindo-o. `std::getline` guarda os espaços interiores e retira o terminador. A alternativa `getline(std::cin >> std::ws, nome)` descarta também espaços iniciais e linhas vazias; só a uses quando isso corresponde ao formato pretendido.

Para separar campos dentro de uma linha, `find` localiza um separador e `substr` extrai cada parte. Com `s = "azul,verde"`, `s.find(',')` devolve `4`. `s.substr(0, 4)` é `"azul"` e `s.substr(5)` é `"verde"`. Quando `find` não encontra o carácter, devolve `std::string::npos`; nesse caso não há segundo campo.

## Strings de C e o terminador

Uma string de C é uma sequência de `char` terminada por `\0`. O array `char nome[] = "Ana";` tem quatro posições: `A`, `n`, `a`, `\0`. O terminador não aparece no texto impresso, mas ocupa memória. `<cstring>` fornece `strlen`, que percorre até ao terminador, e operações de cópia. Estas funções exigem que os apontadores e capacidades estejam corretos.

Arrays de `char` comparados com `==` não comparam o texto, comparam endereços depois da conversão para apontadores. Usa `std::string`, ou `std::strcmp` quando o contrato pede strings de C válidas. `strcpy` não verifica o tamanho do destino. Reservar só três posições para copiar `"Ana"` é insuficiente.

### Presença de letras em dois textos

Agora queremos as letras que aparecem num dos textos, mas não nos dois. Trata-se da **diferença simétrica** dos conjuntos de letras. Contar repetições ou retirar caracteres na ordem em que aparecem responderia a outra pergunta.

O alfabeto deste exemplo tem as 26 letras de `a` a `z`; a maiúscula correspondente conta como a mesma letra. Espaços, números e pontuação são ignorados. Não estamos a tratar todo o texto Unicode. As duas entradas são strings de C válidas, não nulas, com até 1000 unidades `char`. O destino não se sobrepõe às entradas e tem pelo menos 27 posições: uma por letra possível e outra para `\0`.

Guardamos duas tabelas de presença, inicialmente falsas. Depois de ler um texto, `em_a[i]` diz se a letra de índice `i` apareceu pelo menos uma vez. Marcar novamente a mesma posição não muda o conjunto. `indice_letra` procura em dois alfabetos explícitos, sem depender de subtrair códigos dos caracteres nem de chamar uma função da biblioteca.

**main.cpp**

```cpp
#include <iostream>

int indice_letra(char letra) {
    const char minusculas[] = "abcdefghijklmnopqrstuvwxyz";
    const char maiusculas[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    for (int i = 0; i < 26; ++i)
        if (letra == minusculas[i] || letra == maiusculas[i]) return i;
    return -1;
}

// texto e uma string de C valida com, no maximo, 1000 unidades char.
void marcar_letras(const char texto[], bool presentes[]) {
    for (int i = 0; texto[i] != '\0'; ++i) {
        int indice = indice_letra(texto[i]);
        if (indice >= 0) presentes[indice] = true;
    }
}

// a e b sao validas; saida tem 27 posicoes e nao se sobrepoe as entradas.
int diferenca_letras(const char a[], const char b[], char saida[]) {
    bool em_a[26]{};
    bool em_b[26]{};
    marcar_letras(a, em_a);
    marcar_letras(b, em_b);
    const char alfabeto[] = "abcdefghijklmnopqrstuvwxyz";
    int usados = 0;
    for (int i = 0; i < 26; ++i)
        if (em_a[i] != em_b[i]) saida[usados++] = alfabeto[i];
    saida[usados] = '\0';
    return usados;
}

int main() {
    char saida[27];
    int n = diferenca_letras("ac AB!", "B-cd", saida);
    std::cout << n << " [" << saida << "]\n";
    n = diferenca_letras("abAB", "ba", saida);
    std::cout << n << " [" << saida << "]\n";
}
```

Saída:

```
2 [ad]
0 []
```

Em `"ac AB!"`, ficam presentes `a, b, c`; em `"B-cd"`, `b, c, d`. As letras b e c aparecem nos dois e são excluídas. A condição `em_a[i] != em_b[i]` é verdadeira só para a e d. Percorrer a tabela do índice 0 ao 25 escreve o resultado alfabeticamente, uma vez por letra, sem ordenar o texto original.

No segundo caso, `"abAB"` repete letras que `"ba"` contém uma vez, mas os conjuntos são iguais. A saída é vazia e continua a precisar de `saida[0] = '\0'`. O número devolvido conta letras, sem incluir o terminador. No maior resultado, escrevemos 26 letras e o terminador na posição 26; reservar só 26 posições falharia precisamente nesse caso.

A procura de cada unidade percorre no máximo 26 posições do alfabeto, comparando maiúscula e minúscula. No fim, percorremos as 26 presenças. Com este alfabeto fixo, o tempo é linear no comprimento total dos textos e a memória adicional é constante. O algoritmo não usa `string`, `set`, `sort` nem `tolower`; o `main` apenas usa `cout` para mostrar o resultado. As ferramentas permitidas fazem parte do enunciado, além do resultado esperado.

## Recursão e progresso

Uma função recursiva chama-se a si própria e precisa de um caso base com uma medida que diminua até esse caso. A soma recursiva de `1` até `n` pode usar `soma(0) = 0` e `soma(n) = n + soma(n - 1)` para `n > 0`. O contrato exige `n >= 0` e uma soma representável no tipo escolhido. Se não tratares `0`, podes criar chamadas sem fim.

Para `n = 3`, a descida constrói chamadas e a subida combina resultados:

| Passo | Chamada | Resultado |
| --- | --- | --- |
| descer | `soma(3) = 3 + soma(2)` | pendente |
| descer | `soma(2) = 2 + soma(1)` | pendente |
| descer | `soma(1) = 1 + soma(0)` | pendente |
| base | `soma(0) = 0` | 0 |
| subir | `1 + 0` | 1 |
| subir | `2 + 1` | 3 |
| subir | `3 + 3` | 6 |

A descida reduz `n` e a subida combina os resultados. Uma entrada negativa nunca se aproxima do caso base.

Cada chamada conserva os seus parâmetros e variáveis locais. Um grande número de chamadas pode esgotar a pilha. Quando um ciclo resolve o mesmo problema com um acumulador, não precisas de recursão. Usa-a quando a decomposição do problema a torna clara, não como substituição automática de qualquer ciclo.

## Exercícios

A referência não muda de destino

Segue `int a = 4; int b = 9; int& r = a; r = b; b = 2;`. Quanto vale `a` no fim?

Pista

Lembra-te de que r é um nome alternativo para a.

Solução

`r = b` guarda `9` em `a`. A última atribuição altera apenas `b` para `2`. O resultado é `a = 9`, `b = 2` e `r = 9`.

[Voltar à explicação](https://resumos.rgo.pt/cadeiras/p/funcoes-arrays/#valor-refer%C3%AAncia-e-const)

Passar um subintervalo válido

Um array `v` contém `1, 2, 3, 4`. Qual chamada pode ler o intervalo inteiro sem sair do array? A função `soma(inicio, n)` lê `n` inteiros a partir de `inicio` e não lê nenhum quando `n` é zero.

Pista

Marca os índices 0 a 3 e a posição logo depois do fim.

Solução

`soma(v + 2, 2)` lê os índices `2` e `3`. O tamanho deve acompanhar o intervalo real, não o tamanho inicial do array.

*   **`soma(v + 2, 2)`.** Os dois elementos são os índices 2 e 3.
*   **`soma(v + 2, 3)`.** A terceira leitura seria v\[4\], fora do array.
*   **`soma(v + 4, 1)`.** v + 4 marca o fim e não aponta para um elemento.
*   **`soma(nullptr, 1)`.** Uma leitura por um apontador nulo viola o contrato.

Erros frequentes

Formar uma posição depois do fim pode ser válido; ler a partir dela não é.

[Voltar à explicação](https://resumos.rgo.pt/cadeiras/p/funcoes-arrays/#arrays-c-e-dimens%C3%B5es)

Programar uma contagem num intervalo fechado

O contrato pede o número de valores em \[minimo, maximo\], incluindo os extremos: um valor igual a minimo ou a maximo conta. Corrige a função sem alterar os dados de teste. O programa inicial compila e corre, mas os resultados nos limites estão errados. A técnica de testar limites é desenvolvida em [Exceções e testes](https://resumos.rgo.pt/cadeiras/p/excecoes-testes/#testar-com-asserts).

**main.cpp**

```cpp
#include <iostream>
#include <vector>

int contar(const std::vector<int>& valores, int minimo, int maximo) {
    int total = 0;
    for (int x : valores)
        if (minimo < x && x < maximo) ++total;
    return total;
}

int main() {
    std::cout << "Vazio: " << contar({}, 1, 3) << " (esperado 0)\n";
    std::cout << "Limites: " << contar({1, 2, 3}, 1, 3) << " (esperado 3)\n";
    std::cout << "Repetidos: " << contar({2, 2, 2}, 2, 2) << " (esperado 3)\n";
}
```

Pista

Compara a condição com a definição de intervalo fechado.

Solução

A condição correta é `minimo <= x && x <= maximo` . O vazio continua a dar zero, o intervalo de 1 a 3 inclui os três valores e o intervalo reduzido a 2 inclui as três repetições. A referência constante conserva a coleção original.

Confere a tua resposta:

*   Incluí os dois limites.
*   Obtive 0, 3 e 3 nos casos apresentados.
*   Mantive a coleção sem alterações e sem cópias desnecessárias.

Erros frequentes

Não alteres só um dos limites. Não mudes os resultados esperados para esconder a falha.

[Voltar à explicação](https://resumos.rgo.pt/cadeiras/p/excecoes-testes/#testar-com-asserts)

Construir uma string invertida

Define `std::string inverter(const std::string& texto)` para devolver as unidades do texto pela ordem inversa. Usa apenas texto ASCII, como `abc`, e conserva o argumento. A string vazia devolve uma string vazia. Esta resposta é de autoavaliação.

**main.cpp**

```cpp
#include <iostream>
#include <string>

std::string inverter(const std::string& texto) {
    return texto; // Substitui pelo algoritmo.
}

int main() {
    std::string original = "abc";
    std::cout << "[" << inverter("") << "] (esperado [])\n";
    std::cout << inverter("x") << " (esperado x)\n";
    std::cout << inverter(original) << " (esperado cba)\n";
    std::cout << inverter("abcd") << " (esperado dcba)\n";
    std::cout << original << " (esperado abc)\n";
}
```

Pista

Constrói outra string e percorre a entrada do fim para o início.

Mais uma pista

Começa com `i = texto.size()`. Enquanto `i > 0`, lê `texto[i - 1]` e diminui `i`. Assim, nunca subtrais um a zero para formar um índice.

Solução

```
std::string inverter(const std::string& texto) {
    std::string resultado;
    for (std::size_t i = texto.size(); i > 0; --i)
        resultado += texto[i - 1];
    return resultado;
}
```

Para `abc`, os índices lidos são 2, 1 e 0. No vazio, o ciclo não começa. O argumento só é lido; o resultado tem armazenamento próprio. Outra solução válida copia o texto e troca pares de posições simétricas.

Confere a tua resposta:

*   Devolvi uma string e não imprimi dentro da função.
*   Tratei o vazio, um carácter e comprimentos pares e ímpares.
*   Conservei o texto original e justifiquei os índices usados.

Erros frequentes

`size()` usa um tipo sem sinal. Começar em `size() - 1` no vazio, ou testar `i >= 0` para terminar uma contagem descendente sem sinal, dá limites errados. Inverter bytes de texto UTF-8 também pode estragar caracteres, por isso o contrato deste exercício limita a entrada a ASCII.

[Voltar à explicação](https://resumos.rgo.pt/cadeiras/p/funcoes-arrays/#strings-e-linhas)

Somar uma linha de uma matriz

A matriz recebida tem duas linhas válidas, `linha` é 0 ou 1 e a soma cabe em `int`. Escreve `int soma_linha(const int matriz[][3], int linha)` que devolve a soma das três colunas dessa linha. Usa a matriz `{{1, 2, 3}, {4, 5, 6}}` como caso de teste.

Pista

Um ciclo de c = 0 até c < 3 chega para uma linha fixa.

Solução

```
int soma_linha(const int matriz[][3], int linha) {
    int total = 0;
    for (int c = 0; c < 3; ++c) total += matriz[linha][c];
    return total;
}

// Teste: com m = {{1, 2, 3}, {4, 5, 6}},
// soma_linha(m, 0) devolve 6 e soma_linha(m, 1) devolve 15.
```

Com a matriz do enunciado, a chamada com linha `0` soma `1 + 2 + 3` e devolve `6`; com linha `1` soma `4 + 5 + 6` e devolve `15`. A dimensão interior `3` tem de estar na assinatura para o compilador calcular o endereço de `matriz[linha][c]`.

Confere a tua resposta:

*   Percorri as três colunas da linha indicada.
*   Obtive 6 para a primeira linha de {{1, 2, 3}, {4, 5, 6}}.
*   Justifiquei por que a função precisa da dimensão interior.

Erros frequentes

Não declares o parâmetro como `int**`. São representações diferentes.

[Voltar à explicação](https://resumos.rgo.pt/cadeiras/p/funcoes-arrays/#arrays-c-e-dimens%C3%B5es)

A última ocorrência também conta

Uma função deve copiar todas as ocorrências dos valores que aparecem mais de uma vez, pela ordem de entrada. Recebe `n >= 0`, arrays distintos e sem sobreposição, com `n` posições válidas em cada um. Não pode usar contentores nem funções da biblioteca. A entrada vazia deve devolver zero sem aceder aos apontadores.

Analisa esta proposta com `3, -1, 3, 8, -1, -1, 5`. Prevê o que escreve, calcula o resultado pedido e corrige a condição de procura. Explica também quantas posições do destino podem ser lidas depois da chamada.

```
int filtrar(const int entrada[], int n, int saida[]) {
    int usados = 0;
    for (int i = 0; i < n; ++i) {
        for (int j = i + 1; j < n; ++j) {
            if (entrada[i] == entrada[j]) {
                saida[usados++] = entrada[i];
                break;
            }
        }
    }
    return usados;
}
```

Pista

A ocorrência de índice 2 precisa de procurar antes ou depois de si?

Solução

A proposta escreve `3, -1, -1` e devolve 3: encontra parceiros à direita dos índices 0, 1 e 4. Perde o 3 do índice 2 e o -1 do índice 5, que só têm parceiros anteriores. O resultado pedido é `3, -1, 3, -1, -1`, com cinco elementos.

Para cada `i`, percorre todos os `j` de 0 a `n - 1` e exige `j != i` além da igualdade dos valores. Encontrar o próprio elemento não demonstra repetição. Ao encontrar outro, copia uma vez e termina essa procura. O percurso exterior conserva a ordem e não altera a entrada. Não uses uma coleção de valores únicos, pois o contrato conserva cada ocorrência repetida.

O chamador pode ler só o prefixo de tamanho devolvido, neste caso cinco posições, mesmo que tenha reservado sete. No vazio, o ciclo exterior não executa e o resultado é zero. Casos com um elemento e com todos distintos também dão zero; um caso em que todos os valores se repetem conserva o array completo.

Confere a tua resposta:

*   Obtive 3, -1, 3, -1, -1, com cinco posições válidas.
*   Identifiquei as últimas ocorrências perdidas pela procura só à direita.
*   Corrigi a procura, conservei a origem e respeitei a capacidade e o vazio.

[Voltar à explicação](https://resumos.rgo.pt/cadeiras/p/funcoes-arrays/#arrays-de-entrada-e-de-sa%C3%ADda)

Repetir uma letra não muda o conjunto

Define `int diferenca_letras(const char a[], const char b[], char saida[])` que escreve as letras presentes num dos textos, mas não nos dois, e devolve o número de letras escritas. Usa as 26 letras de a a z, ignora os restantes caracteres e trata maiúsculas como as minúsculas correspondentes. As entradas são strings de C válidas com até 1000 unidades `char`; o destino, sem sobreposição, tem 27 posições. Não uses contentores nem funções da biblioteca.

Prevê o resultado para `a = "Bbb+c"` e `b = "b D!"`. Uma proposta subtrai as frequências e inclui b porque aparece três vezes no primeiro e uma no segundo. Explica o erro e testa também textos sem letras, conjuntos iguais e uma saída com as 26 letras.

Solução

O primeiro conjunto é b,c e o segundo é b,d. A letra b pertence aos dois, por isso fica excluída apesar da frequência diferente. Só c e d pertencem a um dos textos. A função escreve `"cd"`, acrescenta `\0` e devolve 2.

Inicializa duas tabelas de 26 valores falsos. Percorre cada texto até `\0`, localiza a letra num alfabeto de maiúsculas/minúsculas e marca a sua presença; ignorar um carácter é diferente de o usar como índice. Percorre depois o alfabeto e copia apenas as posições em que as duas presenças diferem. Isto produz ordem alfabética sem ordenar as entradas.

Textos sem letras e conjuntos iguais produzem uma string vazia, com `saida[0] = '\0'`. Se uma entrada contém todo o alfabeto e a outra não tem letras, o resultado ocupa as posições 0 a 25 e o terminador ocupa a 26. O comprimento devolvido não inclui esse terminador. Compara estes casos com a implementação da lição, que mantém as entradas intactas.

Confere a tua resposta:

*   Obtive duas letras, cd, ignorando maiúsculas e repetições.
*   Usei presença por texto e não uma diferença de frequências.
*   Escrevi em ordem alfabética e reservei o terminador, incluindo no vazio e no resultado de 26 letras.

[Voltar à explicação](https://resumos.rgo.pt/cadeiras/p/funcoes-arrays/#presen%C3%A7a-de-letras-em-dois-textos)
