CPP
De ROMHackingWiki
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:
- char,
- int,
- float,
- void,
- 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 |
| int | Integer | 1word | signed: -2147483648 to 2147483647; unsigned: 0 to 4294967295 |
| short int short | Short Integer. | 2bytes | signed: -32768 to 32767;unsigned: 0 to 65535 |
| long int long | Long integer. | 4bytes | signed: -2147483648 to 2147483647unsigned: 0 to 4294967295 |
| bool | Boolean value.It can take one of two values: true or false. | 1byte | true or false |
| float | Floating point number. | 4bytes | 3.4e +/- 38 (7 digits) |
| double | Double precision floating point number. | 8bytes | 1.7e +/- 308 (15 digits) |
| long double | Long double precision floating point number. | 8bytes | 1.7e +/- 308 (15 digits) |
| wchar_t | Wide character. | 2bytes | 1 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
| OPERADOR | ACÇÃ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
| OPERADOR | ACÇÃO |
| > | Maior do que |
| >= | Maior ou igual a |
| < | Menor do que |
| <= | Menor ou igual a |
| == | Igual a |
| != | Diferente de |
| OPERADOR | ACÇÃO |
| && | AND (E) |
| || | OR (OU) |
| ! | NOT (NÃO) |
Operadores Lógicos Bit a Bit
| OPERADOR | ACÇÃ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 …
| LEVEL | OPERADOR | DESCRIÇÃO | GROUPING | |
| 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 | < > <= >= | relational | Left-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

