CPP

De ROMHackingWiki

Tabela de conteúdo

Recomendações a quem ler este manual.

Espero que as pessoas leiam o manual sequencialmente, isto porque após alguma ponderação acredito ser esta a maneira mais coerente e lógica para se ter uma aprendizagem profunda.

Espero ainda uma outra coisa muito mais importante do que a minha organização de tópicos que é: Pretendia que o leitor realmente meditasse nas limitações do próprio tópico antes de avançar para o seguinte, até porque isso permitiria uma aprendizagem de outro nível que seria a criação pessoal do leitor: Ou seja, pensar o que é que o tópico me permite fazer. O que é que eu posso fazer com isto. Até onde é que eu posso ir com isto. Se eu fosse o criador da linguagem achava que isto ficaria melhor desta ou daquela maneira, colocaria este ou aquele comando, diria que aquele comando não me serve para muito, …etc., espero que o leitor crie, pois só assim irá simular o que o criador desta linguagem fez. Repare que esta linguagem já tem muita e muita revisão.


Compilador

Antes de avançarmos convém termos uma aprendizagem mais interactiva, por forma a que o leitor experimente os programas que vão sendo lançados ao longo do texto.

Precisaremos de um compilador. Este programa irá traduzir todas as instruções que escrevermos no nosso programa em linguagem binária, mas estruturada numa forma para que o sistema operativo reconheça aquele pedaço de dados como um executável, como uma série de instruções. ora conclui-se que estes ficheiros EXE são o output dos compiladores.

Convém ainda explicar mais: o código que escrevermos no compilador chama-se “código-fonte(“source code”). É o código que escrevemos e está numa linguagem C++, que human-readable. Os compiladores actuais incluem também pré-compiladores (antes eram software independente, extra), estes precompiladores vão fazer alterações ao código fonte, que basicamente consiste em eliminar pedaços de código que escrevemos, substituir pedaços de código que escrevemos por outro (copy-paste), enfim, são estéticas mas não vão alterar as nossas instruções.


Onde arranjar um compilador: Eu Recomendaria o DEV-C++ que não é bem um compilador mas sim um IDE, o que torna as coisas mais fáceis. é uma aplicação onde tem o compilador, um editor, um linker… tem as coisas mais agradáveis esteticamente funcionalmente.


Começar a programar

Código fonte Resultado
// my first program in C++
/*temos aqui o nosso
primeiro programa*/
#include <iostream>
using namespace std;
int main ()
{
 int a;
 cout << "Hello World! Diga o seu nome!\n";
 cin >> a;
 cout << “Benvindo”<<a;
 system ("pause");
 return 0;
}
Hello World! Diga o seu nome!
João
Benvindo João
Prima qualquer tecla para continuar…

Este é o nosso primeiro programa. Podem fazer copy paste e depois clicar no botão “compile & run” e obtemos o que está na segunda coluna. Vamos explicar cada linha de código.


// my first program in C++
/*temos aqui o nosso
primeiro programa*/

Esta é uma linha de comando, o précompilador ao analisar o código encontra a sequencia “//” e vai eliminar o texto que está a seguir até ao fim da linha. a linguagem C++ permite também fazer comentários por mais do que uma linha, chama-se comentário por bloco e o que faz é eliminar tudo o que encontrar entre a sequencia inicial /* e o final */.


#include <iostream>

Isto é mais outra instrução para o précompilador. Ao encontrar o símbolo “#”, ele repara que é uma chamada para ele. Depois, interpreta que deve incluir algo através da directiva “include”. Mas o quê? é um ficheiro chamado de iostream. Este ficheiro é importante para nós, o seu nome vem de in+out+stream. Pois contém código já feito por outros por forma a manipular as ligações de entrada e saída de dados. no fim de contas vamos incluir este ficheiro porque vamos utilizar algumas funcionalidades contidas neste ficheiro.

using namespace std;

Existem funções pré-elaboradas e são reunidas numa biblioteca (de funções) padrão do C++. Essas funções pertencem ao "espaço de nomes" std (de standard, padrão em inglês). Por exemplo, no código usado, usamos os objetos cin e cout. Ambos pertencem ao espaço std. Isso significa que, se quisermos usá-los, deveríamos escrever std::cin e std::cout. Para ser mais rápido de escrever o código, usamos essa linha (using namespace std;). Ou seja, evitamos escrever std::cin, escrevendo apenas cin.


int main ()
{
}

Ora aqui temos a função main(). Esta função é especial, diferente de todas as outras que iremos utilizar. O método até é engenhoso. O programa vai começar por esta função, é logo a primeira coisa que faz, vai percorrer as instruções todas e quando chegar ao fim das instruções e da função main(). o programa findou. não há mais nada. Mesmo que tenhamos mais instruções escritas antes ou depois do main . Assim a técnica é chamar estas funções extras no meio do main(). A função vai retornar um número, como é que eu sei isso? É porque declarei que o int que vem de inteiro. uso () pois isso é nos vai dizer que aquilo é uma função {} é apenas estabelecer um bloco de código.

int a;

aqui vai ser reservado um espaço de memória a qual vamos chamar de "a". porém esse espaço reservado vai ter o tamanho de um inteiro, ie, de 4 bytes (este tamanho varia de acordo com o processador).


cout << "Hello World. Diga o seu nome:\n";

Esta é finalmente a instrução que faz realmente o resultado final e o que faz é colocar a sequência de caracteres entre aspas no fluxo out (que irá ser o ecrã pois definimos antes como sendo o standard std) cout (c+out) esta função está declarada no ficheiro “iostream” e dentro do standard namespace. mas afinal porque é que nós temos ainda de dizer que vamos usar um ficheiro especifico e declarar que vamos usar este (o std) namespace, uma vez que o cout está apenas definido, e reconhecido nestas circunstancias?


 cin >> a;

Esta linha vai guardar o que for digitado no teclado e vai colocar na variavel a;

 cout << “Benvindo”<<a;

aqui será impresso no ecrã a frase entre aspas "" e depois é impresso também o valor que está no espaço de memória a que chamamos de "a".

system ("pause");

Coloquei esta linha para que o programa não finaliza-se sem que pudéssemos ver uma janela com o resultado, se não o fizesse a janela abria-se e fechava sem que nos apercebêssemos devido á rapidez

return 0;

A função return, até deveria ter escrito return(0)diz-nos que o programa vai retornar o valor de zero. Poderíamos fazê-lo retornar qualquer outro valor que pretendêssemos, porém por convenção e para que não tenhamos resultados inesperados, fazemos com que o zero represente a ideia de que o programa funcionou como esperado sem erros.

Organização do código fonte

Colocámos o código fonte desta forma - Código fonte 1

Código fonte 1 Código fonte 2
int main ()
{
 cout << " Hello World ";
 return 0;
}
int main () { cout << "Hello World"; return 0; }

Mas poderíamos muito bem ter feito desta: Código fonte 2


Isto para enfatizar dois pontos:

  • A separação entre instruções faz-se por meio de uso do ponto e virgula “;”
  • A divisão do código por várias linhas serve apenas para tornar o código mais legível, e mas não perdermos nenhuma linha no raciocínio. Portanto podemos por quantas linhas pretendermos, até no meio de instruções, que isso não vai afectar nada pois o “;” é que “manda”

mais uma nota isto funciona para declaração de funções mas não declaração de directivas para o pré-processamento, que são aquelas que não necessitamos do “;”

Explicando Bits e Bytes

Podemos pensar na memória do computador como uma fita, uma grande fita feita de frames sequenciais. Em cada um desses frames, podemos colocar uma certa voltagem: tem voltagem ou não tem voltagem: se tem voltagem associamos o valor 1, se não tem voltagem associamos o valor 0. Daí termos a linguagem binária de zeros e uns.

Agora podemos fazer combinações se tivermos posição de zeros e uns, da direita para a esquerda.

0 1 10 11 100 101 110 111 …

E na verdade podemos estender este conceito para um número infinito de combinações. Ora o que aconteceu é que nos bastavam pouco menos de 256 combinações (8 bits ordenados) para termos uma combinação para cada letra, maiúscula e minúscula, número, pontos de exclamação, interrogação, etc …e isso era o suficiente para a nossa comunicação. mas para haver um certo consenso para que uma dada combinação desse um dado símbolo surgiu a tabela ASCII (surgiram outras tabelas quando se quis colocar os símbolos de outras línguas, como o japonês ou o chinês – ver tabela ISO) Portanto com 8 bits ou 8 casas conseguíamos ter qualquer símbolo que utilizamos. A esse conjunto de 8 bits chamamos de byte, mais convenientemente. Portanto, um byte tem 8 casas de zeros - uns , ou seja 28 que dá as 256 combinações. E o byte é a unidade básica que o C++ consegue operar.

Então antes podíamos pensar na memória do PC como um conjunto de n bits ordenados de memória, Agora podemos pensar em n/8 bytes de memória. Temos 1º byte, 2º byte,….m byte. dito de outra forma, tínhamos uma única fita composta de bits. agora temos muitas fitinhas de bytes.



Tipologia de variáveis – Bytes e variáveis e partição em vários sectores da memória

Quando queremos guardar um valor o sistema operativo vai reservar uma quantidade x de bytes, numa posição dada na memória. A quantidade reservada vai depender do que declararmos que queremos guardar. Recorde-se que gravar o valor 5 é diferente de ter 5,67. Por isso é natural que ocupe mais espaço em termos de memória. (é natural que quanto maior for o número seja necessário mais combinações de bits e bytes) Portanto vou necessitar de apenas 1 byte para guardar uma letra que denominamos de char vou necessitar 4 bytes para poder ter de 0 to 4294967295. …

C++ tem 5 tipos básicos:

  1. char,
  2. int,
  3. float,
  4. void,
  5. double


a tabela seguinte mostra os vários tipos básicos que existem:


tipos de dados fundamentais
Nome Descrição tamanho*Variação
char Character or small integer. 1byte signed: -128 to 127; unsigned: 0 to 255
intInteger 1word signed: -2147483648 to 2147483647; unsigned: 0 to 4294967295
short int short Short Integer.2bytessigned: -32768 to 32767;unsigned: 0 to 65535
long int longLong integer.4bytessigned: -2147483648 to 2147483647unsigned: 0 to 4294967295
bool Boolean value.It can take one of two values: true or false.1bytetrue or false
floatFloating point number.4bytes3.4e +/- 38 (7 digits)
doubleDouble precision floating point number.8bytes1.7e +/- 308 (15 digits)
long doubleLong double precision floating point number.8bytes 1.7e +/- 308 (15 digits)
wchar_tWide character.2bytes1 wide character


Para os números reais temos as seguintes variantes:

  • float
  • double
  • long double

Para os inteiros:

  • int
  • short int
  • long int
  • unsigned
  • signed

Vantagem - Divisão da memória em partições

Até agora descriminámos as nossas necessidades de variáveis. Agora em vez de dividirmos a memória na unidade máxima (tipo float) preferimos discriminar e ter uma secção da memória dedicada apenas a cada tipo de variável ou de dados. Ou seja poderíamos imaginar a memória a alocar espaço de memória á medida que íamos necessitar, se primeiramente necessitássemos de um int, e depois de um float. poderíamos imaginar ter uma alocação sequencial, ou seja da do byte x até x+2 tinhamos o int e logo depois tínhamos o float. o que acontece é que é preferível fazer divisão da memória em secções. cada secção dedicada apenas a uma determinada tipologia de variável. Ou seja se olharmos para a fita da memória em ver de termos por hipótese: ….int, float int…. ele reúne por tipologia …int, int, float…

se voltarmos a anologia das fitinhas,

a fita de de bits é dividida em secções e na parte reservada á declaração de variáveis o que ele faz e dividir esse espaço em varias partes atribuindo a cada uma a tipologia de variáveis, é como tivéssemos várias fitas, uma int, outra float,etc..e depois colámos umas ás outras. Essa fita resultante seria uma fita que é apenas aquela reservada á declaração de variáveis.

Pergunta: serão todas as fitas do mesmo tamanho?? por acaso não sei, mas não devem ter todas o mesmo tamanho de bytes, até porque se cada tipologia ocupa um tamanho diferente, iríamos ter no máximo x quantidade de ints e uma quantidade menor de floats.. mas por outro lado nós usamos mais ints do que floats. qual o critério? não sei.

Pergunta: Haverá realmente vantagem em termos discriminação por fitas e fitinhas?? uma resposta a isto é que agora sabemos que logo a seguir a um int está outro int e não outra qualquer variável.

  • Isto dá uma vantagem quando queremos fazer o acesso a uma dada variável,
  • podemos passar de um int para o int seguinte sabendo que temos apenas 4 bits de diferença.

esta justificação está correcta mas não se fica inteiramente com a ideia. mas isto será novamente abordado quando dermos os ponteiros. aqui é que se fica inteiramente com a ideia.



Declarando variáveis

Vai-nos ocorrer a necessidade do programa guardar certos valores. Assim vamos querer declarar uma variável para depois guardar um valor e mais tarde poder ser utilizado

a sua forma geral é: tipo_da_variável lista_de_variáveis;


Para poder declarar uma variável podemos escrever

int a;

Neste caso apenas estamos a declarar uma variável que chamamos “a”. O que é que está a acontecer? Estamos a pedir ao computador para reservar um espaço da sua memória. e que depois associe esse espaço de memória ao nome “a”. Assim o computador tem de saber onde aloca as coisas. É como tivéssemos um quadro de duas colunas, uma para os nomes e outra para o valor.

a=5;

Neste caso estamos a fazer com que o computador guarde o valor 5 no espaço alocado á variável a;

Como primeiro pedimos para reservar memória e depois escrevemos o valor, por isso é que temos que dizer o quanto espaço de memória é que deve ser reservado daí o int antes de a; Agora o que aconteceria se puséssemos a=5,555.. não sei qual é que seria o resultado. Mas ou não compilaria, ou se isso acontecesse estaríamos a alocar mais memória do que pedimos e portanto poderia acontecer estarmos a escrever em memória que pertencia a outro programa, logo podemos estar a corromper os nossos dados, penso que não é coisa que se queira.

Podemos declarar uma variável ao mesmo tempo que lhe damos um valor, por exemplo:

int a=5;

Podemos ainda declarar quantas variáveis queiramos ao mesmo tempo;

int a, b,c,d;	

têm é que ter as vírgulas no meio e ponto e virgula no final


Podemos declarar várias variáveis ao mesmo tempo e associar os respectivos valores?

int a=5, b=6; 		

sim também podemos fazer isso, não esquecer as virgulas.

Também podemos fazer a iniciação das variáveis da seguinte forma:

int a (0);


  • Lembre-se sempre que é necessário declarar a variável antes de atribuirmos um valor, caso contrário esse valor irá para um sítio qualquer na memória e é bem possível alterar uma memória já atribuída.
  • E declarar a variável antes de utilizarmos.
  • O nome das variáveis terão de ser únicas, como é lógico!
  • Há diferença entre termos maiúsculas e minúsculas (pois elas têm um valor diferente no código ASCII) logo ter – nome e NOME ou mesmo Nome são todas variáveis diferentes – o C++ É CASE SENSITIVE
  • Por fim não podem ter o nome das palavras reservadas que são os nomes das funções e tipologia, (ver quadro)
  • Utiliza-se as letras números e o sublinhado (_)
  • Variáveis até 32 caracteres são aceites

• É prática comum usar letras minúsculas para nomes de variáveis e maiúsculas para nomes de constantes.


Palavras Reservadas do C

int,char,float, double short, long, signed, unsigned, void, bool, wchar_t
for, if, else, goto, return, do, while, break, switch, case, continue
const_cast, reinterpret_cast dynamic_cast, static, static_cast
enum, struct, union, class, public, private, protected, template
asm,catch,typename, typedef, inline,default, new, delete, explicit, export,

extern, friend,,mutable, namespace, operator, register, true,false, const, auto, sizeof, , this, throw, try, typeid, using, virtual, volatile, and, and_eq, bitand, bitor, compl, not, not_eq, or, or_eq, xor, xor_eq

Convém memorizar ou ficar muito familiarizado com estas palavras porque se assim o fizermos já temos muito caminho percorrido.


Posso ter então algo unsigned short int NumberOfSisters; signed int MyAccountBalance; por definição, se não especificarmos se é signed ou unsigned a maior parte dos compiladores vai assumir que é signed.

o short e o long podem ser logo usados que será assumido que falam de ints. logo as declarações em baixo são equivalentes short Year; short int Year;


CONSTANTS

Constantes são expressões com valor fixo. é fixo no sentido em que quando o programa após ser compilado e estiver a ser executado esse valor não pode ser alterado. claro que poderemos alterar o valor no source code, ie, enquanto estivermos a programar.

temos as constantes literais que é o caso de darmos um valor fixo a uma variável a=5;

depois temos as constantes tipo caracteres do tipo 'z' 'p' "Hello world" "How do you do?"

  • quando temos apenas 1 letra temos o ‘’
  • quando temos mais do 1 letra temos “”

portanto ter x e ‘x’ é diferente! a 1ª é uma variável e a segunda é uma letra (que é uma constante)

Formatação de caracteres

Nós podemos formatar as constantes que são strings. não é apenas formatar mas também para inserir certos símbolos que se não recorrermos a uma sequencia de símbolos, o compilador irá interpretar de uma outra forma. É melhor ver…

\n newline
\r carriage return
\t tab
\v vertical tab
\b backspace
\f form feed (page feed)
\a alert (beep)
\’ single quote (')
\” double quote (")
\? question mark (?)
\\ backslash (\)


Exemplos:

“frase expressa em duas\
linhas”


“isto forma” “apenas um linha de caracteres”

Operações - operadores

de que é que nos servia guardar valores se não temos a possibilidade de os trabalhar? realmente fazer operações dão bastante jeito. com estas armas podemos fazer operações entre valores, Variáveis (e os seus valores) e ainda os endereços das variáveis.

Operadores Aritméticos e de Atribuição

OPERADORACÇÃO
+ Soma (inteira e ponto flutuante)
- Subtração ou Troca de sinal (inteira e ponto flutuante)
* Multiplicação (inteira e ponto flutuante)
/ Divisão (inteira e ponto flutuante)
 % Resto de divisão (de inteiros)
++ Incremento (inteiro e ponto flutuante)
-- Decremento (inteiro e ponto flutuante)

Operadores Relacionais e Lógicos

OPERADORACÇÃO
> Maior do que
>= Maior ou igual a
< Menor do que
<= Menor ou igual a
== Igual a
!= Diferente de


OPERADORACÇÃO
&& AND (E)
|| OR (OU)
 ! NOT (NÃO)



Operadores Lógicos Bit a Bit

OPERADORACÇÃO
& AND
* OR
^ XOR (OR exclusivo)
~ NOT
>> Deslocamento de bits à direita
<< Deslocamento de bits à esquerda


Expressão Original Expressão Equivalente
x=x+k; x+=k;
x=x-k; x-=k;
x=x*k; x*=k;
x=x/k; x/=k;
x=x>>k; x>>=k;
x=x<<k; x<<=k;
x=x&k; x&=k;
x=x *(y+a) x*=y+a
x=x+1 X++


  • repare no valor = = quando queremos comparar. no C o = é associativo, ie, é como dizer atribui o valor. por isso temos o igual igual para fazer a distinção quando queremos é comparar os valores.


  • repare na regra da direita para a esquerda, a operação de atribuição vai sempre da direita para a esquerda
a = 2 + (b = 5);

é equivalente a: b = 5; a = 2 + b;

a seguinte expressão também é válida a = b = c = 5;


alguns exemplos

 (7 == 5)     // evaluates to false.
 (5 > 4)      // evaluates to true.
 (3 != 2)     // evaluates to true.
 (6 >= 6)     // evaluates to true.
 (5 < 5)      // evaluates to false.

suponhamos que tinhamos a=2 b=3 and c=6,

(a == 5)     // evaluates to false since a is not equal to 5.
(a*b >= c)   // evaluates to true since (2*3 >= 6) is true. 
(b+4 > a*c)  // evaluates to false since (3+4 > 2*6) is false. 
((b=2) == a) // evaluates to true.



!(5 == 5)    // evaluates to false because the expression at its right (5 == 5) is true. 
!(6 <= 4)    // evaluates to true because (6 <= 4) would be false. 
!true        // evaluates to false
!false       // evaluates to true.


((5 == 5) && (3 > 6) )  // evaluates to false ( true && false ).
((5 == 5) || (3 > 6) )  // evaluates to true ( true || false ).

OPERADOR TYPE CASTING

Type casting permite converter um dado tipo de variavel (int, float, …) num outro.

  • Uma maneira é preceder
int i;
float f = 3.14;
i = (int) f;
converte o float em inteiro, retornando o valor 3. O restante é perdido, é chamado truncar.
  • Uma outra maneira é
i = int ( f );
ambas as maneiras são validadas em c++

Ora bem já temos a tipologia de variáveis e agora mesmo as operações aritméticas, convém agora explicar o ponto comum entre as duas. Se eu tiver a=10/3, o resultado sei que é 3,(3). Ou seja a divisão de dois números inteiros deu um número real. Acontece que se eu declarar int a; Estou a dizer que ele será inteiro, o resultado seria 3. Mesmo que eu diga float a; o resultado continua a ser 3 mas desta vez, 3,0000 Ou seja tenho tenho de converter um dos inteiros (pelo menos) em float, (que o outro se converte por prédefinição). Então poderia fazer a=(float)10/3;

É importante lembrar que a conversão de tipos também é válida para tipos definidos pelo usuário e objetos.

(#DEFINE) PARA CONSTANTES

#define PI 3.14159265
#define NEWLINE '\n'


se colocarmos estas linhas no header, o que vai acontecer é o seguinte O preprocessador vai verificar o nosso source code e sempre que encontrar a directiva #define vai literalmente substituir cada ocorrência do identificador no source code pelo valor definido.

A vantagem disto é que

  • podemos ter um identificador ao nosso gosto, e sempre que necessitarmos do valor escrevemos o identificador, em vez do valor, até porque se o valor fosse complicado poderíamos enganar-nos a escrever. Claro que nos poderíamos enganar também a escrever o identificador, daí a escolhermos um nome familiar.
  • E se necessitarmos de alterar o valor, alteramos apenas 1 vez, em vez de todas as vezes onde apareceria o valor.


O formato geral é:

#define identificador valor 

Repare que a directiva de preprocessador não tem o “;”

// defined constants: calculate circumference
#include <iostream>
using namespace std;
#define PI 3.14159
#define NEWLINE '\n';
int main ()
{
 double r=5.0;               // radius
 double circle;
 circle = 2 * PI * r;
 cout << circle;
 cout << NEWLINE;
 system (“pause”);
 return 0;
 }
31.4159

(CONST)DECLARAR CONSTANTES

nós podemos transformar uma variavel numa constante do género:

const int pathwidth = 100;
const char tabulator = '\t';
const zipcode = 12440;

Com o prefixo “const”, dizemos que a variável não poderá alterar o seu valor. Repare que se fosse uma variável eu poderia ter: int a=5; e logo a seguir dizer int a=6; e o valor do a ficava com o valor de 6; agora com o prefixo “const” eu não poderei alterar o valor, porque irá dar erro no compilador

OPERADOR CONDICIONAL ( ? )

condition ? result1 : result2

O operador condicional avalia se a condição é verdadeira então a expressão irá retornar o return1; se for falso retorna o valor 2.

7==5 ? 4 : 3     // returns 3, since 7 is not equal to 5.
7==5+2 ? 4 : 3   // returns 4, since 7 is equal to 5+2.
5>3 ? a : b      // returns the value of a, since 5 is greater than 3.
a>b ? a : b      // returns whichever is greater, a or b.
// conditional operator
#include <iostream>
using namespace std;
int main ()
{
int a,b,c;
a=2;
b=7;
c = (a>b) ? a : b;
cout << c;
system (“pause”);
return 0;
}
7

OPERADOR VIRGULA ( , )

O operador virgula (,) é usado para separar 2 ou mais expressões onde se espera apenas 1 expressão. Quando tivermos várias expressões estiverem a ser avaliadas para um valor, a expressão mais á direita é a que é considerada.

a = (b=3, b+2);

neste exemplo iria colocar o valor 3 em b e depois adicionaria 2 ao valor de b ficando a com 5 e b com 3

SIZEOF()

a = sizeof (char);

este operador aceita um operador que pode ser uma tipologia de variavel ou mesmo a variavel. e retorna o tamanho de bytes reservados á tipologia ou á variável. no exemplo associa o valor 1 a “a” porque é um char. este é relativamente simples, mas dá jeito.

PRECEDÊNCIA DE OPERADORES

Vejamos a expressão

a = 5 + 7 % 2

deveremos interpreter assim ou assado?

a = 5 + (7 % 2)    // with a result of 6, or
a = (5 + 7) % 2    // with a result of 0

Seguindo a nossa prioridade incutida das lições de matemática, sabemos que o resultado deveria ser o primeiro. a criação de uma ordem de prioridade de operadores em programação tem lógica se queremos que a linguagem se aproxime com a nossa linguagem corrente. por outro lado o facto de termos uma ordem de prioridade de símbolos faz com que a leitura do código não seja sequencial e nos permita ter alguma flexibilidade de escrita. se uma pessoa tiver duvidas pode sempre recorrer ao topo e ao fim da hierarquia utilizando os símbolos que estão no topo eg. a=10+(2*3). em caso de igualdade de precedência predomina a sequencia de enumeração, ou seja o que vem antes …

LEVELOPERADORDESCRIÇÃOGROUPING
1:: scope Left-to-right
2() [] . -> ++ -- dynamic_cast static_cast reinterpret_cast const_cast typeid postfix Left-to-right
3++ -- ~ ! sizeof new delete* &+ - unary (prefix) Right-to-left
4(type) type casting Right-to-left
5.* ->* pointer-to-member Left-to-right
6* / % multiplicative Left-to-right
7+ - additive Left-to-right
8<< >> shift Left-to-right
9< > <= >= relationalLeft-to-right
10== != equality Left-to-right
11& bitwise AND Left-to-right
12^ bitwise XOR Left-to-right
13 bitwise OR Left-to-right
14&&Logical AND Left-to-right
15 logical OR Left-to-right
16?: conditional Right-to-left
17= *= /= %= += -= >>= <<= &= ^= != assignment Right-to-left
18, comma Left-to-right

BASIC INPUT/OUTPUT

COUT -Standard Output

CIN - Standard Input

string streams

Estruturas de controle

As estruturas de controle são utilizadas para tomar decisões e/ou criar áres de código que serão repetidas até que determinada situação desejada aconteça.

Em geral as estruturas de controle utilizam uma expressão que é avaliada e o resultado é um valor booleano (verdadeiro ou falso), definindo se o bloco de código contido na estrutura deve ser executado ou não. Um excessão a essa regra é a estrutura SWITCH, que utiliza um valor para definir qual bloco de código a ser executado. Verifique sua sintaxe abaixo para um melhor entendimento.

Expressão

Uma expressão é um conjunto de elementos (contantes, variáveis, chamadas para funções ou métodos, etc...) que possam produzir um resultado capaz de ser avaliado como verdadeiro ou falso. Tais expressões são formadas por um ou mais elementos, ligados por operadores matemáticos (soma, subtração, divisão, etc...), lógicos (concatenação ou comparativos) (e, ou, igual a, negação, etc...) ou binários (shift, ou binário, e binário, etc...).

A definição padrão para as comparações é:

FALSO:

  • Matemático: 0
  • Caracter: "\0"
  • Outros: NULL, false

VERDADEIRO:

Qualquer coisa que não seja falsa.

A definição dos operadores booleanos nativos (true / false) é:

#define false 0
#define true !false

Exemplos de expressões:

( i + j == 3)
/* será verdadeiro se o valor de "i" mais o valor de "j" for
 * exatamente 3 (ou 3.0, caso número real)
 */
( i - b )
/* será falso caso o valor de "i" menos o valor de "b" for
 * exatamente 0 (zero)
 */
( i % 2 )
/* será verdadeiro caso a exista resto na divisão de "i"
 * pelo valor 2. NOTA: Esta comparação é muito utilizada
 * para checar se um valor é par ou ímpar. Valores que não
 * tem resto na divisão por 2 são SEMPRE par.
 */
( i < 4231.312 )
/* será verdadeiro caso o valor da variável "i" for menor
 * que 4231.312
 */

estrutura condicional - IF ... ELSE

A estrutura IF ELSE é utilizada para tomadas de decisões. A análise da expressão define se o bloco da estrutra deve ser executado ou não. Caso não seja executado, opcinalmente pode haver um bloco alternativo de execução.

Sintaxe:

// Decisão simples. Se a expressão for verdadeira, o código A é executado.
if( [EXPRESSÃO] ) {
  [CÓDIGO A]
}

/* Decisão com dois caminhos. Se a expressão for verdadeira,
 * o código A é executado, senão, o código B é executado.
 */
if( [EXPRESSÃO] ) {
  [CÓDIGO A]
}
else {
  [CÓDIGO B]
}
// Estruturas aninhadas.
if( [EXPRESSÃO A] ) {
  [CÓDIGO A]
}
else if( [EXPRESSÃO B] ) {
  [CÓDIGO B]
}
else if( [EXPRESSÃO C] ) {
  [CÓDIGO C]
}
else {
  [CÓDIGO D]
}

Exemplos:

if(true) {
  cout << "Este código sempre é executado" << endl;
}
if( 1+2 == 1) {
  cout << "Este código nunca é executado" << endl;
}
else {
  cout << "Como a exmpressão acima sempre será falsa, este código será executado" << endl;
}

loop - WHILE

Enquanto a análise da expressão for resultante em verdadeiro, o bloco de código é repetido.

Sintaxe:

while( [EXPRESSÃO] ) {
  [CÓDIGO]
}

Exemplos:

while( true ) {
  cout << "Loop infinito." << endl;
}

int i = 0; // uma variável para contar iterações
while( i < 5) { // enquanto i for menor que 10
  cout << "O valor de i é: " << i << endl;
  i++;
}
/* Saída deste trecho:
 * ----- ----- -------
 * O valor de i é: 0
 * O valor de i é: 1
 * O valor de i é: 2
 * O valor de i é: 3
 * O valor de i é: 4
 */

loop - DO ... WHILE

Semelhante ao WHILE, porém, esta estrutura executa o código e depois avalia a expressão, ou seja, ela obriga que o código seja executado ao menos uma vez. A vantagem desta estrutura é não exigir uma pré-condição para a execução, tendo em vista que o código tem que ser executado ao menos uma vez, onde pode ser criada a condição para avaliação.

Sintaxe:

do {
  [CÓDIGO]
} while( [EXPRESSÃO] );

Exemplos:

do {
  cout << "Loop infinito." << endl;
} while( true );

int i = 0; // iterador
do {
  cout << "O valor de i é: " << i << endl;
  i++;
} while(i < 5);
/* Saída deste trecho:
 * ----- ----- -------
 * O valor de i é: 0
 * O valor de i é: 1
 * O valor de i é: 2
 * O valor de i é: 3
 * O valor de i é: 4
 */

loop - FOR

Normalmente utilizado em estruturas de iteração finita e bem definida, onde se define um valor inicial de iteração, uma expressão e um modo de alteração do valor inicial.

Loops inifinitos utilizando a estrutura for são mais rápidos que utilizando a estrutura while ou do ... while, logo, se este for o caso, implementações utilizando o for são preferíveis.

Sintaxe:

for([INICIALIZAÇÃO]; [EXPRESSÃO]; [ALTERAÇÃO]) {
  [CÓDIGO]
}

Exemplos:

// note que não há inicialização, expressão e alteração.
for(;;) {
  cout << "Loop infinito." << endl;
}

for(int i = 0; i < 5; i++) {
  cout << "O valor de i é: " << i << endl;
}
/* Saída deste trecho:
 * ----- ----- -------
 * O valor de i é: 0
 * O valor de i é: 1
 * O valor de i é: 2
 * O valor de i é: 3
 * O valor de i é: 4
 */

Note que o iterador (i) foi inicializado na primeira etapa da estrutura for, a expressão definida na segunda e por fim há um incremento sobre o iterador.

for(int i = 0; i < 5; i+=2) {
  cout << "O valor de i é: " << i << endl;
}
/* Saída deste trecho:
 * ----- ----- -------
 * O valor de i é: 0
 * O valor de i é: 2
 * O valor de i é: 4
 */

Observe que temos a mesma estrutura, porém com o incremento modificado para ser feito de dois em dois.

for(int i = 5; i < 0; i--) {
  cout << "O valor de i é: " << i << endl;
}
/* Saída deste trecho:
 * ----- ----- -------
 * O valor de i é: 5
 * O valor de i é: 4
 * O valor de i é: 3
 * O valor de i é: 2
 * O valor de i é: 1
 */

Novamente, porém o incremento é negativo (subtração), o que nos leva a um loop inverso.

Nota importante: As estruturas WHILE, DO ... WHILE e FOR seguem o mesmo propósito e em geral qualquer código criado em qualquer uma delas, pode ser criado utilizando outra.

jump statements - BREAK

Sai imediatamente de uma estrutura de controle. A instrução BREAK normalmente é utilizada para detectar execuções anormais, que devem parar a execução do bloco de código. Outro uso comum é em estruturas SWITCH, onde a execução de uma opção selecionada deve ser interrompida por uma instrução BREAK.

Sintaxe:

break;

Exemplo:

int i = 0;
while(true) { // loop infinito
  if( i == 5 ) {
    break;
  }
  cout << "O valor de i é: " << i << endl;
  i++;
}
/* Saída deste trecho:
 * ----- ----- -------
 * O valor de i é: 0
 * O valor de i é: 1
 * O valor de i é: 2
 * O valor de i é: 3
 * O valor de i é: 4
 */

Exemplos:

jump statements - CONTINUE

jump statements - GOTO

jump statements - EXIT

estrutura selectiva- SWITCH

Estrutura de controle baseada em seleção. O SWITCH recebe um valor e seleciona o caso que se enquadra para este valor.

Podemos criar a estrutura com ou sem um bloco de código de execução padrão, o qual será executado caso nenhum dos outros blocos definidos seja executado.

Sintaxe:

// sem bloco de código padrão
switch( [VALOR] ) {
  case [COMPARATIVO A]:
    [CÓDIGO]
    break;
  case [COMPARATIVO B]:
    [CÓDIGO]
    break;
}
// com bloco de código padrão
switch( [VALOR] ) {
  case [COMPARATIVO A]:
    [CÓDIGO A]
    break;
  case [COMPARATIVO B]:
    [CÓDIGO B]
    break;
  default:
    [CÓDIGO C]
    break;
}

Exemplo:

int i = 12;

switch( i ) {
  case 3:
    cout << "Este caso não será executado neste exemplo" << endl;
    break;
  case 12:
    cout << "Este caso será executado neste exemplo" << endl;
    break;
  default:
    cout << "Este caso não será executado neste exemplo" << endl;
    break;
}

Cuidado: Caso a instrução BREAK não seja utilizada no fim de um caso, o caso seguinte também será executado. Nem sempre isso é desejável.

Exemplo:

int i = 3;

switch( i ) {
  case 3:
    cout << "Este caso será executado neste exemplo" << endl;
  case 12:
    cout << "Este caso também será executado neste exemplo" << endl;
    break;
  default:
    cout << "Este caso não será executado neste exemplo" << endl;
    break;
}

Note que o caso 3 e o 12 serão executados, devido ao caso 3 não ter a instrução BREAK no final de seu bloco de código.

Nota importante: As estruturas SWITCH são mais rápidas que as estruturas IF ... ELSE, logo, se algum tipo de decisão pode ser definido com estruturas SWITCH, utilize-a.

FUNÇÕES

VARIAVEIS LOCAIS VS GLOBAIS

ARGUMENTOS POR VALOR E POR REFERÊNCIA

ARGUMENTOS DE VALOR POR DEFAUT

VOID

OVERLOAD FUNCTIONS

INLINE FUNCTIONS

RECURSIVIDADE

DECLARAR FUNÇÕES

ARRAYS

INICIALIZANDO ARRAYS

ACEDER VALORES EM ARRAYS

ARRAYS MULTIDIMENSIONAIS

SEQUENCIA DE CARACTERES

2ª PARTE

PONTEIROS

FITAS E PONTEIROS

O OPERADOR REFERENCIA &

O OPERADOR DE DERREFERÊNCIA (*)

DECLARAR VARIAVEIS PONTEIROS

PONTEIROS E ARRAYS

INICIAÇÃO DE PONTEIROS

ARITMÉTICA DE PONTEIROS

PONTEIROS PARA PONTEIROS

PONTEIROS VOID

PONTEIROS NULOS

PONTEIROS PARA FUNÇÕES

MEMÓRIA DINÂMICA

OPERADOR NEW

OPERADOR DELETE

ENUMERAÇÕES - ENUM

ESTRUTURAS - STRUCT

ESTRUTURAS DE DADOS

PONTEIROS PARA ESTRUTURAS

NESTING ESTRUTURAS

UNIÕES - UNIONS

UNIOES SIMPLES

UNIÕES ANÓNIMAS

CLASSES

CONSTRUCTURS DESTRUCTORS

OVERLOADING CONSTRUCTURS

DEFAUT CONSTRUCTURS

PONTEIROS PARA CLASSES

OVERLOADING OPERATORS

THIS

STATIC MEMBERS

Amizade e Herança (Friends e Inheritance)

Funções Friend

Classes Friend

Herança entre classes

Herança Múltipla

Polimorfismo (Polymorphism)

Ponteiros para Classe Base

Funções virtuais

Classes Base Abstratas

Gabaritos (Templates)

Gabaritos de Função

Funções sobrecarregadas são normalmente usadas para executar operações semelhantes sobre tipos de dados diferentes. Se as operações são idênticas para cada tipo, isto pode ser executado mais compacta e convenientemente usando-se gabaritos de função. O programador escreve uma única definição do gabarito da função. Baseado nos tipos de argumentos fornecidos explicitamente ou inferidos das chamadas para esta função, o compilador gera funções separadas, no código objeto, para tratar cada tipo de chamada apropriadamente. Em C, esta tarefa pode ser executada usando-se macros criadas com a diretiva #define do pré-processador. Porém, macros apresentam a possibilidade de sérios efeitos colaterais e não possibilitam ao compilador executar uma verificação de tipo. Os gabaritos de funções fornecem uma solução compacta como as macros, mas possibilitam uma verificação de tipo completa.

Todas as definições de gabaritos de funções começam com a palavra-chave template seguida por uma lista de parâmetros de tipo formais para o gabarito de funções, incluso entre os sinais de maior e menor (< e >); cada parâmetro formal de tipo deve ser precedido pela palavra-chave class ou typename, como é mostrado abaixo:

template <class T>
template <typename ElementType>
template <class BorderType, class FillType>

Os parâmetros de tipo formais de uma definição de gabarito são usados (como seriam com parâmetros de tipos primitivos ou tipos definidos pelo usuário) para especificar os tipos dos parâmetros da função, especificar o tipo de retorno da função e para declarar variáveis dentro da função. A definição da função vem a seguir e é definida como qualquer outra função. Note que a palavra-chave class ou typename usada para especificar parâmetros de tipo do gabarito de função na realidade significa "qualquer tipo primitivo ou tipo definido pelo usuário".

Examinaremos o gabarito de função printArray no exemplo abaixo. O gabarito de função é usado em um programa completo posteriormente.

template <class T>
void printArray(const T *array, const int count)
{
   for (int i=0; i<count; i++)
      cout << array[i] << " ";

   cout << endl;
}

O gabarito de função printArray declara um parâmetro formal único T (T poderia ser qualquer identificador válido) para o tipo do array a ser impresso pela função printArray; T * é chamado de parâmetro de tipo. Quando o compilador encontrar uma chamada para a função printArray no código-fonte do programa, o tipo do primeiro parâmetro de printArray é substituído por T na definição do gabarito e C++ cria uma função gabarito completa para imprimir um array do tipo de dados especificado. Então, a função recém-criada é compilada. No exemplo que será apresentado posteriormente, três funções printArray são instanciadas - uma espera um array int, uma espara um array double e uma espera um array char. Por exemplo, a instanciação para o tipo int é:

void printArray(const int *array, const int count)
{
   for (int i=0; i<count; i++)
      cout << array[i] << " ";

   cout << endl;
}

Cada parâmetro de tipo formal, em uma definição de gabarito de função, deve normalmente aparecer na lista de parâmetros da função pelo menos uma vez. O nome de um parâmetro de tipo formal poder ser usado somente uma vez na lista de parâmetros de um cabeçalho de gabarito. Nomes de parâmetros de tipo formais entre funções gabarito não precisam ser únicos. O exemplo que é mostrado logo mais a frente ilustra o uso do gabarito de função printArray. O programa começa por instanciar int array a, array double b e char array c, de tamanhos 5, 7, e 4, respectivamente. A seguir, cada um dos arrays é impresso chamando printArray - uma vez com um primeiro parâmetro a do tipo int *, uma vez com um primeiro parâmetro b do tipo double * e uma vez com um primeiro parâmetro c do tipo char *. A chamada

printArray(a, aCount);

por exemplo, faz com que o compilador infira que T é int e instancie uma função gabarito printArray cujo parâmetro de tipo T é int. A chamada

printfArray(b, bCount);

faz com que o compilador infira que T é double e instancie uma segunda função gabarito printArray cujo parâmetro de tipo T é double. A chamada

printArray(c, cCount);

faz com que o compilador infira que T é char e instancie uma terceira função gabarito printArray cujo parâmetro de tipo T é char.

// Usando funções gabarito
#include <iostream>

using std::cout;
using std::endl;

template <class T>
void printArray(const T *array, const int count)
{
   for (int i=0; i<count; i++)
      cout << array[i] << " ";

   cout << endl;
}

int main()
{
   const int aCount = 5, bCount = 7, cCount = 4;
   int a[aCount] = { 1, 2, 3, 4, 5 };
   double b[bCount] = { 1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7 };
   char c[cCount] = "ALÔ"; // quarta posição reservada para o caractere nulo

   cout << "Array a contém:" << endl;
   printArray(a, aCount); // função gabarito para inteiros

   cout << "Array b contém:" << endl;
   printArray(b, bCount); // função gabarito para doubles

   cout << "Array c contém:" << endl;
   printArray(c, cCount); // função gabarito para caracteres

   return 0;
}
-> Saída gerada pelo código-fonte anterior:
Array a contém:
1 2 3 4 5
Array b contém:
1.1 2.2 3.3 4.4 5.5 6.6 7.7
Array c contém:
A L Ô

Sobrecarregando Gabaritos de Função

Gabaritos de Classe

Gabaritos de Classe e parâmetros não-tipo

Gabaritos e projetos multi-arquivo

NAMESPACES

USING

NAMESPACE ALIAS

NAMESPACE STD

EXCEPTIONS

STANDARD EXCEPTIONS

EXCEPTIONS ESPECIFICAÇÕES

TYPECASTING - CONVERSÃO IMPLICITA

TYPECASTING - CONVERSÃO eXPLICITA

dynamic_cast

static_cast

reinterpret_cast

const_cast

typeid

DIRECTIVAS DO PRÉPROCESSADOR

#DEFINE, #UNDEF - DEFINIÇÕES MACRO

IFDEF, #IFNDEF, #IF, #ENDIF, #ELSEand #ELIF- INCLUSÕES CONDICIONAIS

#LINE- CONTROLO DE LINHA

#ERROR - DIRECTIVA DE ERRO

#INCLUDE - DIRECTIVA DE INCLUSÃO DE FICHEIRO FONTE

#PRAGMA - DIRECTIVA PRAGMA

NOMES DE MACRO PRÉDEFINIDOS

INPUT/OUTPUT COM FICHEIROS

ABRIR UM FICHEIRO

FECHAR UM FICHEIRO

FICHEIROS TEXTO

VERIFICAR FLAGS DE ESTADO

get and put stream pointers

tellg() and tellp()

seekg() and seekp()

FICHEIROS BINÁRIOS

BUFFER E SINCRONIZAÇÃO

Fonte: Wikibooks

Links Externos

Bibliotecas

Interface

XML

Sockets

Ferramentas pessoais