Aprenda C++

  • Página Inicial
  • Contato!
  • Tudo sobre C++ Parte 1!
  • Tudo sobre C++ Parte 2!
  • Tudo sobre C++ Parte 3!
  • Tudo sobre C++ Parte 4!
  • Tudo sobre C++ Parte 5!
  • Tudo sobre C++ Parte 6!
  • Tudo sobre C++ Parte 7!
  • Tudo sobre C++ Parte 8!
  • Tudo sobre C++ Parte 9!
  • Tudo sobre C++ Parte 10!
  • Tudo sobre C++ Parte 11!
  • Tudo sobre C++ Parte 12!
  • Tudo sobre C++ Parte 13!
  • Tudo sobre C++ Parte 14!
  • Tudo sobre C++ Parte 15!
  • Tudo sobre C++ Parte 16!
  • Tudo sobre C++ Parte 13

    Aritmética de Ponteiros

    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;
    }
    
    

    Uso de Funções com Ponteiros

    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: " << &param << endl;
        cout << "Conteúdo de 2: " << param << endl;
    }
    
    

    Números Randomicos em C++

    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
    
    

    Criar Arquivos de Cabeçalho em C++

    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).

    Criar Makefile em Linux

    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.

    Usando Funções em Strings

    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;