Buscar

De Objective Caml para C e C++

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes
Você viu 3, do total de 57 páginas

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes
Você viu 6, do total de 57 páginas

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes
Você viu 9, do total de 57 páginas

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Prévia do material em texto

PDF gerado usando o pacote de ferramentas em código aberto mwlib. Veja http://code.pediapress.com/ para mais informações.
PDF generated at: Fri, 06 Dec 2013 10:28:06 UTC
De Objective Caml para C e
C++
Conteúdo
Páginas
Introdução 1
Os tipos básicos 5
Variáveis e funções 14
Instruções 17
Construção de tipos 33
Introdução à programação orientada a objetos 50
Referências
Fontes e Editores da Página 53
Fontes, Licenças e Editores da Imagem 54
Licenças das páginas
Licença 55
Introdução 1
Introdução
A linguagem C é uma linguagem bastante simples, criada na década de 70 e pouco atualizada desde então. A
linguagem C provê uma camada de abstração em cima do nível de linguagem de assemblagem. Essa camada é
composta por tipos básicos (basicamente tipos numéricos), construtores de tipos simples, construções algorítmicas
clássicas, e modularização funcional. Os principais pontos fortes da linguagem C são uma ampla difusão, a rapidez
de execução dos programas compilados, e a possibilidade de interação com recursos de hardware. A principal
desvantagem é que não possui conceitos de linguagens de programação mais avançados, o que pode penalizar a
produtividade dos programadores naquela linguagem. A facilidade de acesso aos recursos de hardware, como a
memória principal, é uma faca de dois gumes: permite programas muito rápidos, e falhas extremamente súteis (do
tipo que se manifesta só em um determinado ambiente, em condições de uso muito específicas e que podem levar
semanas a serem identificadas e corrigidas).
A linguagem C++ é uma linguagem derivada de C, e inclui novos paradigmas de programação como a orientação a
objetos, tipos referências, polimorfismo paramétrico. C++ oferece muito mais recursos que C e é uma linguagem
significativamente mais complexa e difícil de se dominar. Uma propriedade interessante é a sua compatibilidade
quase total com a linguagem C, fazendo com que código C possa ser compilado por um compilador C++. Essa
compatibilidade se estende até o código objeto: código C++ pode fazer uso de código desenvolvido em C. Embora a
linguagem C++ seja bastante complexa, felizmente, não é necessário entender e dominar todos os recursos providos
por essa linguagem para poder utilizá-la nos seus projetos de programação.
As linguagens C e C++ possuem ambas uma biblioteca padrão que provê algumas extensões práticas como a
possibilidade de realizar entradas e saídas, de interagir com o sistema de arquivos. A biblioteca padrão de C oferece
alguns poucos recursos de algoritmos e estruturas de dados. Em compensação a linguagem C++ possui uma
biblioteca bem mais rica que
Diferente de Objective Caml, que pode-se interpretada ou compilada para bytecode e linguagem nativa, as
linguagens C e C++ podem apenas ser compiladas. As diferentes atividades que compõem o processo de trabalho
com essas linguagens são:
•• Edição: A grande maioria dos editores de texto possuem destaques de sintaxe para as linguagens C e C++. As
duas principais são vi (e seus descendentes vim e gvim) e emacs (ou seu primo xemacs). Os editores de texto
geralmente são configurados para oferecer destaque de sintaxe para a linguagem C quando o arquivo editado
possui o prefixo .c e .h, e para a linguagem C++, quando o nome do arquivo é prefixado com ".C", ".cc", ".cpp" e
".hh".
•• Compilação: Existem diversos compiladores para essas linguagens. Os compiladores gcc e g++, além de serem
gratuitos e de código aberto, podem ser utilizados em qualquer plataforma (Linux, Free BSD, Windows, MacOS).
•• Execução: O código é compilado em linguagem nativa da plataforma e só pode ser executado naquela plataforma.
Um primeiro programa em C++
Esse primeiro exemplo vai ilustrar passo a passo as diferentes fases de criação de um programa. Iremos desenvolver
em C++ um programa que imprime uma linha na saída padrão com o texto "ola." e que retorna o valor 0. Em
Objective Caml, poderíamos programar isso com o seguinte código:
let _ =
 Printf.printf "ola.\n";
 0
O código seguinte é equivalente ao anterior, e fica mais próximo da versão C++:
Introdução 2
let main () =
 Printf.printf "ola.\n";
 0
let _ =
 main()
Agora vamos para o C++... Você vai reparar que é um pouco mais complexo, mas fundamentalmente não é muito
diferente. Para começar, devemos 'criar um programa'. Abra então um editor de texto e cria um novo documento.
 Inclui o seguinte texto no documento sendo editado:
#include <iostream>
int main ()
{
 std::cout << "ola.\n"
 return 0
}
Guarda o conteúdo do documento em um arquivo, por exemplo chamado exemplo1.cpp. Temos então nosso
primeiro programa em C++! O que faz? Discutiremos isso depois, pois vamos partir logo para a segunda fase, que é
a de 'compilação'.
Para isso, devemos utilizar o compilador C++. Podemos fazer isso através de um console de comandos. Abra então
um console de comandos e digite o seguinte comando para compilar o arquivo:
g++ -ansi -Wall exemplo1.cpp
Esse comando é composto pelo nome do compilador (g++), duas opções (-ansi e -Wall) e o nome do arquivo a
ser compilado. A opção -ansi instrui o compilador a verificar que você está seguindo as recomendações do
padrão ANSI da linguagem C++. Isso garante a portabilidade de seu código. A opção -Wall é ainda mais
importante, pois ela instrui o compilador a reportar avisos sobre erros possíveis no seu programa. A experiência
prova que 99% dos erros possíveis são erros de fatos. Vale então a pena utilizar esse recurso para descobrir (pelo
menos parte dos) erros que inevitavelmente são cometidos pelos programadores: 'errare humanum est'.
Deve aparecer a seguinte mensagem no console:
exemplo1.cpp: In function 'int main()':
exemplo1.cpp:6: error: expected `;' before '}' token
O compilador indica que na função int main(), há um erro, localizado na linha 6 (pode ser que você tenha
introduzido linhas adicionais no seu editor, neste caso o número da linha onde ocorre esse erro poderá ser diferente).
O que aconteceu? Bom, a mensagem de erro é bastante clara: o compilador estava esperando o caractere ; antes do
}. E o compilador está certíssimo, esquecemos de colocar um ponto e vírgula no final da linha 5 e quando encontrou
} o compilador reparou essa falta! Corrija então esse erro no seu editor, salve novamente no arquivo e repete o
comando de compilação.
Agora não há mais erro detectado pelo compilador. Vamos então executar o nosso programa... Mas onde está ele?
Lista o conteúdo do diretório atual (o comando ls permite fazer isto) e observará a presença de um programa
chamado a.out. Ao menos que seja instruído de outra forma, o compilador gera um programa em um arquivo com
esse nome esquisito. Para gerar o programa com um outro nome, deve se utilizar uma opção específica do
compilador, que é -o seguido do nome desejado. Por exemplo, se desejarmos chamar esse programa de
exemplo1, o comando seria:
Introdução 3
g++ -ansi -Wall exemplo1.cpp -o exemplo1
Para executar o programa, basta digitar no console o caminho até o arquivo que contem esse programa, por exemplo:
./exemplo1
Aparece então a seguinte mensagem:
ola.
Ufa!... Escrevemos o nosso primeiro programa em C++, e vimos como compilá-lo. Aprendemos três opções muito
úteis do compilador g++, e verificamos que o compilador tem um papel importante para apoiar o desenvolvimento
de programas corretos através da emissão de mensagens de erro e de avisos.
Explicações complementares
Para terminar essa introdução, precisamos ainda explicar como esse programa funciona. No processo começaremos a
aprender algumas coisas da linguagem C++. Então, o que faz esse programa? Vamos incluí-lo novamente, dessa vez
identificando as linhas com números:
1 #include <iostream>
2 int main ()
3 {
4 std::cout << "ola.\n";
5 return 0;
6 }
Nós não vamos entrar muito em detalhes aqui. A linha 1 faz referência a um arquivo chamado iostream,o qual
faz parte da biblioteca padrão do C++. Este arquivo contém declarações de tipos e funções relacionadas com fluxos
de entrada e saída (iostream abrevia input/output stream), ou seja com leitura e escrita de textos.
Em C e em C++, todas as linhas iniciando com o caractere # ('jogo da velha') são 'diretivas de pré-processamento'.
Nessas linguagens, a compilação começa com uma fase dita de pré-processamento que realiza diversas operações de
manipulação do código fonte como a inclusão de arquivos. Por exemplo, a diretiva #include resulte na inclusão
do conteúdo de um arquivo.
As linhas 2 a 6 contém a definição de uma função, cujo nome é main (informações importantes sobre esse nome
são dadas no final desta seção). Antes do nome da função vem o tipo do resultado da mesma: aqui é o tipo int que
corresponde a números inteiros. Depois do nome da função, vem uma lista de parâmetros entre parênteses. Aqui a
lista é vazia e não há parâmetros. main portanto é uma função que não tem argumentos, e que retorna um valor do
tipo int. Em Caml esse tipo é denotado unit->int.
Depois da lista de parâmetros, vem o corpo da função, colocado entre chaves. Neste exemplo, o corpo dessa função é
uma seqüência composta de dois comandos (ou instruções).
O primeiro comando (linha 4) aplica o operador de impressão C++, que se escreve << (menor menor). Esse
operador é infixo e tem dois argumentos: o primeiro, a esquerda, é um fluxo de saída, no caso é std::cout, a
saída padrão, o segundo argumento é o valor que será impresso, no caso é o texto "ola.\n". O resultado da
aplicação deste operador é o próprio fluxo de saída. Assim, ele pode ser encadeado, como no exemplo seguinte:
std::cout << "Oi\n" << "Tudo bem?";
O segundo comando (linha 5) é uma instrução pré-definida da linguagem C++ que, tem um argumento, e instrui que
a função deve terminar a sua execução e retornar o valor dado em argumento, no caso o valor 0.
Introdução 4
Agora que explicamos cada linha do exemplo, devemos voltar ao nome da função: main. As linguagens C e C++
têm como convenção que a execução de um programa sempre inicia com a chamada da função chamada main. O
valor que retorna a função main é comunicado ao sistema operacional e pode ser usado para informar da ocorrência
ou não de algum problema. Por convenção, o valor 0 indica uma execução bem-sucedida.
Portanto, quando irá desenvolver um programa nessas linguagens, sempre deverá definir uma função com esse
nome, que será o ponto inicial da execução desse programa.
Um primeiro programa em C
O programa C seguinte é equivalente ao programa C++ dado em exemplo no parágrafo anterior.
#include <stdio.h>
int main ()
{
 fprintf(stdout, "ola.\n");
 return 0;
}
Para experimentar a programação em C, abre um editor de texto, cópia o código dado e grave o mesmo em um
arquivo chamado exemplo2.c. Para compilar esse arquivo e gerar um programa executável nomeado exemplo2,
entre com o seguinte comando no console:
gcc -ansi -Wall exemplo2.c -o exemplo2
Então quais são as diferenças com o programa C++? Basicamente, são reduzidas ao uso de funções diferentes para
realizar a impressão da mensagem ola. na saída padrão. Em C, utiliza-se uma função nomeada printf que é
disponibilizada na biblioteca padrão através do arquivo stdio.h. Observe a similaridade com o comando
Printf.printf da linguagem Objective Caml: o primeiro argumento é o fluxo de saída (stdout designa a
saída padrão em C), e o segundo argumento é um texto a ser impresso. Em Objective Caml, como em C, esse texto
pode ter diretivas de formatação de valores que são então passados como argumentos adicionais à função fprintf.
Lembre-se...
•• C e C++ não possuem interpretadores: Código nessas linguagens deve ser compilado para poder ser executado.
• Um compilador para C é o programa gcc, para C++ pode usar o g++.
• Um programa C ou C++ deve possuir uma função chamada main. Um programa sempre começa a se executar
pela função main.
Os tipos básicos 5
Os tipos básicos
Introdução
O sistema de tipos de uma linguagem de programação é composta por:
•• Tipos básicos, que são os elementos de base para representar informações mais simples e construir tipos mais
complexos
• Construtores de tipos, que tem como papel combinar tipos mais elementares para construir tipos mais complexos;
•• Regras de conversão entre tipos, que definem se e como valores de um tipo podem ser convertidos para um outro
tipo.
O sistema de tipos também possui os construtores de tipos, que são apresentados em um módulo específico sobre .
Ainda possui regras que definem quando, e como valores de um tipo podem ser convertidos entre se.
Os tipos básicos de C e de C++ são apresentados nesse módulo. Primeiro lembramos os tipos básicos de Objective
Caml são unit, bool, char, int, float e string. Os tipos básicos de C++ são:
• void: o tipo vazio,
• bool: o tipo booleano,
• int: o tipo inteiro,
• char: o tipo dos caracteres,
• float e double são tipos para os números decimais.
A seguinte tabela provê uma correspondência entre os tipos básicos de C e C++ e os de Objective Caml.
Objective Caml C e C++
- void
unit -
char char
int short int, long int, long long int
float float, double, long double
string - (*)
(*) As linguagens C e C++ possuem construções para representar textos (string), mas essas não fazem parte conjunto
dos tipos básicos. A biblioteca padrão da linguagem C fornece funções de tratamento de textos utilizando uma
técnica baseada em ponteiros para caracteres. Essas funções são disponíveis na linguagem C++ que ainda fornece,
através de sua biblioteca padrão, um tipo nomeado string e funções que manipulam valores desse tipo.
O tipo vazio
Em Objective Caml, o tipo unit é o tipo das computações seqüênciais e possui apenas um valor, denotado (). Em
C e C++, existe um tipo similar, embora mais simples ainda! Trata-se do tipo void, que não possui valor algum. Ele
é usado para definir funções que não retornam resultado.
Booleanos
A linguagem C++ possui um tipo para representar os booleanos. Tem como nome bool e os valores true e
false, representando respectivamente verdadeiro e falso.
Os operadores booleanos são:
• negação: !,
• conjunção: &&,
Os tipos básicos 6
• disjunção: ||.
É importante notar que os operadores booleanos binários possuem uma ordem pré-definida para a avaliação de seus
operandos.
• A avaliação da expressão c1 && c2 começa pela avaliação da sub-expressão c1. Se a mesma for igual ao valor
false, a avaliação da expressão completa é terminada e resulta no valor false. Caso contrário, a segunda
sub-expressão c2, é avaliada e o resultado sera o da expressão completa.
• Similarmente, na avaliação de uma disjunção c1 || c2, caso a avaliação da sub-expressão resulte no valor
true, então é concluída a avaliação da expressão completa, resultando no valor true. Caso contrário, o valor da
expressão completa é o da sub-expressão c2.
Existe um quarto operador, que pode ser chamado operador condicional. É similar à construção
if...then...else de Objective Caml. A sintaxe é ... ? ... : ..., onde cada ... representa um
argumento. Possui três argumentos, sendo que o primeiro é uma condição e os dois últimos devem ter tipos
compatíveis. O valor da expressão é o valor do segundo argumento se a condição for verdadeira, do terceiro
argumento caso contrário. Considere, como exemplo, a seguinte expressão:
 i >= 0 ? 1 : -1
O valor dessa expressão será caso o valor de i for maior ou igual a zero, e caso contrário (>= é o operador
maior ou igual).
No caso do operador condicional, o segundo argumento é avaliado apenas se o resultado da avaliação do primeiro for
true, enquanto que o terceiro argumento será avaliado apenas quando o resultado da avaliação do primeiro for
false.
Booleanos na linguagem C
Na versão inicial da linguagem C, não existia tipo para os booleanos. Em C, as condições possuem o tipo inteiro,onde a condição falso é representada pelo valor 0, e a condição verdadeiro por qualquer valor não nulo. A biblioteca
padrão da linguagem C, versão 1999, existe uma definição para o tipo booleano que é idêntica à da linguagem C++.
Para ter acesso a essa definição, o programador deve incluir um arquivo utilizando o comando seguinte no início do
arquivo:
#include <stdbool.h>
Impressão de valores booleanos
Para imprimir um valor do tipo booleano, pode-se utilizar a função printf e o operador <<. Em impressão no
estilo C, temos então:
// exemplo de programa C++ que imprime os dois valores do tipo bool, utilizando uma função de impressão herdada de C
#include <cstdio>
int main ()
{
 fprintf(stdout, "verdadeiro: %i\n", true);
 fprintf(stdout, "falso: %i\n", false);
}
Um programa de impressão de valores booleanos no estilo C++ seria:
// exemplo de programa C++ que imprime os dois valores do tipo bool, utilizando o operador de impressão do C++
#include <iostream>
using namespace std;
int main ()
Os tipos básicos 7
{
 cout << "verdadeiro: " << true << endl;
 cout << "falso: " << false << endl;
}
A execução de ambos programas resultará na seguinte impressão na saída padrão:
verdadeiro: 1
falso: 0
Caracteres
As linguagens C e C++ possuem ambas um mesmo tipo para representar caracteres: trata-se do tipo denominado
char. Os valores desse tipo podem ser denotados colocando o caractere entre aspas simples. Nesse aspecto
Objective Caml é muito similar, pois adota exatamente as mesmas convenções.
A linha seguinte possui alguns valores do tipo char:
'a' 'A' '0' '\n' '\\' '\042'
Esses valores representam, respectivamente, a letra a minúsculo, a letra a maiúsculo, o algarismo zero, o caractere
especial de quebra de linha (caracteres especiais são precedidos de uma contra-barra), o caractere de contra-barra, e o
caractere cujo código ASCII é 42 (o caractere de aspa dupla).
Na verdade, as linguagens C e C++ tratam o tipo char como um tipo inteiro, onde oito bits são representados para
representar os valores. São portanto 256 valores diferentes. Existem duas variações:
• signed char, correspondendo aos números de -128 até 127;
• unsigned char, correspondendo aos números de 0 até 255.
Isso significar que 'a' + 1 é uma expressão legal, tanto em C quanto em C++, e ela é igual a 'b'.
Impressão de caracteres
Para imprimir um valor do tipo char, pode-se utilizar a função printf e o operador <<. Em impressão no estilo C,
temos então:
// exemplo de programa C++ que imprime um valor do tipo char, utilizando uma função de impressão herdada de C
#include <cstdio>
int main ()
{
 fprintf(stdout, "%i - %c\n", 'a', 'a');
}
Note que o texto de impressão possui duas diretivas de formatação. A primeira é %i: manda imprimir o (primeiro)
valor 'a' como se fosse um valor do tipo int. A segunda é %c e manda imprimir o (segundo) valor 'a' como
se fosse um valor do tipo char. A saída resultante é:
97: a
No parágrafo anterior, observe nosso grifo em como se fosse. O que acontece efetivamente é o seguinte. A diretiva
%i indica que o argumento correspondente é um valor do tipo int. No caso, o valor é do tipo char. O que acontece
aqui é que o compilador insere uma conversão do valor do tipo char para o tipo int. Em C, ou em C++, essa
conversão é realizada automaticamente, e o compilador nem emite um aviso que foi feita essa conversão. Isto é um
exemplo bastante esclarecedor da diferença de rigor entre o sistema de tipo de C e C++ e o de Objective Caml, onde
seria exigido a aplicação de uma função para converter os valores desses tipos.
Os tipos básicos 8
Um programa de impressão de valores booleanos no estilo C++ seria:
// exemplo de programa C++ que imprime os dois valores do tipo bool, utilizando o operador de impressão do C++
#include <iostream>
using namespace std;
int main ()
{
 cout << 'a' << endl;
}
A execução desse programa resultará na seguinte impressão na saída padrão:
a
Números inteiros
As linguagens C e C++ possuem uma variedade de tipos inteiros. Esses tipos são pré-definidos e variam em função
de dois aspectos:
•• presença ou não de sinal;
•• tamanho da representação binária, que vai de um até oito bytes.
Vamos olhar para o exemplo mais simples, que é o tipo char, já discutido na seção sobre caracteres. Um char é um
tipo inteiro codificado com um byte, ou seja oito bits. Possui portanto valores diferentes. São duas
variantes para esse tipo: com sinal ou sem sinal. Na variante com sinal, denominada signed char, ou
simplesmente char os valores vão de -128 até 127. Na variante sem sinal, denominada unsigned charos
valores ficam na faixa de 0 até 255. Você pode verificar quem, em ambos casos, temos 256 valores diferentes.
Existem quatro tamanhos possíveis que são um, dois, quatro e oito bytes. Acabamos de (re)ver o caso do tamanho de
um byte. Os demais três tamanhos são nomeados short int, long int e long long int. Também são
chamados de short, long e long long. Todos possuem variantes com sinal e sem sinal que são acessíveis
utilizando os prefixos signed e unsigned, sendo que a ausência de tal prefixo corresponde ao tipo com sinal.
Vale salientar que existe um tipo int que corresponde a short int em plataformas computacionais com
processador de 16 bits (ou seja 2 bytes) e a long int em plataformas computacionais com um processador de 32
bits (ou seja 4 bytes).
É importante salientar que, como em Objective Caml, a aritmética implementada é a aritmética do relógio, ou
aritmética módulo. Quando uma computação resulte em um valor ultrapassa os limites do tipo, então que não pode
ser representado, o resultado da computação será igual a esse valor módulo o tamanho do tipo do resultado dessa
computação.
Constantes inteiros
Os valores máximos e mínimos de cada um dos tipos possuem nomes. A tabela seguinte faz um resumo dos mesmos.
Os tipos básicos 9
SCHAR_MIN -127 valor mínimo para um objeto do tipo signed char
SCHAR_MAX +127 valor máximo para um objeto do tipo signed char
UCHAR_MAX +255 valor máximo para um objeto do tipo unsigned char
SHRT_MIN -32767 valor mínimo para um objeto do tipo signed short
SHRT_MAX +32767 valor máximo para um objeto do tipo signed short
USHRT_MAX +65535 valor máximo para um objeto do tipo unsigned short
INT_MIN -32767 valor mínimo para um objeto do tipo int
INT_MAX +32767 valor máximo para um objeto do tipo int
UINT_MAX +65535 valor máximo para um objeto do tipo unsigned int
LONG_MIN -2147483647 valor mínimo para um objeto do tipo long int
LONG_MAX +2147483647 valor máximo para um objeto do tipo long int
ULONG_MAX +4294967295 valor máximo para um objeto do tipo unsigned long int
LLONG_MIN -9223372036854775807 valor mínimo para um objeto do tipo long long int
LLONG_MAX +9223372036854775807 valor máximo para um objeto do tipo long long int
ULONG_MAX +18446744073709551615 valor máximo para um objeto do tipo unsigned long long int
Em C, acesso a esses nomes é realizado colocando a seguinte diretiva no cabeçalho do arquivo:
#include <limits.h>
Em C++, o acesso é realizado com a diretiva seguinte de inclusão:
#include <climit>
Existem outras definições pertinentes agrupadas no arquivo stdint.h, mas que não detalharemos aqui.
Operadores inteiros
Os valores de todos os tipos inteiros podem ser combinados através de operadores aritméticos como a adição,
subtração, etc. São eles:
• adição: +,
• subtração: -,
• multiplicação: *,
• divisão inteira: /,
• resto da divisão inteira: %,
• negação: -.
No caso dos operadores de divisão, se o segundo operando for nulo, o resultado é indefinido. Esse conceito de
resultado indefinido não existe em Objective Caml. Isso quer dizer que qualquer coisa pode acontecer... Para se ter
uma idéia da gravidade disso, imagine então que o disco rígido seja reformatado cada vez que é realizada uma
divisão por zero.Soa absurdo para você? Bem, esse comportamento é legal tanto em C quanto em C++! Em
conclusão, é a tarefa do programador evitar que os operadores / e % sejam aplicados com o seu segundo argumento
igual a zero. Diferente de Objective Caml, o sistema de exceções da linguagem não vai tratar isso.
Também existem operadores de comparação que podem ser aplicados aos tipos inteiros:
• igualdade: ==;
• diferença: !=;
• menor que: <;
Os tipos básicos 10
• menor ou igual a: <=;
• maior que: >;
• maior ou igual a: >=.
Finalmente, existem operadores que não possuem equivalentes em Objective Caml e que permitem manipular
valores em nível de bits. São eles:
• deslocamento a esquerda: <<;
• deslocamento a direita: >>;
• conjunção bit a bit: &;
• disjunção bit a bit: |;
• disjunção exclusiva bit a bit: ^;
• negação bit a bit: ~.
Para entender o funcionamento desses operadores, devemos colocar-mo-nós ao nível dos bits. Considere uma
variável c do tipo unsigned char. c é representada por um byte, ou seja oito bits. Supondo que o valor de c
seja , os oito bits serão então . O sentido de cada um dos operador será apenas explicado através de
exemplos, agrupados na seguinte tabela:
c >> 2 23 >> 2 00010111 >> 2 00000101 5
c << 1 23 >> 1 00010111 << 1 00101110 46
c & 24 23 & 24 00010111 & 00011000 00010000 16
c ^ 24 24 ^ 24 00010111 >> 0001100 00001011 11
~c ~23 ~00010111 11101000 232
Exercícios
• Utilizando operadores de combinação bit a bit, escreva uma função que dada dois inteiros e retorna
.
Impressão de valores inteiros
A impressão de um valor inteiro utilizando a função fprintf é realizada através de uma diretiva de formatação
que pode ter como componentes uma diretiva de tamanho e uma diretiva de sinal. A diretiva de tamanho é hh para
char, h para short, omitida para int, l para long e ll para long long. A diretiva de sinal é i para
tipos com sinal e u para tipos sem sinal. Assim o programa seguinte permite imprimir valores constantes inteiros
disponibilizados no arquivo climits:
#include <climits>
#include <cstdio>
int main ()
{
 fprintf(stdout, "SCHAR_MIN = %hhi\n", SCHAR_MIN);
 fprintf(stdout, "SCHAR_MAX = %hhi\n", SCHAR_MAX);
 fprintf(stdout, "UCHAR_MAX = %hhu\n", UCHAR_MAX);
 fprintf(stdout, "SHRT_MIN = %hi\n", SHRT_MIN);
 fprintf(stdout, "SHRT_MAX = %hi\n", SHRT_MAX);
 fprintf(stdout, "USHRT_MAX = %hu\n", USHRT_MAX);
 fprintf(stdout, "INT_MIN = %i\n", INT_MIN);
 fprintf(stdout, "INT_MAX = %i\n", INT_MAX);
 fprintf(stdout, "UINT_MAX = %u\n", UINT_MAX);
Os tipos básicos 11
 fprintf(stdout, "LONG_MIN = %li\n", LONG_MIN);
 fprintf(stdout, "LONG_MAX = %li\n", LONG_MAX);
 fprintf(stdout, "ULONG_MAX = %lu\n", ULONG_MAX);
 fprintf(stdout, "LLONG_MIN = %lli\n", LLONG_MIN);
 fprintf(stdout, "LLONG_MAX = %lli\n", LLONG_MAX);
 fprintf(stdout, "ULLONG_MAX = %llu\n", ULLONG_MAX);
}
A execução desse programa resulte na impressão seguinte na saída padrão:
SCHAR_MIN = -128
SCHAR_MAX = 127
UCHAR_MAX = 255
SHRT_MIN = -32768
SHRT_MAX = 32767
USHRT_MAX = 65535
INT_MIN = -2147483648
INT_MAX = 2147483647
UINT_MAX = 4294967295
LONG_MIN = -2147483648
LONG_MAX = 2147483647
ULONG_MAX = 4294967295
LLONG_MIN = -9223372036854775808
LLONG_MAX = 9223372036854775807
ULLONG_MAX = 18446744073709551615
Graças à sobrecarga do operador de impressão, C++ não precisa das diretivas de formatação: ele adapta-se
automaticamente em função do tipo do valor a ser impresso. Assim, a mesma saída pode ser obtida com o seguinte
programa:
#include <climits>
#include <iostream>
using namespace std;
int main ()
{
 cout << "SCHAR_MIN = " << SCHAR_MIN << endl
 << "SCHAR_MAX = " << SCHAR_MAX << endl
 << "UCHAR_MAX = " << UCHAR_MAX << endl
 << "SHRT_MIN = " << SHRT_MIN << endl
 << "SHRT_MAX = " << SHRT_MAX << endl
 << "USHRT_MAX = " << USHRT_MAX << endl
 << "INT_MIN = " << INT_MIN << endl
 << "INT_MAX = " << INT_MAX << endl
 << "UINT_MAX = " << UINT_MAX << endl
 << "LONG_MIN = " << LONG_MIN << endl
 << "LONG_MAX = " << LONG_MAX << endl
 << "ULONG_MAX = " << ULONG_MAX << endl
 << "LLONG_MIN = " << LLONG_MIN << endl
 << "LLONG_MAX = " << LLONG_MAX << endl
Os tipos básicos 12
 << "ULLONG_MAX = " << ULLONG_MAX << endl;
}
Números decimais
As linguagens C e C++ possuem três tipos reais, chamados tipos flutuantes reais, para representar números decimais.
São eles: float, double e long double, em ordem crescente de tamanho e precisão.
Constantes decimais
As constantes dos tipos flutuantes podem ser escritos em base 10 e 16 (utilizando o prefixo 0x). Uma constante
flutuante possui até três partes:
• O significante é uma seqüência de dígitos, com possívelmente um ponto para separar a parte inteira da parte
decimal.
• O expoente é iniciado com o caractere e ou Eseguido de uma seqüência de dígitos para os números escritos em
base 10. Para os números flutuantes escritos em base 16, o expoente inicia com o caractere p ou P e o expoente
é aplicado ao número 2 ao invés de 10. Se o significante contem um ponto, então o expoente é opcional.
• O sufixo flutuante que indica qual é o tipo da constante. Quando é ausente, o tipo é double. Pode ser f ou F
para indicar que é do tipo float e pode ser l ou L para indicar que é do tipo long double.
Não podemos esquecer de assinalar que tanto o significante como o expoente podem iniciar opcionalmente com um
sinal positivo (caractere +) ou negativo (caractere -).
Seguem alguns exemplos de como escrever o número :
3.14 /* tipo: double */
.314e1/* tipo: double */
314e-2 /* tipo: double */
314e-2f /* tipo: float */
314e-2L /* tipo: long double */
Operadores decimais
Os valores de todos os tipos flutuantes podem ser combinados através de operadores aritméticos como a adição,
subtração, etc. São eles:
•• adição: +,
•• subtração: -,
•• multiplicação: *,
•• divisão: /,
•• negação: -.
No caso dos operadores de divisão, se o segundo operando for nulo, o resultado é indefinido. Repare que, diferente
de Objective Caml, que provê operadores diferentes para os tipos int e float, tanto em C e C++, os operadores
aritméticos são os mesmos tanto para os tipos inteiros quanto para os tipos flutuantes.
A biblioteca padrão de C fornece funções que implementam os operadores matemáticos clássicos. Para utilizar essas
funções, deve-se fazer a seguinte diretiva:
#include <cmath>
Ou, se for programa em C:
#include <math.h>
Os tipos básicos 13
Segue uma tabela com apenas parte das funcionalidades disponíveis. A cada operador matemático, são associadas
três funções, cada um correspondendo a um dos três tipos flutuantes:
funcionalidade Tipo double Tipo float Tipo long double
seno: double sin(double x); float sinf(float x); long double sinl(long double
x);
cosseno: double cos(double x); float cosf(float x); long double cosl(long double
x);
tangente: double tan(double x); float tanf(float x); long double tanl(long double
x);
arccosseno: double acos(double
x);
float acosf(float x); long double acosl(long
double x);
arcseno: double asin(double
x);
float asinf(float x); long double asinl(long
double x);
arctangente: double atan(double
x);
float atanf(float x); long double atanl(long
double x);
expoente em base : double exp(double x); float expf(float x); long double expl(long double
x);
expoente em base 2: double exp2(double
x);
float exp2f(float x); long double exp2l(long
double x);
logaritmo em base (logaritmo
natural):
double log(double x); float logf(float x); long double logl(long double
x);
logaritmo em base : double log2(double
x);
float log2f(float x); long double log2l(long
double x);
logaritmo em base : double log10(double
x);
float log10f(float
x);
long double log10l(long
double x);raiz quadrada. double sqrt(double
x);
float sqrtf(float x); long double sqrtl(long
double x);
raiz cúbica: double cbrt(double
x);
float cbrtf(float x); long double cbrtl(long
double x);
valor absoluto: double fabs(double
x);
float fabsf(float x); long double fabsl(long
double x);
potência: double pow(double x,
double y);
float powf(float x,
float y);
long double powl(long double
x, long double y);
arredondamento para cima: double ceil(double
x);
float ceilf(float x); long double ceill(long
double x);
arredondamento para baixo: double floor(double
x);
float floorf(float
x);
long double floorl(long
double x);
arredondamento para o inteiro mais
próximo:
double round(double
x);
float roundf(float
x);
long double roundl(long
double x);
arredondamento para o mais
próximo int:
int rint(double x); int rintf(float x); int rintl(long double x);
arredondamento para o mais
próximo long int:
long int lrint(double
x);
long int lrintf(float
x);
long int lrintl(long double
x);
arredondamento para o mais
próximo long long int:
long long int
llrint(double x);
long long int
llrintf(float x);
long long int llrintl(long
double x);
truncamento: double trunc(double
x);
float truncf(float
x);
long double truncl(long
double x);
Os tipos básicos 14
Apresentamos de forma muito superficial os tipos flutuantes. A biblioteca padrão oferece outras funcionalidades, em
particular permite um controle muito afinado de problemas de precisão e de estouro. As funções apresentadas devem
porém devem satisfazer 99,9% das necessidades de programação.
Variáveis e funções
Variáveis
A definição de uma variável faz-se com a seguinte sintaxe:
tipo nome = valor;
onde tipo é uma expressão que define o tipo da variável, nome é o nome da variável, e valor é uma expressão
que define o valor inicial da variável. Diferente de Objective Caml, não é obrigatório associar um valor inicial a uma
variável e a seguinte sintaxe também é legal:
tipo nome;
Seguem então alguns exemplos:
char c = 'a';
int i = 0;
long long num;
Na primeira linha, é definida uma variável, cujo nome é c, o tipo é char e que tem como valor inicial 'a'. Na
segunda linha, é definida uma variável nomeada i, de tipo inteiro, cujo valor inicial é 0. Enfim, na terceira linha, é
definida uma variável de nome num, de tipo long long, e cujo valor é qualquer.
Pode se definir múltiplas variáveis do mesmo tipo, separando seus nomes por vírgulas. Assim, na linha seguinte, são
definidas três variáveis inteiras, chamadas respectivamente i, j, e k, onde apenas j possui um valor inicial, que é
2.
int i, j = 2, k;
Funções
A definição de uma função faz-se com a seguinte sintaxe:
tipo nome (parametros)
corpo
onde tipo é o tipo de retorno da função, nome é o nome da função, parametros é a lista de parâmetros da
função e corpo é um bloco de instruções.
argumentos é uma lista de declarações de parâmetros separadas por vírgulas. Essa lista pode ser vazia, quando a
função não possui nenhum parâmetro. Cada parâmetro é declarado com seu tipo e o seu nome.
corpo é um bloco, entre símbolos de chaves de instruções C/C++. Veremos mais adiante quais construções
podemos utilizar, por enquanto basta saber que um bloco pode conter declarações de variáveis e instruções
algorítmicas similares aquelas da linguagem Objective Caml (atribuição de variável, chamada de função, construções
condicionais e repetitivas). Uma diferença significativa é que um bloco C ou C++ não pode conter a definição de
uma função, portanto não existe o conceito de uma função local a outra função.
Segue agora um exemplo completo de uma função:
Variáveis e funções 15
int square (int n)
{
 return n * n;
}
Parâmetros opcionais
A linguagem C++ oferece algumas facilidades relacionadas aos parâmetros que não existem na linguagem C. Assim,
é possível dar um valor defaut a alguns parâmetros de uma função. Vejamos isso com base um exemplo:
int f (int a, int b = 0)
{
 return a + b;
}
int g (int k)
{
 return f (k);
}
A função f possui dois parâmetros, porém o segundo (nomeado b) é opcional. Quando a função f é chamada
dentro da função g, o parâmetro opcional recebe o valor defaut especificado na definição da função, no caso desse
exemplo. O mecanismo de associação entre parâmetros efetivos e parâmetros formais é realizado da esquerda para a
direita. Quando o número de parâmetros efetivos é menor que o número de parâmetros formais, esses últimos
recebem o valor defaut especificado na definição da função. Se não houver valor defaut especificado, então ocorrerá
um erro na compilação.
Considere agora o seguinte programa:
#include <iostream>
using namespace std;
int f2 (int a = 1, int b = 2, int c = 3)
{
 return a * b * c;
}
int main g()
{
 cout << f2() << endl;
 cout << f2(5) << endl;
 cout << f2(5, 10) << endl;
 cout << f2(5, 10, 15) << endl;
 return 0;
}
A saída será:
6
30
150
750
Variáveis e funções 16
Na primeira chamada, não há parâmetros efetivos, logo os parâmetros formais receberão seu valor defaut e o
resultado é . Na segunda chamada, há um parâmetro efetivo, que será associado ao primeiro
parâmetro formal, e o resultado é . Na terceira chamada, há dois parâmetros efetivos, que serão
associados aos dois primeiros parâmetros formais, e o resultado é . Na quarta e última
chamada, há trˆ´s parâmetros efetivos, que são associados aos parâmetros formais, e o resultado é
.
Vale salientar que uma função pode ter um número qualquer de parâmetros opcionais, mas eles devem aparecer nas
últimas posições da lista de parâmetros. Assim a seguinte definição de função é ilegal:
int uma_funcao_mal_definida (int a = 0, int b)
{
 return a + b;
}
Sobrecarga
Em c++ é possivel fazer com que duas ou mais funções diferentes tenham o mesmo nome desde que tenham
parâmetros diferentes e isso é chamado de sobrecarga de função por exemplo:
# include <iostream>
# include <string>
using namespace std;
int soma (int a,int b)
 {
 return a + b;
 }
float soma (float a,float b)
 {
 return a + b;
 }
string soma (char a,char b)
{
 return a + b;
} 
Nenhuma dessas três funções soma seria sobreescrevida ou ocorreria um erro pois todas possuem paramentros
diferentes e para chama-las é necessário apenas colocar os parâmetros correspondentes por exemplo:
soma (a,b);
Isso chamaria a terceira função de soma que tem como parâmetros dois caracteres.
Instruções 17
Instruções
Instruções de expressão
As instruções de expressão compõem a forma mais simples de construir uma instrução, pois permite instruir o
compilador que uma dada expressão deve ser avaliada nesse ponto. A sintaxe da instrução de expressão é:
expressao;
Ou seja, é apenas uma expressão seguida de um ponto e vírgula. A seguir vamos então ver quais são as expressões
que são mais frequentemente encontradas em instrução expressão.
Além das expressões sobre os tipos básicos já apresentadas, existem uma série de expressões diferentes providas que
permitem compor os blocos básicos de algoritmos seqüênciais. São elas:
•• Atribuição;
•• Chamada de função;
•• Pós-incremento;
•• Pré-incremento;
•• Atribuição composta;
•• Composição sequencial.
Atribuição
A sintaxe da atribuição em C e C++ é a seguinte:
alvo = expressao
A semântica dessa construção é a seguinte: a expressão fonte expressao é avaliada, e o valor resultante dessa
avaliação é memorizado por alvo. Qual a natureza do alvo? Os conceitores da linguagem chamam alvo de
lvalue. Esse conceito é similar ao de uma referência em Objective Caml: trata-se de uma entidade que pode guardar
um valor. Por enquanto, a única classe de entidades que vimos que podem ser lvalues são as variáveis.
Segue um pequenoexemplo:
int i, j;
i = 0;
j = i + 2;
Nesse trecho de código, a primeira linha declara i e j como sendo variáveis do tipo inteiro, cujo valor inicial é
indefinido. Na segunda linha, é realizada uma atribuição. O alvo é a variável i e a expressão é o valor inteiro 0.
Após a execução dessa instrução, i passa a guardar o valor 0. Na terceira linha, há uma segunda atribuição. O alvo
é a variável j e a expressão fonte é i + 2, cujo valor é . Após essa instrução, o valor memorizado por
j passa a ser 2 e o valor guardado por i continua sendo 0.
O operador de atribuição tem um valor de retorno, que é o próprio valor atribuído. Ele também tem a propriedade de
ser associativo a direita. Essas duas propriedades fazem com que seja possível realizar diversas atribuições
encadeadas em uma única instrução:
int alt, larg;
alt = larg = 5;
alt e larg são ambas variáveis do tipo inteiro com valor inicial indefinido. A segunda linha é um encadeamento 
de atribuições. Como o operador de atribuição é associativo a direita, a expressão deve ser lida assim: alt = 
(larg = 5). A atribuição à variável alt é realizada, fazendo a avaliação da expressão larg = 5 que é uma
Instruções 18
outra atribuição. Essa segunda atribuição é então avaliada fazendo a avaliação da expressão fonte 5 cujo resultado
(o número ) é atribuído à variável larg e é retornado como valor da expressão larg = 5. Esse valor é atribuído
à variável alt e é o resultado da expressão alt = larg = 5. Ao final então da avaliação dessa expressão,
ambas variáveis alt e larg guardam o valor .
Chamada de função
A chamada de função é realizada colocando o nome da função seguida de uma lista de parâmetros entre parênteses.
Se a lista for vazia, deve-se colocar mesmo assim os parênteses - caso contrário, a expressão representaria a função
sem realizar a chamada à mesma. Alguns exemplos de chamada de funções definidas na biblioteca padrão de C/C++:
printf("oi\n"); /* executa a função printf com o argumento "oi\n" */
printf("%i", 42); /* executa a função printf com os dois argumentos "%i" e 42 */
getchar(); /* executa a função getchar sem nenhum argumento */
exit (0); /* executa a função exit com o argumento 0 */
Operadores de incremento, decremento e atribuição
C/C++ possuem uma série de operadores de atribuição que tem como objetivo tornar o código mais enxuto e, em
determinados casos, permitir ou facilitar o trabalho do compilador para tirar melhor proveito das facilidades
fornecidas pelo micro-processador subjacente.
Pós-incremento
Iniciaremos vendo o talvez mais difundido desses operadores: pós-incremento. Veja um exemplo:
int i = 0;
i++;
Temos então uma variável inteira i que tem como valor inicial . Na segunda linha, temos um operador de
pós-incremento. Esse operador faz o seguinte: lê o valor inicial de i, adiciona a esse valor, guarda o resultado
dessa soma em i e retorna o valor inicial de i. Portanto é basicamente equivalente à função seguinte:
int post_increment (int & i)
{
 int result = i;
 i = i + 1;
 return result;
}
Enfim, para concluir sobre o operador de pós-incremento, deve-se salientar que apenas pode ser aplicado a um
lvalue: não faria sentido nenhum escrever 2++...
Exercício
•• Considere o seguinte trecho de código:
int i, j, k;
i = j = k = 0;
i = j++ = k++;
Qual o valor de cada variável ao término da execução desse trecho de código?
•• Considere o seguinte trecho de código:
Instruções 19
int i = 0;
f(i++);
Qual o valor do parâmetro efetivo na chamada da função f?
Pré-incremento
O operador de pré-incremento é similar ao de pós-incremento e se escreve da mesma maneira! A única forma de
diferencia-lo é na sua posição com relação ao seu argumento. Veja um exemplo:
int i = 0;
++i;
O operador de pré-incremento portanto é localizado antes do seu argumento, que deve ser um lvalue. Quando
aplicado, o argumento é avaliado, o resultado dessa avaliação é somado com o número e o resultado dessa soma é
atribuído ao argumento. Esse resultado também é o resultado da expressão.
O operador de pré-incremento portanto é equivalente à seguinte função:
int post_increment (int & i)
{
 i = i + 1;
 return i;
}
Exercício
•• Considere o seguinte trecho de código:
int i, j, k;
i = j = k = 0;
i = ++j = ++k;
Qual o valor de cada variável ao término da execução desse trecho de código?
•• Considere o seguinte trecho de código:
int i = 0;
f(++i);
Qual o valor do parâmetro efetivo na chamada da função f?
•• Na sua opinião, qual dos dois operadores de incremento deve ser executado mais rapidamente?
Operadores de decremento
Existem dois operadores para decrementar um lvalue. Não surpreendemente são chamados pós-decremento e
pré-decremento, tem como sintaxe -- e se distinguem pela posição relativa ao seu operando. Temos um exemplo de
ambos operadores no trecho de código seguinte:
int i = 5, j = 5;
--i;
j--;
A segunda e terceira linhas contêm, respectivamente, o operador de pré-decremento aplicado à variável i e o
operador de pós-decremento aplicado à variável j. O valor do primeio decremento é e o do segundo é .
Instruções 20
Exercícios
•• Considere o seguinte trecho de código:
int i, j, k;
i = j = k = 0;
i = j-- = --k;
Qual o valor das variáveis i, j e k ao término da execução desse trecho de código?
•• Escreva funções que tem papel idêntico aos dois operadores de decremento.
Operadores de atribuição composta
Além do operador de atribuição simples, existem uma série de outros operadores de atribuição que correspondem ao
efeito conjugado de uma operação aritmética ou binária e de uma atribuição. A seguinte tabela resume esses
operadores
+= soma atribuição i += j equivale a i = i + j
*= produto atribuição i *= j equivale a i = i * j
-= subtração atribuição i -= j equivale a i = i - j
/= divisão atribuição i /= j equivale a i = i / j
%= resto atribuição i %= j equivale a i = i % j
<<= deslocamento esquerda atribuição i <<= j equivale a i = i << j
>>= deslocamento direita atribuição i >>= j equivale a i = i >> j
&= conjunção binária atribuição i &= j equivale a i = i & j
^= disjunção exclusiva binária atribuição i ^= j equivale a i = i ^ j
Exercício
Considere o seguinte trecho de código:
int i = 0;
i += 5;
i -= -5;
i *= 3;
i /= 2;
i %= 2;
i <<= 1;
i >>= 2;
i &= 15;
i |= (1 << 5);
Qual o valor de i após cada atribuição?
Instruções 21
Composição seqüêncial
O operador , (vírgula) pode ser utilizado para combinar seqüencialmente expressões. É um operador binário infixo.
A sintaxe portanto é:
exp1 , exp2
A expressão exp1 é avaliada primeiro, e a expressão exp2 é então avaliada. O resultado da segunda
sub-expressão é o resultado da expressão completa. Assim, a seguinte expressão
a = 2, a += 1
tem como valor 3.
É um operador binário associativo a esquerda e pode ser repetido para encadear sequencialmente mais de duas
expressões. O valor da seqüência é o valor da última expressão da seqüência. No exemplo seguinte (puramente
ilustrativo...):
a = (2, 3, 5, 7)
o operador é encadeado para formar uma seqüência de quatro expressões, sendo o valor da última ( ) o valor da
combinação. Nesse exemplo, a variável a é atribuída então esse valor .
Blocos e seqüênciamento de instruções
Como já foi ilustrado em números exemplos, as instruções podem ser combinadas em seqüência: a ordem nas quais
as instruções aparecem corresponde à ordem na qual serão executadas.
É conveniente ter uma construção para agrupar uma seqüência de instrução em um único bloco. Em Objective Caml,
isso é feito com os delimitadores begin e end. Em C e C++, os blocos são delimitados por chaves:
• { para iniciar, e
• } para concluir.
Em C, declarações de variáveis só podem ocorrer antes da primeira instrução do bloco. Já em C++, as declarações de
variáveispodem ocorrer em qualquer posição. Um exemplo de bloco C ou C++ é:
{
 int n, sq;
 scanf("%i", &n);
 sq = n * n;
 printf("%i * %i = %i\n", n, n, sq);
}
O bloco seguinte só é legal em C++ (não o é em C):
{
 int n;
 scanf("%i", &n);
 int sq = n * n;
 printf("%i * %i = %i\n", n, n, sq);
}
Um bloco pode ocorrer em qualquer posição onde uma instrução pode ocorrer, assim os blocos podem ser aninhados
como no exemplo seguinte:
{
 int n;
Instruções 22
 scanf("%i", &n);
 {
 int sq = n * n;
 printf("%i * %i = %i\n", n, n, sq);
 }
}
É importante salientar que qualquer variável declarada em um bloco é local a esse bloco. O escopo da mesma inicia
logo após a sua declaração e termina no final do bloco. Desta forma, há um erro no bloco seguinte, pois a variável sq
é local ao bloco mais interno e não é conhecida no bloco mais externa.
{
 int n;
 scanf("%i", &n);
 {
 int sq = n * n;
 }
 printf("%i * %i = %i\n", n, n, sq); /* atenção: erro nessa linha !!! */
}
Retorno de função
Como as instruções de C e C++ não retornam valor, precisamos de um mecanismo para poder indicar qual valor uma
função deve retornar. É o papel da instrução de retorno. Ela tem a seguinte sintaxe:
return expressao;
onde expressao é uma expressão. A semântica é que essa expressão é avaliada e o valor resultante é o valor de
retorno da função. A execução da função que contem a instrução é interrompida e o valor obtido através da avaliação
da expressão é retornado.
Um exemplo
Segue um exemplo de uso da instrução de retorno em uma função que calcula o quadrado de um dado número
decimal:
float quadrado (float x)
{
 return x * x;
}
Observação sobre a função main
Lembre-se que a função main é, por convenção, o ponto inicial de execução de um programa escrito em C++ ou
em C. O tipo de retorno da função main é o tipo int. O valor de retorno pode ser utilizado para fornecer
informações sobre o desenrolar da execução do nosso programa ao sistema operacional ou a demais programas. Por
convenção nos sistemas Unix e similares, um programa retorna o valor 0 quando a execução transcorreu
normalmente. Em C e C++, o comportamento defaut da função main é de retornar o valor 0. Assim, o seguinte
programa:
int main ()
{
}
Instruções 23
é equivalente ao seguinte programa
int main ()
{
 return 0;
}
Exercícios
Escreva uma função que, dada um número decimal, retorna o cubo desse número.
Condição
A execução de uma instrução, ou de um bloco de instrução, pode ser condicionada ao valor de uma condição,
utilizando a tradicional construção if ... then ... else .... A sintaxe básica é a seguinte:
if (condicao) 
 instrucao1
else
 intrucao2
Note que a condição deve aparecer entre parênteses, e apenas pode haver uma ocorrência da construção sintática
"instrução" em cada ramo de execução. Caso seja preciso realizar mais de uma instrução em um ramo, deve-se
agrupá-las em um bloco utilizando a construção de blocos vista anteriormente.
Um primeiro exemplo de condição que permite atribuir à variável menor o menor valor de duas variáveis n e m é:
if (n <= m)
 menor = n;
else
 menor = m;
Esse segundo exemplo demostra como combinar uma instrução condicional com blocos:
if (n <= m)
{
 min = n;
 max = m;
}
else
{
 min = m;
 max = n;
}
Uma outra forma bastante comum de escrever a construção anterior, utilizando um leiaute um pouco diferente é:
if (n <= m) {
 min = n;
 max = m;
} else {
 min = m;
 max = n;
}
Instruções 24
Em Objective Caml, as instruções formam uma classe de expresssões e algumas restrições de tipos aplicam-se às
instruções como a instrução condicional, onde as instruções da ambos ramos devem ter o mesmo tipo. Tanto em C
quanto em C++, as instruções não possuem tipos, logo não existe essa restrição.
É importante salientar que o ramo else é opcional. Assim a construção sintática seguinte é perfeitamente legal:
if (condicao)
 instrucao1
Um exemplo que ilustra essa forma é
if (menor > maior) {
 int tmp = menor;
 menor = maior;
 maior = tmp;
}
Enfim, pode-se encadear um número qualquer de instruções condicionais, utilizando a seguinte forma:
if (condicao1)
 instrucao1
else if (condicao2)
 intrucao2
else if (condicao3)
 intrucao3
else
 instrucaoe
Nesse caso, o último ramo também é opcional, como demostrado no seguinte exemplo:
if (mes = 1) printf("janeiro");
else if (mes = 2) printf("fevereiro");
else if (mes = 3) printf("marco");
else if (mes = 4) printf("abril");
else if (mes = 5) printf("maio");
else if (mes = 6) printf("junho");
else if (mes = 7) printf("julho");
else if (mes = 8) printf("agosto");
else if (mes = 9) printf("setembro");
else if (mes = 10) printf("outubro");
else if (mes = 11) printf("novembro");
else if (mes = 12) printf("dezembro");
Exemplos
Com o operador condicional, e utilizando a recursividade, já podemos a definir algumas funções interessantes.
Esse primeiro exemplo calcula o -ésimo elemento da seqüência de Fibonacci.
int fib (int n)
{
 if (n == 1 || n == 2)
 return 1;
 else
Instruções 25
 return fib(n-1) + fib(n-2);
}
Esse segundo exemplo calcula onde representa um número decimal e representa um número natural.
int power (float x, int n)
{
 if (n == 0)
 return 1;
 else {
 float x2 = power (x, n/2);
 float quad = x2 * x2;
 if (n % 2 == 1) {
 quad *= x;
 }
 return quad;
 }
}
Exercícios
•• Defina uma função que calcula o maior divisor comum de dois números naturais maiores que um.
•• Defina uma função que testa se um número positivo é primo.
Seleção
As linguagens C e C++ possuem um segundo tipo de instrução condicional, que identificamos como instrução de
seleção. Enquanto uma instrução permite orientar o fluxo de execução em função do valor de uma condição, uma
instrução de seleção permite orientar o fluxo de execução em função de um valor inteiro.
A sintaxe da instrução de seleção é:
switch (expressao) 
instrucao
com a restrição que expressao é um expressão de tipo inteiro. Em geral instrucao é um bloco de instruções
(entre chaves).
A semântica é um pouco complexa a ser explicada, mas, felizmente, é mais fácil de entender!... A semântica é que
expressao é avaliada, e em função do valor obtido através dessa avaliação, o fluxo de execução continua a partir
de um certo ponto de controle ou após a instrução de seleção. Para efeitos de explicação, vamos assumir que é o
valor de expressão. Existe dois tipos de pontos de controle: case ou default. Os pontos casesão seguidos
de uma expressão. Se existir pelo menos um ponto case tal que o valor da expressão associada seja também igual a
, então a fluxo de execução continua a partir do primeiro desses pontos. Se não existir nenhum ponto case
satisfazendo essa condição, e existir um ponto default, então a execução continua a partir do ponto default. Se
também não existir ponto default, a execução continua após a instrução de seleção.
Instruções 26
Exemplos
Para passar a intuição, então vamos lançar mão de alguns exemplos.
•• O primeiro exemplo é:
int i = 1;
switch (i) {
 case 0: printf("0\n");
 case 1: printf("1\n");
}
printf("saiu\n");
A expressão de seleção tem valor que também é o valor do segundo ponto de controle. A execução desse trecho de
código resultará na seguinte impressão na saída padrão:
1
saiu
•• O segundo exemplo é:
int i = 2;
switch (i) {
 case 0: printf("0\n");
 case 1: printf("1\n");
}
printf("saiu\n");
A expressão de seleção tem valor que é o valor de nenhum ponto de controle case. Como não há ponto de
controle default, o fluxo de execução prossegue após a instruçãode seleção. A execução desse trecho de código
resultará na seguinte impressão na saída padrão:
saiu
•• O terceiro exemplo é:
int i = 2;
switch (i) {
 case 0: printf("0\n");
 case 1: printf("1\n");
 default: printf("outro\n");
}
printf("saiu\n");
A expressão de seleção tem valor que é o valor de nenhum ponto de controle case. Como há um ponto de
controle default, o fluxo de execução prossegue após esse ponto de controle. A execução desse trecho de código
resultará na seguinte impressão na saída padrão:
outro
saiu
•• O quarto exemplo é:
int i = 0;
switch (i) {
Instruções 27
 case 0: printf("0\n");
 case 1: printf("1\n");
 default: printf("outro\n");
}
printf("saiu\n");
Agora a expressão de seleção tem valor . O primeiro ponto de controle tem valor , e a execução prossegue a
partir dele. A saída portanto será:
0
1
outro
saiu
Será que é isso que você estava esperando? Se for, muito bem, você leu atentamente os parágrafos iniciais. Se não
for, talvez é porque você ainda tem em mente o comportamento da construção de casamento de padrões de Objective
Caml. Nessa construção, apenas é avaliada a expressão que corresponde ao primeiro padrão que casa. Na verdade
esse tipo de comportamento é mais comum em algoritmos e processamento de dados que o comportamento exibido
por esse último exemplo. Vamos ver na seqüência como obtê-lo em C ou C++.
Give me a break
A instrução break permite interromper o fluxo de execução em uma instrução de seleção. Quando o fluxo de
execução encontra uma instrução break, o fluxo sai da instrução de seleção mais interna contendo essa instrução.
Continuando a linha de exemplos da seção, vejamos como empregar a instrução break:
int i = 0;
switch (i) {
 case 0: printf("0\n");
 break;
 case 1: printf("1\n");
 break;
 default: printf("outro\n");
}
printf("saiu\n");
O valor da expressão de seleção é que também é o valor do primeiro ponto de controle case. A instrução de
impressão é executada e uma instrução break é encontrada, resultando no desvio do fluxo de execução para a
primeira instrução após a instrução de seleção. A execução desse trecho de código resulta então na seguinte
impressão na saída padrão
0
saiu
Instruções 28
Exemplo
Para concluir sobre a instrução de seleção, mostramos um exemplo completo. Esse exemplo ilustra os diferentes
pontos já visto e ainda a possibilidade de enumerar diversos pontos de controle.
void imprime_numero_em_portugues (int n)
{
 switch (n) {
 case 0:
 printf("nenhum");
 break;
 case 1:
 printf("um");
 break;
 case 2: case 3:
 printf("poucos");
 break;
 case 4: case 5: case 6:
 printf("alguns");
 break;
 default:
 printf("muitos");
 }
}
Repetição
As linguagens C e C++ possuem três construções de repetição diferentes: instrução while, instrução do e
instrução for.
Instrução while
A sintaxe da instrução while é a seguinte:
while (condicao)
 instrucao
A semântica é similar à da linguagem Objective Caml:
• A expressão condicao é avaliada
• Se o resultado dessa avaliação é (ou ), então termina a execução da instrução while,
• Se o resultado dessa avaliação é (ou diferente de ), então a instrução instrucao é avaliada e a
instrução while é executada novamente (volta-se ao primeiro ponto).
Nessa construção, o corpo da repetição é executado zero ou mais vezes.
Segue aqui um exemplo onde a instrução é empregada para calcular o n-ésimo valor da seqüência de Fibonacci.
int fibonacci (int n)
{
 if (n <= 2) return 1;
 int corrente, anterior, i;
 corrente = anterior = 1;
 i = 2;
Instruções 29
 while (i != n) {
 i += 1;
 int tmp = anterior;
 anterior = corrente;
 corrente += tmp; 
 }
 return corrente;
}
Exercício
Utilizando a instrução while, escreva funções para:
•• calcular o fatorial de um número inteiro positivo;
•• elevar um número decimal à uma potência inteira;
•• calcular o maior divisor comum de dois números inteiros positivos.
Instrução do
A instrução do tem a seguinte sintaxe:
do
 instrucao
while (condicao);
A semântica é similar à construção while, a diferença ficando no momento que a condição de repetição é avaliada:
após a execução do corpo da instrução. Assim, em uma instrução do, o corpo é executado pelo menos uma vez.
Exemplo
A função seguinte emprega uma instrução do e calcula o número mínimo de bits necessários para representar um
número inteiro.
int logaritmo2 (int n)
{
 int result = 0;
 do {
 n /= 2;
 result += 1;
 } while (n);
 return result;
}
Instruções 30
Instrução for
A instrução for é a mais complexa das construções de repetição das linguagens C e C++. A sintaxe dela é a
seguinte:
for (expressao_init; condicao_c; expressao_inc)
 instrucao
onde:
• expressao_init é uma expressão ou declaração de inicialização;
• condicao_c é uma condição de repetição
• expressao_inc é uma expressão de incremento
• instrucao é a instrução que constitui o corpo da repetição.
A semântica é a seguinte. Primeiro expressao_init é avaliada. Em seguida, condicao_c é avaliada. Se o
resultado da avaliação for ou , então a execução prossegue após a instrução for. Caso contrário, o corpo
instrucao é executado. Após isso, a expressão de incremento expressao_inc é avaliada.
Em uma instância típica de uma instrução for, a expressão expressao_init é utilizada para declarar e
inicializar variáveis, e expressao_inc para incrementar essas variáveis. Por exemplo, o código seguinte resulte
na impressão dos 10 primeiros números naturais na saída padrão:
for (int n = 0; n < 10; ++n)
 printf("%i\n", n);
Diferente de Objective Caml, onde a construção de repetição limitada só permite incrementar ou decrementar a
variável de laço, em C e C++, qualquer operação pode ser utilizada para construir expressao_inc. Assim, a
função seguinte imprime todos os múltiplos de que são menores ou iguais a topo:
int imprime_multiplos (int m, int topo)
{
 for (multiplo = m; multiplo <= topo; multiplo += m)
 printf("%d\n", multiplo);
}
Evidentemente, pode-se utilizar uma instrução bloco para combinar mais de uma instrução no corpo de uma iteração
for:
int imprime_multiplos_em_uma_linha_só (int m, int topo)
{
 for (int multiplo = m; multiplo <= topo; multiplo += m) {
 printf("%d", multiplo);
 if (multiplo + m <= topo) {
 printf(" ");
 } else {
 printf("\n");
 }
}
Instruções 31
Modificação do fluxo de execução em repetições
Existem duas formas de modificar o fluxo de execução no corpo de uma repetição: através de uma interrupção, ou
passando para a próxima iteração.
Interrupção
A instrução de interrupção tem a seguinte sintaxe:
break;
Observe que é a mesma palavra-chave que utilizamos em instruções de seleção (switch). Blocos de instrução em
repetições e seleção são as duas únicas possibilidades para ter uma instrução de interrupção break.
A semântica é que, quando o fluxo de execução encontra uma instrução de interrupção, ele pula diretamente para
depois da instrução de repetição mais aninhada que o contem.
Próxima repetição
A instrução de próxima repetição ou continuação tem a seguinte sintaxe:
continue;
Essa instrução só pode ocorrer dentro do bloco de instruções de uma instrução de repetição. A semântica é que o
fluxo de execução pula diretamente para o fim do corpo da repetição. Seguem três trechos esquemáticos desse
comportamento
while (/* condição */) {
 /* algumas instruções */
 continue;
 /* outras instruções */
 /* alvo do pulo do continue é aqui*/
}
do {
 /* algumas instruções */
 continue;
 /* outras instruções */
 /* alvo do pulodo continue é aqui*/
} while (/* condição */);
for (/* inicialização */; /* teste */; /* incremento */) {
 /* algumas instruções */
 continue;
 /* outras instruções */
 /* alvo do pulo do continue é aqui*/
}
Instruções 32
Exercícios
Defina funções que desenham as seguintes formas geométricas na saída padrão:
• Um quadrado, de lado 
Exemplo de saída para n = 5:
*****
* *
* *
* *
*****
• Um retângulo, de largura e altura 
Exemplo de saída para n = 5 e m = 4
*****
* *
* *
*****
• Um quadrado rotacionado de 45 graus, cuja diagonal é .
Exemplo de saída para n = 2
 *
 * *
* *
 * *
 *
Construção de tipos 33
Construção de tipos
Conceitos iniciais 
As linguagens C e C++ possuem muitos mecanismos para construir e estruturar dados complexos. Diferentemente
dos tipos básicos, onde há uma forte correspondência entre essas linguagens e a linguagem Objective Caml, a
construção de tipos complexos em C e C++ é bem diferente daquela provida por Objective Caml. Esse capítulo não
aborda a construção de tipos via classes de objetos, que é um paradigma presente nas linguagens Objective Camle e
C++, mas que é ausente da linguagem C.
O sistema de tipos de C e C++ é bastante complexo e, em prol da lisibilidade desse texto, algumas aproximações e
simplificações foram realizadas. Apresentaremos as seguintes construções para a criação e manipulação de tipos
complexos:
•• Enumerações correspondem a uma forma muito básica de tipos variantes de Objective Caml.
•• Arranjos correspondem aos arranjos de Objective Caml, embora sejam objetos muito mais simples. A linguagem
C++ provê, através da sua biblioteca, uma outra implementação de arranjos que tem um nível de abstração mais
próximo ao dos arranjos de Objective Caml.
•• Registros correspondem aos tipos registros de Objective Caml.
•• Ponteiros correspondem às referências de Objective Caml.
•• As referências, presentes apenas na linguagem C++, não tem correspondência direta em Objective Caml, embora
possam ser simuladas por ponteiros, logo pelas referências de Objective Caml.
•• As uniões correspondem aproximadamente aos tipos variantes de Objective Caml, embora não sejam tão práticas
para serem manipuladas.
•• Enfim, terminaremos com a apresentação dos tipos funcionais, que existem em C e em C++. Embora não sejam
tão flexíveis quanto os tipos funcionais de Objective Caml, permitem ainda programação de ordem superior.
Vale destacar que em C e em C++, pode-se dar um nome a um tipo, utilizando a seguinte construção, ou alguma
variação:
typedef expressaodetipo nome;
Por exemplo, podemos definir um tipo chamado inteiro que é idêntico ao tipo básico int com a seguinte
construção:
typedef int inteiro; // exemplo de definição de um tipo chamado "inteiro", que nada mais é que o tipo int
Então a mais simples expressão de tipo que exista é um nome de um tipo básico, ou de outro tipo que já foi definido
dessa forma. Uma vez definido um nome para um tipo, ele pode ser usado para declarar variáveis ou funções da
mesma forma que os tipos básicos. Por exemplo:
inteiro soma3 (inteiro a, inteiro b, inteiro c) // exemplo (artificial) onde usamos um nome de tipo definido por nós
{
 inteiro resultado = a;
 resultado += b;
 resultado += c;
 return resultado;
}
Também pode ser utilizar uma expressão de tipo diretamente na declaração de uma variável. Assim esse código:
struct Spoint { // nesse exemplo, usamos uma expressão de tipo registro, detalhes sobre essa construção seguem mais adiante
 double x;
Construção de tipos 34
 double y;
} point; // point é uma variável cujo tipo é um tipo registro struct Spoint
é equivalente a
typedef struct Spoint {
 double x;
 double y;
} Tpoint; // definimos Tpoint como o nome de um tipo registro struct Spoint
Tpoint point; // point é uma variável cujo tipo é Tpoint, ou seja o tipo registro struct Spoint
Enumerações 
Com relação ao sistema de tipos de Objective Caml, os tipos enumerados lembram de forma superficial os tipos
variantes. Os tipos enumerados são porém muito mais limitados, e correspondem mais precisamente a tipos variantes
onde as alternativas devem ser todas constantes.
Um tipo enumerado contem um número finito de valores. Cada valor tem um nome que o identifica. Por exemplo a
seguinte definição:
enum Ecor {vermelho, azul, amarelo};
introduz um tipo enumerado, que possui como rótulo Ecor, e três enumeradores, que são valores inteiros constantes
com identificadores vermelho, azul, amarelo.
Pode-se dar um nome a esse tipo, utilizando uma definição de tipo:
typdef enum Ecor {vermelho, azul, amarelo} Tcor;
Em seguida, podemos declarar e utilizar variáveis com esse tipo, e os valores que foram introduzidos na operação
podem também aparecer no programa, onde serão consideradas como valores inteiros. Segue um trecho de programa
que utiliza a definição acima:
void imprime_cor_em_ingles (Tcor c) // o parâmetro poderia também ter sido declarado como enum Ecor c
{
 switch (c1) {
 case vermelho:
 printf("red");
 break;
 case azul:
 printf("blue");
 break;
 case vermelho:
 printf("amarelo");
 break;
 }
}
Afirmamos que um valor inteiro é associado a cada valor de uma enumeração. Podemos realizar essa associação de
forma implícita ou explícita. A forma explícita é realizada associando o número inteiro na definição da enumeração,
como demostrado no seguinte exemplo:
enum Edirecao { norte = 0; oeste = 90; sul = 180; leste = 270 };
Construção de tipos 35
Caso não aparece associação explícita, a associação é realizada de maneira implícita, utilizando as seguintes regras: o
primeiro valor da enumeração recebe o valor zero, e um valor que não está na primeira posição é associado com a
soma de um com o inteiro associado ao valor anterior.
Finalmente, podemos também misturar as associações explícitas e implícitas, como no seguinte exemplo:
enum Ecodigo { NO_ERROR = 0; IO_ERROR = 10; FORMAT_ERROR; TIMEOUT_ERROR };
Nesse exemplo, FORMAT_ERROR e TIMEOUT_ERROR denotam respectivamente os inteiros e .
Exercício
Considere o seguinte tipo de dados definido em Objective Caml
type card_suit_t = Spades | Hearts | Diamonds | Clubs
Define um tipo equivalente em C/C++.
Arranjos 
A linguagem C permite declarar arranjos uni-dimensionais, dando as seguintes informações: o tipo dos elementos, e
o tamanho do arranjo. Por exemplo, o seguinte código define uma variável notas para guardar quatro valores do
tipo float, utilizaremos o seguinte código:
float notas [4];
Portanto a sintaxe consiste em identificar o tipo dos elementos, através de uma expressão de tipos, o nome do
arranjo, e entre colchetes o tamanho do arranjo.
Opcionalmente, podemos associar um valor inicial aos elementos do arranjo assim definido. Há duas possibilidades
• Para definir o mesmo valor em todas as posições do arranjo, colocamos apenas o valor do primeiro elemento. Por
exemplo, o código seguinte corresponde à definição de um arranjo similar ao exemplo anterior, mas onde todas as
posições do arranjo armazenam o valor :
float notas [4] = { 0.0f };
Portanto a sintaxe exige que a expressão de inicialização seja colocada entre chaves.
•• Também pode-se definir individualmente cada posição do arranjo. Isso é realizado colocando entre chaves, a
enumeração dos valores do arranjo. O exemplo seguinte portanto é equivalente ao anterior:
float notas [4] = {0.0f, 0.0f, 0.0f, 0.0f};
A sintaxe exige que os elementos da inicialização sejam separados por vírgulas.
Operadores sobre arranjos
C e C++ oferecem a possibilidade de acessar cada posição do arranjo individualmente. Como em Objective Caml, o
endereço da primeira posição é e as demais posições são obtidas somando um à anterior. Por exemplo, o código
seguinte declaraum arranjo e inicializa todas as suas posições em seqüência.
float notas [4];
notas[0] = 0.0f;
notas[1] = 0.0f;
notas[2] = 0.0f;
notas[3] = 0.0f;
Construção de tipos 36
Evidentemente, uma repetição for é uma construção particularmente adequada para percorrer todas as posições de
um arranjo. Por exemplo:
float notas [4];
for (int i = 0; i < 4; ++i)
 notas[i] = 0.0f;
Objective Caml possui uma função que, aplicada a um arranjo, retorna o número de posições do arranjo. Isso não
existe nem C nem em C++ (C++ tem uma outra implementação de arranjo, chamada vector que tem essa
funcionalidade; será apresentada mais adiante). Objective Caml possui também mecanismos para verificar que o
acesso aos arranjos é realizado dentro dos limites. C e C++ não oferecem esses mecanismos, e podemos
perfeitamente escrever o seguinte programa:
#include <cstdio>
int main ()
{
 int arranjo[2] = {1, 2};
 arranjo[2] = 3;
 printf("arranjo[0] = %i, arranjo[1] = %i, arranjo[2] = %i\n", arranjo[0], arranjo[1], arranjo[2]);
}
Observe que a função main inclui a definição de uma variável arranjo, com duas posições armazenando valores
do tipo int. As posições válidas portanto são e . O programa realiza um acesso em escrita na posição ,
portanto fora dos limites e também um acesso em leitura. Utilizando g++ versão 4.0.1, esse programa é compilado
sem erro (nem aviso). Ele pode até ser executado! E funciona! Olhe:
daviddeharbe $ g++ -Wall prog.cpp -o prog
daviddeharbe $ ./prog
arranjo[0] = 1, arranjo[1] = 2, arranjo[2] = 3
Então o programa está correto? Claro que não. Nessa oportunidade ele executou normalmente, mas pode ser que em
alguma outra plataforma, tenha um erro em tempo de execução. Nesse exemplo, é bastante fácil detectar esse erro
lendo o código, mas em programas mais extensos essa tarefa de verificação por leitura é muito mais difícil.
Esse pequeno programa ilustra uma diferença de filosofia entre as linguagens C e C++ de um lado, e a linguagem
Objective Caml do outro. As primeiras privilegiam o desempenho em tempo de execução, mas não oferecem suporte
para verificações elementares, criando grandes riscos do que erros passam despercebidos durante muito tempo até
eles causarem problemas. Objective Caml, do outro lado, é muito mais rigorosa, e aplica um maior número de regras,
tanto em tempo de compilação, quanto em tempo de execução. A implementação em Objective Caml de um dado
algoritmo, será mais lenta que a implementação em C ou em C++, mas em compensação, oferecerá muito mais
garantias de correção e de segurança.
Construção de tipos 37
Arranjos como parâmetros de funções
Nas linguagens C e C++, como em Objective Caml, os parâmetros são passados por valor. Isso significa que quando
uma função é chamada, as variáveis que correspondem aos parâmetros formais da função comportam se como se
fossem variáveis locais ao corpo da função que são inicializadas com o valor dos argumentos que foram utilizados
para chamar essa função.
No caso dos arranjos, o parâmetro formal é o mesmo arranjo que o argumento efetivo da chamada da função. O
seguinte exemplo ilustra essa particularidade:
#include <cstdio>
void swap1 (int i, int j)
{
 int tmp = i;
 i = j;
 j = tmp;
}
void swap2 (int tab[2])
{
 int tmp = tab[0];
 tab[0] = tab[1];
 tab[1] = tmp;
}
int main ()
{
 int a = 1, b = 2;
 printf("antes: a = %i, b = %i\n", a, b);
 swap1(a, b);
 printf("depois: a = %i, b = %i\n", a, b);
 int arranjo[2] = {1, 2};
 printf("antes: arranjo[0] = %i, arranjo[1] = %i\n", arranjo[0], arranjo[1]);
 swap2(arranjo);
 printf("depois: arranjo[0] = %i, arranjo[1] = %i\n", arranjo[0], arranjo[1]);
} 
A execução desse programa resultará na seguinte impressão na saída padrão
antes: a = 1, b = 2
depois: a = 1, b = 2
antes: arranjo[0] = 1, arranjo[1] = 2
depois: arranjo[0] = 2, arranjo[1] = 1
Resumidamente, a explicação disso é que, em C e C++, um arranjo corresponde ao endereço da primeira posição do
arranjo. Quando a função swap2 é chamada com o argumento arranjo, o endereço da primeira posição de
arranjo é copiada para tab, que passa a ter então a sua primeira posição nesse endereço. Logo tab[0] e
tab[1] são respectivamente idênticos a arranjo[0] e arranjo[1].
Construção de tipos 38
Arranjos como resultados de funções
As linguagens C e C++ não permitem que o tipo de retorno de uma função seja um tipo arranjo. Uma possibilidade
para contornar essa limitação é ter um parâmetro a mais que será atualizado no corpo da função. O exemplo seguinte
ilustra essa técnica.
Um primeiro exemplo
O seguinte programa provê uma implementação em C++ de um algoritmo que calcula e imprime os números primos
até um certo valor . Esse algoritmo é conhecido como o crivo de Eratosthenes.
// programa que lê um número n da entrada padrão e imprime na saída padrão todos os números primos até n
// implementa o crivo de Eratosthenes
#include <cstdio>
Observe que o exemplo inclui uma função tal que um dos parâmetros é um arranjo de booleanos.
void compute_primes (bool sieve[], int n) // preenche o crivo de 0 até n-1
{
 sieve[0] = sieve[1] = false;
 for (int i = 2; i * i < n; ++i) {
 if (sieve[j]) {
 for (int j = i + i; j < n; j += i) {
 sieve[j] = false;
 }
 }
 }
}
int main ()
{
 int i, n;
 scanf("%d", &n);
 if (n < 2) return;
 bool sieve [n+1] = {true};
 compute_primes(sieve, n+1);
 for (int i = 2; i < n+1; ++i) {
 if (sieve[i]) printf("%d ", i);
 }
 printf("\n");
}
Detalhes sobre o crivo podem ser obtidos em: Weisstein, Eric W. "Sieve of Eratosthenes." From MathWorld--A
Wolfram Web Resource. http:/ / mathworld. wolfram. com/ SieveofEratosthenes. html
Construção de tipos 39
Arranjos multi-dimensionais
Como em Objective Caml, um arranjo de dimensão de valores de um tipo t podem ser implementados em C e
em C++ usando um arranjo de arranjos de dimensão de valores do tipo t.
Uma matriz bi-dimensional de inteiros, de dimensão por , pode então ser declarada como:
int matriz [n][m];
Um segundo exemplo
float media (float tab[], int n) // calculo da media de um arranjo tab de floats de tamanho n.
{
 float sum = 0.0f;
 for (int i = 0; i < n; ++i)
 sum += tab[i];
 return sum/n;
}
int main()
{
 int n;
 cin >> n; // o numero de alunos e lido
 if (n == 0) return 0;
 float notas[3][n];
 for (int i = 0; i < n; ++i) // para cada aluno
 cin >> notas[0][i] >> notas[1][i] >> notas[2][i]; // ler as tres notas e guardar no arranjo notas
 for (int i = 0; i < 3; ++i) { // calculo e impressao da media de cada prova
 cout << "A media da " << i << "a prova e " << media(tab[i], n) << endl;
 }
}
Tipos registros
Os tipos registros de C/C++ são essencialmente idênticos aos de Objective Caml: é um forma de agregar um número
fixo de valores de diferentes tipos. Esses valores são chamados campos do registro. Cada campo possui um nome
que o identifica e um tipo que especifica quais valores ele pode ter. Vamos agora ver a sintaxe em C/C++ através de
um exemplo.
Considere o seguinte tipo registro (em OCaml):
type point = { x : float ; y : float }
Em C/C++, o equivalente é
typedef struct Sponto {
 float x;
 float y;
} point;
Pode-se acessar aos campos utilizando um operador que tem a mesma sintaxe que o de Objective Caml. Assim, caso
desejarmos definir uma função que calcula a distância entre dois pontos dados, podemos escrever:
#include <math.h>
float distance (point p1, point p2)
{
Construção de tipos 40
 float dx = p1.x - p2.x;
 float dy = p1.y - p2.y;
 return sqrtf(dx*dx + dy+dy);
}
C/C++ diferem de Objective Caml na forma de denotar valores de um tipo registro. Enquanto que em Objective
Caml tem que explicitamente

Outros materiais