Aula 3


Resumo da matéria

Programa

Um programa, como se viu, consiste na concretização prática dum algoritmo.  Um programa em C++ consiste de um conjunto de instruções que são executadas duma forma sequencial.  As instruções alteram o valor de variáveis, isto é, alteram o estado da memória associada ao programa, ou modificam o fluxo de controlo. Nesta secção vamos concentrar-nos em: (1) apresentar um conjunto de instruções básicas existentes em todos os programas, (2) aprender a definir variáveis e (3) verificar como se pode modificar o seu valor.

Tipicamente um programa tem a seguinte estrutura (todas as linha precedidas de // são comentários, sendo portanto ignorados pelo compilador, servindo "apenas" para documentar os programas) :

// As duas linhas seguintes são necessárias para permitir a 
// apresentação de variáveis no ecrã e a inserção de valores
// através do teclado (usando os canais [ou streams] cin e cout):

#include <iostream>
using namespace std;

// A linha seguinte indica o ponto onde o nosso programa vai
// começar a ser executado. Indica também que o programa não
// usa argumentos e que o valor por ele devolvido ao sistema
// operativo é um inteiro (estes assuntos serão vistos em
// pormenor mais tarde):

int main (void)
{   // esta chaveta assinala o início do nosso programa.

    // Aqui é onde vamos escrever o nosso programa.

}   // a última chaveta assinala o fim do nosso programa.

Variáveis

Memória

Os computadores têm memória.  É através da sua manipulação que os programas fazem algo de útil.  As linguagens de alto nível, como o C++, "escondem" a memória por trás do conceito de variável.  Assim, variáveis são pedaços de memória a que atribuímos um nome, e cujo conteúdo é interpretado de acordo com o seu tipo.

Importante:  Os tipos não passam duma abstração.  Tudo o que representamos na memória do computador corresponde a padrões de bits, os famosos "zeros e uns".  O tipo duma variável indica como um dado padrão de bits será interpretado.

Cada variável tem duas características: um nome e um tipo. Antes de usar uma variável é necessário indicar ao computador qual o seu nome e tipo. Uma instrução onde se pede ao compilador para criar uma variável com um dado nome e dum determinado tipo denomina-se definição. Uma definição tem o seguinte aspecto:

int a;  // esta definição indica ao computador que vamos usar uma variável 
        // chamada 'a' que só pode conter valores inteiros.  Portanto o
        // nome da nossa variável é 'a' e o seu tipo é int.
Quando uma variável é definida, o computador reserva para ela o número de bits necessário para guardar um valor do tipo referido.

Ao definir uma variável pode (e muitas vezes deve) atribuir-se-lhe um valor inicial.  A atribuição de um valor a uma variável que acabou de ser definida é chamada a inicialização. Esta operação pode ser feita de várias maneiras:

int a = 256;  // esta definição indica ao computador que vamos usar uma
              // variável 'a' do tipo int e que o seu valor inicial é 256.

int a(256);   // esta definição tem exactamente o mesmo efeito da anterior.
int a;        // estas duas linhas têm exactamente o mesmo efeito das
a = 256;      // anteriores.

Nomes de variáveis

Os nomes de variáveis podem ser constituídos por letras, dígitos, e também pelo caractere '_' (underscore), que se usa normalmente para tornar mais legível o nome da variável. O nome de uma variável não pode ter espaços, e não pode começar por um dígito.

Tipos básicos do C++

Nas tabelas seguintes são apresentados os tipos básicos existentes no C++ e a gama de valores que podem representar no Visual C++.

Importante: A gama de valores representada para cada tipo de dados pode variar com o processador e o sistema operativo!  Por exemplo, no sistema operativo Linux em processadores Alpha, os long int (segunda tabela) têm 64 bits.
 

Tipos básicos elementares
Tipo Descrição Gama Bits
bool Valor booleano true e false (0 e 1) 8
int Número inteiro -231 a 231-1 
(-2147483648 a 2147483647)
32
float Número decimal 
(com vírgula)
1.17549435e-38 a 
3.40282347e+38 (e negativos)
32
char Caractere 
(código ASCII)
Qualquer caractere. Exemplos: 
'a', 'A', '1', '!', '*', etc.
8
Pormenores:
  1. Os caracteres (char) são interpretados como inteiros em C++.  Assim, quer em Linux sobre processadores Intel quer no Windows NT, a gama dos códigos vai de -128 a 127, isto é, os char são interpretados como inteiros com sinal.
  2. Pode-se usar o tipo unsigned char para obter uma gama de 0 a 255.
 
Outros tipos básicos
Tipo Descrição Gama Bits
short [int] Número inteiro -215 a 215-1 (-32768 a 32767) 16
unsigned short [int] Número inteiro positivo 0 a 216-1 (0 a 65535) 16
unsigned [int] Número inteiro positivo 0 a 232-1 (0 a 4294967295) 32
long [int] Número inteiro a mesma que int 32
unsigned long [int] Número inteiro positivo a mesma que unsigned int 32
double Número decimal 
de precisão dupla
2.2250738585072014e-308 a 
1.7976931348623157e+308 
(e negativos)
64
long double Número decimal 
de precisão tripla
a mesma que a double 64
Nota:  Alguns dos tipos derivados do int podem ser escritos duma forma abreviada: os [] indicam a parte opcional na especificação do tipo.

Pormenor:  O qualificador signed também pode ser usado, mas signed int e int são sinónimos.  O único caso em que pode fazer diferença é na construção signed char em máquinas onde os char não tenham sinal.

Tipos aritméticos

Os tipos aritméticos são todos os tipos que permitem representar números (int, float e seus derivados). Sobre estes tipos podem ser efectuadas operações aritméticas e relacionais (ver próximas secções).  Os tipos derivados de int são tipos inteiros.  Os tipos derivados de float são tipos de vírgula flutuante.  A razão pela qual aos últimos se chama tipos de vírgula flutuante ficará clara na cadeira de AC, onde se discutirá em pormenor a representação de valores inteiros e de vírgula flutuante.

Nota: Os char, em rigor, também são tipos aritméticos e inteiros.

Caracteres

O tipo char é tratado de um modo especial. Em cada variável do tipo char é armazenado o código de um caractere.  Esse código consiste de um padrão de bits.  A cada padrão de bits corresponde um determinado caractere.  Cada padrão de bits pode também ser interpretado como um número inteiro em binário.  Assim, em C++, cada caracter é representado por um determinado valor inteiro: o seu código.

Existem várias tabelas de codificação de caracteres diferentes.  De longe a mais comum é a tabela dos códigos ASCII.  É também essa a tabela usada no Windows NT.  Daqui para a frente admitiremos que é essa a tabela em uso.

Pormenor:  Na realidade o Windows NT usa uma extensão do ASCII que permite usar caracteres acentuados.  Existem muitas dessas extensões, que podem ser usadas de acordo com o país em que se está.  Essas extensões são possíveis porque os códigos ASCII usam apenas os 7 bits menos significativos dum caracter.

Não é necessário, em C++, saber os códigos dos caracteres.  Assim, para nos referirmos ao código do caractere 'b' usamos 'b', que tem o mesmo significado que 98 (em ASCII).  O que de facto é armazenado numa variável do tipo char quando lhe é atribuido o valor 'b' é o número 98.  É possível tratar um char como um pequeno número inteiro.  Por exemplo, se fizermos o conjunto de instruções seguinte:

char caractere = 'a';

caractere = caractere + 1;
cout << "O caracter seguinte é: " << caractere;
o resultado é :
O caracter seguinte é: b
O que fizemos foi adicionar um (1) ao código do caracter 'a' de modo a que o código passe a ter o valor do código do caracter 'b'.

Importante: Isto é válido porque sabemos que, no código que estamos a usar (ASCII), o alfabeto possui códigos sequenciais.  (Experimente fazer o mesmo para o caractere 'z' ou para o caractere 'Z' e veja o resultado.)

Importante: Quando é feita a leitura de um caracter do teclado (através de cin) o que é lido é apenas um caractere, mesmo que no teclado seja inserido um código (ou mais do que um caractere), i.e., o resultado das seguintes operações:

char caractere;

cout << "Insira um caractere: "; 
cin >> caractere;
cout << "O caracter inserido é : " << caractere;
seria:
Insira um caractere: 48 
O caracter inserido é: 4
caso o utilizador inserisse 48.

O número oito foi ignorado visto que estamos a ler apenas um caractere, e não o seu código.

Experiência:  O programa seguinte imprime no seu ecrã todos os caracteres do código ASCII (que só especifica os caracteres correspondentes aos códigos de 0 a 127, isto é, todos os valores positivos dos char em Windows NT).

#include <iostream>
using namespace std;

int main(void)
{
    for(int i = 0; i < 128; i++) {
        cout << "'" << char(i) << "' (" << i << ")" << endl;
    }
}
Importante: Alguns dos caracteres escritos são especiais, representando mudanças de linha, etc.  Por isso, o resultado de uma impressão no ecrã de todos os caracteres do código ASCII pode ter alguns efeitos estranhos; não se preocupe com isso.

Valores literais

Além de variáveis, também necessitamos de usar valores literais.  Exemplos de valores literais :
'a'    // do tipo char, representa o código do caractere 'a'.
100    // do tipo int, valor 100 (em decimal).
100.0  // do tipo double (e não float).
100.0F // do tipo float (e não double).
1.1e230  // do tipo double, valor 1.1x10230.

Expressões

Em C++, o tipo de instrução mais simples consiste numa expressão terminada por ponto-e-vírgula.  Assim, as próximas são instruções válidas, se bem que inúteis:
; // expressão nula
2;
1 + 2 * 3;
Para que os programas tenham algum interesse, é necessário que "algo aconteça", i.e., que o estado da memória seja alterado.  Em C++ isso consegue-se alterando os valores das variáveis através do operador de atribuição.  Note-se que, ao contrário do que se passa noutras linguagens, a atribuição não é uma instrução, é uma operação.  As próximas instruções já parecem mais interessantes:
int i;
i = 2;
i = 1 + 2 * 3;
Uma expressão é composta por variáveis e operações.  Normalmente numa expressão existe um operador de atribuição (=, ou variantes), excepto quando a expressão for argumento de alguma função ou quando controlar alguma instrução de selecção ou iteração (a ver em aulas futuras).  Por exemplo:

a = b + 3;

Esta expressão significa: "atribua-se à variável a o resultado da soma do valor da variável b com o valor (literal) 3".

Outro exemplo:

int i, j;
...
bool i_é_igual_a_j;
i_é_igual_a_j = i == j;

Esta expressão significa: "atribua-se à variável i_igual_a_j o valor lógico (booleano) da comparação entre os valores (inteiros) das variáveis a e b".  Depois desta operação o valor de i_igual_a_j é verdadeiro se a for igual a b e falso caso contrário.

Operadores aritméticos

Os operadores aritméticos, que podem ser usados com operandos aritméticos, são os seguintes: +, -, *, / e % (adição, subracção, multiplicação, divisão e resto da divisão inteira [divisão de inteiros]).

Atenção:  O significado destes operadores pode depender do tipo dos operandos usados.  Por exemplo, ao usar operandos inteiros numa divisão, o resultado será inteiro, i.e., o resultado de 10/20 é 0 (zero), e não 0.5.  Do mesmo modo, não faz sentido usar o operador % com operandos de vírgula flutuante.

Pormenor:  Os operador de divisão e resto da divisão inteira estão especificados de tal forma que, se a e b forem valores inteiros, então a é igual a (a/b)*b + a%b.

Operadores relacionais e de comparação

Os operadores relacionais que podem ser usados em C++ são: >, <, >= e <=, com os significados óbvios.  Para comparar dois operandos usam-se os operadores == (igualdade) e != (desigualdade).

Operadores lógicos

Os operadores lógicos que podem ser usados em C++ são: ! (negação ou NÃO), && (conjunção ou E), || (disjunção ou OU).

Operadores de atribuição

A operação de atribuição, que é indicada pelo sinal =, faz com que a variável que está à esquerda do sinal tome o valor da expressão que se encontra à direita do sinal. Exemplo:
a = 3 + 5; // a toma o valor 8
Importante:  Repare bem na diferença entre = (atribuição) e == (comparação, igualdade).

Nota:  Existem muitos outros operadores de atribuição em C++, que normalmente são formas abreviadas de escrever expressões muito comuns.  Assim, i++ tem o mesmo significado que i += 1, e quase o mesmo significado que i = i + 1.

Canais: leitura do teclado e escrita no ecrã

Para escrever no ecrã e ler valores do teclado usam-se aquilo a que se chama canais de entrada e saída (streams).  O canal de saída para o ecrã é designado por cout.  O canal de entrada de dados pelo teclado é cin.  Para efectuar uma operação de entrada de dados é usado o operador >>.  Uma instrução de escrita de dados no ecrã tem o seguinte aspecto:
int a = 10;
cout << "Vou escrever um número a seguir a esta frase: " << a;
A entrada de dados do teclado tem o seguinte aspecto:
cin >> a;
Após uma operação de entrada de dados, a variável toma o valor que foi por nós inserido no teclado, desde que este valor possa ser tomado por essa variável.  Mais tarde veremos como verificar se o valor introduzido estava correcto.

Importante:  Ao ser executada uma operação de leitura como acima, o computador interrompe a execução do programa até que seja introduzido algum valor no teclado.


Exercício resolvido

// Este programa lê dois números inteiros do teclado,
// multiplica-os, e escreve o resultado no ecrã.
#include <iostream>
using namespace std;
int main ()
{
    int primeiro_número;
    int segundo_número;
    cout << "Introduza dois números inteiros: " << endl;
    cin >> primeiro_número >> segundo_número;
    int resultado = primeiro_número * segundo_número;
    cout << "O resultado da multiplicação dos dois numeros é "
         << resultado << endl; 
}
Notas:
  1. A utilização de caracteres acentuados é válida, de acordo com a norma do C++.  Mas o Visual C++ não os aceita senão em comentários e entre "".
  2. Podem haver algumas surpresas com os caracteres acentuados colocados entre "" quando eles são escritos no ecrã (em Windows NT + Visual C++).
  3. Logo, a sua utilização pode não ser recomendável.


Exercícios

1.  Passe para o editor o programa que se encontra acima (exercício resolvido) e corra o programa.

2.  Desenvolva um pequeno programa que escreva no ecrã a frase "Olá mundo!".

3.a)  Escreva um programa que peça ao utilizador para inserir dois números inteiros e os some.  Depois de terminar o programa, o seu ecrã deve ter o seguinte aspecto:

Insira dois números :
12
234
A soma dos dois números inseridos é: 256
3.b)  Altere o seu programa de modo a que possa inserir números decimais (com vírgula).

3.c)  Altere o seu programa para fazer a leitura de cinco números e somá-los.

3.d)  Se usou mais do que duas variáveis para fazer a alínea anterior, re-escreva o seu programa usando apenas duas variáveis. Uma delas deve guardar o valor lido, enquanto a outra deve guardar o valor acumulado da soma dos números inseridos até ao momento.  Faça o traçado deste programa vizualizando a evolução de ambas as variáveis.

4.a)  Escreva um programa que, dado um caracter, escreva no ecrã o seu número de ordem no alfabeto (assuma código ASCII).  Exemplo:

Por favor introduza um caracter:
a
A ordem do caracter 'a' é: 1
Outro exemplo :
Por favor introduza um caracter:
c
A ordem do caracter 'c' é: 3
4.b)  Escreva um programa que, dado um caracter, escreva no ecrã o caracter seguinte no alfabeto (assuma código ASCII).

5.a)  Use o seu programa de soma (a versão feita para inteiros, na alínea 3.a)) para tentar somar os números 2147483647 e 2.  Faça um traçado do programa e tente perceber porque não funciona.

5.b)  Experimente "corrigir" o programa mencionado na alínea anterior usando variáveis do tipo float.  Funcionou?  Porquê?


Exercícios propostos

1.  Corrija o seguinte programa:
#include <iostream>
using namespace std
int main(void)
{
  inteiro p1 p2 divisao,
  cout >> "Introduza dois numeros: << endl;
  cin >> primeiro_numero >> segundo_numero;
  primeiro_numero \ segundo_numero = divisao;
  cout << O Resultado e >> divisao ; 
}
2.a)  Faça aparecer no ecrã a seguinte figura:
****** 
**  **
**  **
******
2.b)  Pense como faria para uma caixa com medidas 50 x 70.  Volte a responder a este exercício após ter sido dado o conceito de "ciclo" (ou então aventure-se).