Introdução à Programação B (2017/2018)

Aulas teóricas

Artur Miguel Dias



Teórica 08 (13/nov/2017)

Tudo sobre strings.
Construções condicionais. Instrução if. Cadeias else-if. Instrução switch.
Exemplo: Interpretador de comandos para a Codificação de César.



Strings

Uma string é uma sequência de carateres. Usando strings, conseguimos manipular texto nos nossos programas escritos em C.

As strings são implementadas usando vetores de carateres. Felizmente que já sabemos muito sobre vetores e portanto manipular vetores de carateres não será novidade.

O único facto novo para aprender é que as strings são vetores de carateres com a seguinte propriedade especial:

Eis uma variável de tipo string inicializada de forma convencional. Repare que não nos esquecemos de terminar a string com o caráter '\0'. Mas o C oferece outra forma, mais conveniente, de inicializar strings, usando literais de tipo string. A nova forma de inicialização fica assim: Nesta segunda forma, o programador nunca escreve o caráter nulo no final de hello, porque o C já coloca esse caráter automaticamente.

Podemos dizer que as strings têm comprimento variável porque conseguimos controlar o seu comprimento através o posicionamento do caráter nulo. No entanto, é preciso ter cuidado: quando se define uma string indica-se sempre uma capacidade e essa capacidade nunca pode ser excedida.

Exemplos de funções que processam strings

Vamos ver vários exemplos de funções que manipulam strings. Repare que quando se passa uma string como parâmetro não interessa passar o seu comprimento, ao contrário do que se faz para os outros vetores. Realmente, o caráter nulo já indica onde termina o conteúdo útil da string.

A seguinte função calcula o comprimento duma string. Examine com atenção este ciclo for, porque é típico das funções que manipulam strings: o ciclo continua enquanto o caráter nulo não aparece; quando o caráter nulo aparecer, o ciclo para.

Por exemplo, a chamada strlen("hello") produz o resultado 5.

Outra maneira mais simples de escrever a mesma função:

A seguinte função testa se um dado caráter ocorre numa string:

Exemplo: a chamada pertence('l', "hello") produz o resultado true.

A seguinte função acrescenta um caráter no final duma string, assumindo que a string tem suficiente capacidade (ou seja, espaço) para tal:

Exemplo de chamada:

A seguinte função descodifica uma string contendo a representação dum número inteiro. Permite-se que apareçam espaços em branco no início, e o número pode iniciar-se com o sinal "+" ou "-". Como pode ver, a função é um pouco complicada, mas vale a pena estudá-la.

Exemplo de chamada:

A biblioteca padrão string

Para além das bibliotecas stdio, stdbool e math, temos também de nos habituar a usar biblioteca string, que contém numerosas funções predefinidas de manipulação de strings. Eis as funções de biblioteca sobre strings mais usadas na prática:

Exemplo: O que escreve o seguinte programa?

O tipo String

Nos nossos programas, usaremos com frequência a seguinte constante e o seguinte tipo: Também, no que resta desta aula e nas próximas aulas, usaremos o tipo String sempre que possível.

Leitura e escrita de strings

Usa-se o designador de formato "%s" para ler e escrever strings usando scanf e printf.

Vejamos primeiro um exemplo de escrita no ecrã:

Agora um exemplo de leitura a partir do teclado:

Note a seguinte regra nova para o caso da leitura de strings. Neste caso, não se coloca o "&" atrás do nome da variável. Trata-se da única exceção à regra. (Aborrecido não é? Além disso, as exceções arriscam-se a criar confusão.)

Outra questão importante: se o programa fizer o scanf duma string e você introduzir várias palavras, só a primeira será lida - na verdade, "%s" indica leitura duma palavra!

Para ler uma linha de texto completa, temos de usar a função ler_linha, definida abaixo. Muito frequentemente, incluiremos esta função nos nossos programas.

Segmento final duma string

Dada uma string str, por exemplo definida assim por vezes interessa referir o segmento final dessa string que começa na posição i. Para isso, usa-se a expressão: Há muitas situações em que nos interessa usar esta expressão. Vejamos dois exemplos.

Exemplo 1: A seguinte expressão testa se a palavra "pervenimus" aparece na posição com índice 23 da string str:

Exemplo 2: A seguinte função testa se a string str1 ocorre dentro da string str2:

A função testa se a string str1 ocorre dentro de str2 na posição 0, depois na posição 1, depois na posição 2, e assim sucessivamente...


Um programa interessante que usa strings: Codificação de César

Para praticar o uso de strings, vamos fazer um programa que codifique uma mensagem secreta usando a técnica da codificação de César. Assume-se que a mensagem a codificar só contém letras maiúsculas e possivelmente espaços e sinais de pontuação. Exemplo: "ATACAMOS AO NASCER DO SOL.".

A codificação consiste simplesmente em substituir cada letra, pela letra que estiver x posições mais adiante no alfabeto. Se for necessário ultrapassar a letra 'Z', então "dá-se a volta" reentrando pelo lado do 'A'. Todos os símbolos que não sejam letras maiúsculas não são alterados.

Por exemplo, a codificação de "OLA, OLE" com deslocamento x=2 fica: "QNC, QNG".

Eis uma solução do problema:
Júlio César

A função main usa a função de biblioteca fgets que só será estudada na próxima aula. Serve para ler uma linha de texto. A função fgets pertence à biblioteca stdio.

Já agora, para descodificar uma mensagem, inverte-se o esquema usado na codificação, andando para trás no alfabeto.



Construções condicionais

As construções condicionais permitem tomar decisões nos programas. Finalmente, chegou a altura de apresentar todas as construções condicionais da linguagem C e de mostrar como devem ser usadas.

Instrução if

Já conhecemos a instrução if, cuja forma geral é Quando esta instrução é executada, em primeiro lugar avalia-se a condição. Se a condição for verdadeira são executadas as instruções Ações1; caso contrário são executadas as instruções Ações2.

Note que a parte do else é opcional, pelo que uma instrução if se pode reduzir a:

Note também que que as chavetas são opcionais no caso de ações definidas por uma única instrução (nesse caso as chavetas não são precisas, mas também não faz mal deixá-las ficar).

A seguinte função usa um if para determinar o maior de dois números inteiros:

Cadeias else-if

A seguinte forma de usar a instrução if ocorre com alguma frequência. Permite testar sucessivamente uma sequência de condições, até se descobrir qual a primeira condição verdadeira.

A esta estrutura chama-se cadeira else-if:

A seguinte função usa uma pequena cadeia else-if e, produz o valor 1, -1 ou 0 consoante o argumento seja positivo, negativo ou igual a zero:

Instrução switch

A instrução switch permite tomar decisões com diversos ramos alternativos: primeiro a expressão Expressão é avaliada, e depois o seu valor é usado para escolher um dos ramos do switch.

Cada ramo dum switch é caraterizado por um valor fixo e por um corpo. Esse corpo deve terminar com uma instrução break.

Se o valor da expressão não estiver previsto em nenhum dos ramos case, então o ramo ativado é o ramo default.

Exemplo: A seguinte função usa um switch para determinar, numa string, três estatísticas: (1) qual o total de dígitos diferentes; (2) qual o total de espaços em branco; (3) qual o total dos carateres restantes. Exemplo de chamada da função anterior:

Expressão if (ou Expressão condicional)

Para além da instrução if, o C disponibiliza também uma expressão if, que tem a forma geral O valor desta expressão é Expressão1 ou Expressão2, consoante o valor de Condição seja verdadeiro ou falso.

A seguinte função usa uma expressão condicional para determinar qual o maior de dois números inteiros:



Exercício: Interpretador de comandos para a Codificação de César

O tema do projeto prático da nossa cadeira varia de ano para ano, mas existe um aspeto que raramente muda: a interação com o utilizador é feita usando um interpretador de comandos. Será assim este ano, tal como foi no ano passado.

Enunciado

Um interpretador de comandos é um programa que está constantemente a ler e processar comandos, introduzidos pelo utilizador.

Por favor, escreva um interpretador de comandos para a Codificação de César com 4 comandos diferentes:

Os três primeiros comandos têm 1 argumento; o último comando não tem argumentos. Eis um exemplo de cada comando: Considere que o deslocamento inicial, antes do utilizador ter tempo de o alterar usando o comando X, é zero.

Uma solução

A primeira parte deste programa já a conhecemos dum exemplo mais atrás. Mas agora, aqui está o programa completo (ou quase...). Neste programa, poderá causar estranheza o uso da variável "salta", usada para gerir o texto de entrada com muito pormenor.

Quando se leem carateres individuais, é preciso ter esse tipo de cautelas. Ao ler um inteiro ou um real, o scanf salta espaços em branco e mudanças de linha até encontrar o valor a ler. Ou seja, o scanf é muito flexível.

Mas quando o scanf lê um caráter individual, ele lê simplesmente o próximo caráter disponível e temos de ter a certeza de que não vamos ler um '\n' que ficou esquecido. Há situações em que temos de ter a certeza de que lemos as nossas linhas até ao final.



#70