Há duas operações aritméticas que podemos efetuar com ponteiros: adição de ponteiros e subtração de ponteiros. Além disso, também é possível comparar dois ponteiros entre si.
O tamanho de uma variável é um aspecto muito importante para o programador. Precisamos saber quanto espaço na memória uma variável ocupa quando é utilizada, para que seja possível escrever programas enxutos e que executem com boa performance. Isso é particularmente importante quando desenvolvemos programas para dispositivos embarcados, os quais, no geral, contam com quantidade restrita de memória disponível.
Dependendo da plataforma, uma variável pode ter tamanhos que variam de 8 a 64 bits. Por exemplo, em uma plataforma de 32 bits, uma variável do tipo int ocupará o espaço de 4 bytes (32 / 8 = 4).
O código a seguir nos permite verificar o tamanho na memória utilizado por ponteiros de diferentes tipos, usando o operador sizeof:
int main() {
int inteiro;
char caractere;
double duplo;
int *iPtr;
char *cPtr;
double *dPtr;
iPtr = &inteiro;
cPtr = &caractere;
dPtr = &duplo;
cout << "Tamanho de um ponteiro de inteiro = " << sizeof(iPtr) << ", valor = " << iPtr << endl;
cout << "Tamanho de um ponteiro de char = " << sizeof(cPtr) << ", valor = " << cPtr << endl;
cout << "Tamanho de um ponteiro de double = " << sizeof(dPtr) << ", valor = " << dPtr << endl;
}
Não podemos efetuar outras operações com ponteiros além da adição e subtração, com multiplicação e divisão, e nem podemos usar operadores de deslocamento bit a bit.
Vejamos um código de exemplo que mostra as operações de adição e subtração de ponteiros:
int main() {
int a;
a = 30;
int *pont1;
pont1 = &a;
cout << "Endereço de a: " << pont1 << endl;
pont1++;
cout << "Endereço do ponteiro incrementado: " << pont1 << endl;
pont1 = pont1 + 10;
cout << "Somando 40 ao ponteiro pont1: " << pont1 << endl;
pont1--;
cout << "Decrementando o ponteiro: " << pont1 << endl;
}
Podemos efetuar a comparação de ponteiros usando os operadores de comparação padrão – apesar de esta técnica não ter tanta utilidade na prática. O código a seguir mostra a comparação entre ponteiros:
int main() {
int vetor[3] = {10, 30, 20};
int *p0 = vetor;
int *p1 = vetor + 1;
int *p2 = vetor + 2;
int *p3 = p2;
cout << "p2 > p0: " << (p2 > p0) << endl;
cout << "p0 > p1: " << (p0 > p1) << endl;
cout << "p2 < p0: " << (p2 < p0) << endl;
cout << "p3 == p2: " << (p3 == p2) << endl;
}
Outro exemplo, mais complexo:
int pares[5] = {0, 2, 4, 6, 8};
int *ponteiro;
ponteiro = pares; // O mesmo que &pares[0]
cout << "Endereço do vetor: " << pares << endl;
for(int i = 0; i < sizeof(pares) / sizeof(int); i++) {
cout << "Conteúdo do " << (i + 1) << "º índice do vetor: " << pares[i] << endl;
}
for(int i = 0; i < sizeof(pares) / sizeof(int); i++) {
cout << "Endereço do " << (i + 1) << "º índice do vetor: " << &pares[i] << endl;
}
for(int i = 0; i < sizeof(pares) / sizeof(int); i++) {
cout << "Endereço apontado pelo " << (i + 1) << "º incremento do ponteiro: " << ponteiro++ << endl;
}
for(int i = 0; i < sizeof(pares) / sizeof(int); i++) {
ponteiro = &pares[i];
cout << "Conteúdo apontado pelo " << (i + 1) << "º incremento do ponteiro: " << *ponteiro << endl;
}
Uma função pode retornar um número inteiro, um real e um caractere, assim como também pode retornar um vetor. Para isso, devemos utilizar ponteiros (ou apontador). A única forma de retornar um vetor é por meio de um ponteiro, pois não é possível criar funções como por exemplo, int[10] calcular()
, onde int[10] quer dizer que a função retorna um vetor com 10 posições.
A seguir veja um exemplo de uso desse recurso através de uma função, que cria um vetor de dez posições e os preenche com valores aleatórios, imprime os valores, e posteriormente passa esse vetor para quem
chamar a função:
int *gerarRandomico();
int main() {
int *p;
int i;
p = gerarRandomico();
for(i = 0; i < 10; i++) {
cout << "p[" << i << "] = " << p[i] << "." << endl;
}
return 0;
}
int *gerarRandomico() {
static int r[10];
int a;
for(a = 0; a < 10; a++) {
r[a] = rand() % 100;
cout << "r[" << a << "] = " << r[a] << "." << endl;
}
return r;
}
PS: Ao passar um ponteiro por um parâmetro, siga essas regras:
void procedimento(int *param);
void procedimento(int param);
int main() {
int n = 10;
int *p;
p = &n;
procedimento(p); // Caso a função/procedimento tenha * e a variável de entrada seja ponteiro
procedimento(*p); // Caso a função/procedimento não tenha * e a variável de entrada seja ponteiro
procedimento(&n); // Caso a função/procedimento tenha * e a variável de entrada não seja ponteiro
procedimento(n); // Casos mais comuns, onde nem a função/procedimento e nem a variável são ponteiros
return 0;
}
void procedimento(int *param) {
cout << "Endereço de 1: " << param << endl;
cout << "Conteúdo de 1: " << *param << endl;
}
void procedimento(int param) {
cout << "Endereço de 2: " << ¶m << endl;
cout << "Conteúdo de 2: " << param << endl;
}
Basicamente, no C, faríamos assim para gerar números randomicos no nosso programa:
srand(time(NULL)); // Importe stdlib.h pro rand/srand e time.h pro time
printf("%d\n", 12 + (rand() % (14 - 12) + 1));
Mas como no C++ temos a orientação a objetos, podemos criar uma classe para trabalharmos mais facilmente com isso.
Crie a classe random assim (inclua cstdlib e ctime):
class Random {
public:
int nextInt(int mini, int maxi);
Random();
private:
int rd;
};
int Random::nextInt(int mini, int maxi) {
this->rd = mini + (rand() % (maxi - (mini - 1)));
return this->rd;
}
Random::Random() {
srand(time(NULL));
}
E no programa principal podemos fazer assim (inclua o arquivo Random.h):
Random ale;
cout << ale.nextInt(15, 20) << endl; // Gera de 15 a 20
cout << ale.nextInt(1, 5) << endl; // Gera de 1 a 5
Quando trabalhamos com projetos grandes, costumamos criar muitas funções para fazer inúmeras tarefas, para isso podemos criar nosso próprios arquivos de cabeçalho.
No projeto em C++, vá em File e selecione o arquivo header (H) e salve na pasta do projeto atual. O header no exemplo terá o nome calculos.h. E coloque esse código:
#ifndef CALCULOS_H_INCLUDED
#define CALCULOS_H_INCLUDED
#define _PI 3.14159
int quadrado(int x);
int cubo(int x);
#endif // CALCULOS_H_INCLUDED
Agora criaremos um novo File da mesma forma, mas um C++ source com o nome calculos.cpp, nele será escrito esses códigos:
#include "calculos.h"
int quadrado(int x) {
return x * x;
}
int cubo(int x) {
return x * x * x;
}
E no main.cpp, colocamos o calculos.h incluído entre aspas, por estar na mesma pasta, dessa forma, veja o código completo abaixo:
#include <iostream>
#include "calculos.h"
using namespace std;
int main() {
cout << "Usando headers: " << endl;
int y = 5;
int quadr = quadrado(y);
int cub = cubo(y);
cout << "Quadrado de " << y << ": " << quadr << endl;
cout << "Cubo de " << y << ": " << cub << endl;
cout << "Valor da constante PI : " << _PI << endl;
return 0;
}
PS: Em todos os arquivos, adicione o Debug e o Release.
Ao incluir o include com aspas (tipo #include "funcoes.h"
), como feito com os arquivos de cabeçalho, o compilador apenas procurará na mesma pasta onde está o código-fonte a ser compilado. Quando são usadas tags (como #include <iostream>
) ele procurará na pasta padrão do compilador (no caso do Linux, poderia ser em /usr/include/c++
, por exemplo).
Basicamente, podemos pegar os códigos-fontes anteriores e colocá-los numa mesma pasta. Nessa pasta crie também um arquivo com o nome makefile, sem extensão, e coloque esse código:
contas: main.o calculos.o
g++ main.o calculos.o -o contas -no-pie
main.o: main.cpp calculos.h
g++ -c main.cpp -o main.o
calculos.o: calculos.cpp calculos.h
g++ -c calculos.cpp -o calculos.o
clean:
rm -f *.o
No caso ele executará de baixo pra cima, e não esqueça da tabulação. Para compilar basta digitar no terminal make
e executar o arquivo digitando ./contas
.
Basicamente, usamos isso para substituir um caractere no C++:
string entrada = "Apenas uma frase!";
replace(entrada.begin(), entrada.end(), ' ', '_'); // Inclua algorithm
cout << entrada << endl;
PS: Podemos fazer uma função para mudar mais de um caractere.
A substituição de palavras inteirass pode ser feita assim:
string entrada = "Apenas uma frase!";
entrada.replace(entrada.find("frase"), 5, "string"); // Na ordem palavra a ser substituída, quantidade de caracteres da mesma e palavra substituta
cout << entrada << endl;
No entanto ele só substitui uma vez. Para substituir todas as ocorrências, podemos criar uma função para isso, por exemplo:
string substituir(string texto, string antigo, string novo);
int main() {
string entrada = "Apenas uma frase! A frase foi repetida!";
entrada = substituir(entrada, "frase", "string"); // Na ordem palavra a ser substituída, palavra a substituir e palavra substituta
cout << entrada << endl;
return 0;
}
string substituir(string texto, string antigo, string novo) {
size_t pos;
while((pos = texto.find(antigo)) != string::npos) {
texto.replace(texto.find(antigo), antigo.length(), novo); // Na ordem palavra a ser substituída, quantidade de caracteres da mesma e palavra substituta
}
return texto;
}
O método find também pode ser usado para encontrar uma palavra numa string:
string entrada = "Apenas uma frase!";
size_t posicao = entrada.find("frase");
if(posicao != string::npos) {
cout << "Palavra encontrada." << endl;
}
else {
cout << "Palavra não encontrada." << endl;
}
Para transformar tudo em maiúscula, podemos fazer assim:
string entrada = "Apenas uma frase!";
transform(entrada.begin(), entrada.end(), entrada.begin(), ::toupper); // Inclua algorithm e cctype
cout << entrada << endl;
Da mesma forma, usamos o tolower
para transformar tudo em minúscula.
Para reverter uma frase:
string entrada = "Apenas uma frase!";
reverse(entrada.begin(), entrada.end()); // Inclua algorithm
cout << entrada << endl;
Para concatenar uma string:
string entrada = "Apenas uma frase!";
entrada.append(" E ela foi concatenada!");
cout << entrada << endl;
Para dividir uma string:
string entrada = "Exemplo de Frase";
regex reg(" "); // Inclua regex, aqui vai o caractere para dividir
vector<string> saida(sregex_token_iterator(entrada.begin(), entrada.end(), reg, -1), sregex_token_iterator()); // Inclua cstring e vector
for(string s: saida) {
cout << s << endl;
}
Para jogar uma string num array de char:
string entrada = "Apenas uma frase!";
const char* novo = entrada.c_str();
cout << novo << endl;
PS: Para fazer o contrário, que é converter um const char* para string, basta fazer um typecast com string.
Você pode converter int em string usando o método to_string()
(inclua cstring), assim:
int num = 50;
string tex = to_string(num); // Inclua cstring
cout << tex << endl;
Pra converter string pra int, usamos o stoi()
, da mesma forma:
string tex = "50";
int num = stoi(tex); // Inclua cstdlib
cout << num << endl;