Como percorrer um array em C++ – 3 principais maneiras

texto "3 maneiras de se percorrer um array em C++" sobre teclado de fundo

Com as novas funcionalidades introduzidas pelos padrões recentes da linguagem, uma tarefa simples como percorrer um array em C++ pode se tornar confusa para alguns, pois agora há diversas maneiras de se fazê-lo. Logo, pensei que seria útil resumir neste artigo as três principais formas de se iterar por um array em C++.

Todavia, antres de passarmos aos detalhes de cada uma das 3 maneiras das quais falarei, é útil saber quais são cada uma delas: 1) for loop usando índices; 2) for loop usando iteradores; 3) range-based for loop (laço for baseado em uma faixa de valores).

Método #1 para percorrer um array em C++ – For loop usando índices

Este método de se percorrer arrays em C++ é o método clássico: todos os programadores de longa data do C++ já o utilisaram em algum momento, e até mesmo os que acabaram de começar no C++ que não estão habituados com as novas funcionalidades da linguagem já devem tê-lo utilizado.

Como funciona?

O modo de se percorrer containers (usarei esse termo mais genérico para me referir aos arrays de quando em quando) usando índices funciona da seguinte maneira: usa-se um for com uma variável de tipo inteiro (preferencialmente se usa o tipo size_t, o mais adaptado a esta finalidade) no primeiro campo (o de declaração); testa-se essa variável no segundo campo (o da condição de execução) para verificar que ela é menor do que o tamanho do vetor; no terceiro campo (o da expressão executada ao fim de cada iteração do for), então, incrementa-se o valor da variável com o operador de incremento.

Exemplo de utilização

No exemplo a seguir, crio e percorro um container de tipo vector – o famoso vetor – com o método do for loop usando índices. Para percorrer o meu vetor intVector, crio a variável index cujo tipo é size_t, e na condição de execução (ou de parada, como você preferir vê-la) verifico se ela é menor do que o tamanho do vetor (index < intVector.size()). Ao fim de cada iteração do for, a variável index é incrementada (++index).

Além disso, no cout do corpo do for (linha 10) eu imprimo o valor da variável index para formar a base da mensagem que é impressa na tela (intVector[1]: , por exemplo), e depois, para acessar o elemento do vetor correspondente ao índice armazenado na variável index, eu preciso usá-la novamente, dessa vez na expressão intVector[index]. Assim, o resultado impresso na tela a cada iteração tem o formato “intVector[índice]: [valor do vetor na posição do índice]” – veja a figura dos resultados após o bloco de código para compreender melhor o que quero dizer.

// Como percorrer um array em C++ - exemplo #1
// For loop usando índices

#include <iostream>
#include <vector>
using namespace std;

int main() {
    vector<int> intVector = {1, 2, 3, 4, 5};
    // For loop usando índices para acessar os elementos do vetor
    for (size_t index = 0; index < intVector.size(); ++index)
    {
        cout << "intVector[" << index << "]: " << intVector[index] << endl;
    }

    return 0;
}
Resultados da execução do código do exemplo 1 sobre como percorrer um array em C++
Figura 1 – Valores impressos na tela após a execução do código do exemplo 1.
Dica – cuidado com a condição de execução do for

Como os índices dos containers começam em 0, e não em 1, sempre tome cuidado ao percorrer um array em C++ usando for para não escrever o teste da condição de execução usando o operador <= ao invés do operador <. Se usarmos o operador <=, acessaremos uma posição além do último elemento do array e nosso programa terá então um comportamento indefinido.

Método #2 para percorrer um array em C++ – For loop usando iteradores

O segundo método para se percorrer arrays em C++ utiliza os iteradores. Os iteradores são tipos especiais de ponteiros cuja finalidade é a de percorrer contêineres, e para se declarar um iterador basta utilizar o tipo do contêiner seguido do operador de escopo e a palavra-chave iterator, seguidos do nome da variável (exemplo: vector<double>::iterator nomeDaVariavel).

Como funciona?

Essa maneira de se percorrer o contêiner pode parecer um pouco estranho para aqueles que não estão acostumados a usar os iteradores, mas não se preocupe: o segredo é entender o que as funções begin() e end() fazem, e o que o operador de incremento ++ faz quando aplicado a ponteiros.

As funções begin( ) e end( ) retornam, respectivamente, um iterador (ou seja, um ponteiro dedicado a percorrer arrays que oferece funções que facilitam essa tarefa) para o primeiro elemento do array, e um iterador para um elemento que fica uma posição após o último elemento do array.

Assim, no for inicializa-se a variável do loop com o tipo iterador para que ela receba o valor de retorno de begin() (ou seja, para que ela seja um iterador de aponta para o primeiro elemento do array), e essa variável é incrementada ao fim de cada iteração do laço for através do operador de incremento ++, que desloca o ponteiro uma posição para a frente – fazendo com que o iterador avance para o próximo elemento no contêiner. Por fim, para acessar o elemento ao qual aponta o iterador, basta usar o operador *, assim como fazemos com ponteiros.

Exemplo de utilização

Tomemos o exemplo a seguir para entender melhor como funciona o for usando iteradores para se percorrer um array em C++. Nesse exemplo, usamos o mesmo contêiner (um vector de inteiros contendo valores de 1 a 5) do exemplo anterior, mas logo vemos a primeira diferença quando olhamos para a variável declarada dentro do for (linha 12): usamos um iterador ao invés de size_t, e como não queremos modificar os valores do nosso vetor, usamos um iterador constante (vector<int>::const_iterator), que não permite usar o operador * para modificar os dados do elemento para o qual ele aponta, mas apenas para lê-los. Como se trata de um iterador constante, usamos o método cbegin() ao invés de begin() para obtê-lo.

Em seguida, na condição de execução do laço, verificamos se iterador é diferente do iterador constante que aponta para a posição além do último elemento do vetor intVector (esse iterador é retornado pela função cend() – aqui também usamos um iterador constante para manter a consistência do bloco); enquanto eles forem diferentes, sabemos que iterador aponta para elementos que pertencem ao vetor.

Por fim, incrementamos o valor do iterador ao fim de cada iteração com a expressão ++iterador, e acessamos o elemento apontado por iterador usando o operador * (linha 14).

// Como percorrer um array em C++ - exemplo #2
// For loop usando iteradores

#include <iostream>
#include <vector>
using namespace std;

int main() {
    vector<int> intVector = {1, 2, 3, 4, 5};

    // For loop usando iteradores para acessar os elementos do vetor
    for (vector<int>::const_iterator iterador = intVector.cbegin(); iterador != intVector.cend(); ++iterador)
    {
        cout << "Valor do elemento para o qual o iterador aponta: " << *iterador << endl;
    }

    return 0;
}
Resultados da execução do código do exemplo 2 sobre como percorrer um array em C++
Figura 2 – Valores impressos na tela após a execução do código do exemplo 2.
Bom hábito – use iteradores constantes sempre que possível

Sempre que se for usar um iterador apenas para ler valores de um contêiner, e não para modificá-los, use as versões constantes dos iteradores (obtidas com cbegin() e cend()). Desta forma, previnem-se modificações indesejadas dos elementos do contêiner, mas também a intenção do código (de não modificar nada) fica clara pelo uso do iterador constante.

Método #3 para percorrer um array em C++ – Range-based for loop

O terceiro método para se percorrer um array em C++ é o range-based for loop (ou for baseado em uma faixa de valores). Esta nova versão do clássico laço for é semelhante ao modo como ele é usado em outras linguagens (como Python e Javascript), e foi introduzida na linguagem com o padrão C++11. Ela é equivalente (em seus efeitos) às outras duas, mas é muito mais fácil de se ler e compreender. Vejamos, então, como ela funciona.

Como funciona?

A sintaxe para se usar o range-based for é a seguinte: for([declaração da variável do loop] : [expressão geradora da faixa a se percorrer]). A variável declarada à esquerda dos dois-pontos é utilizada dentro do for, e só existe no escopo do for, e pode ser uma variável normal ou uma referência. À direita dos dois-pontos, por sua vez, está o contêiner ou a expressão que gerará a faixa de valores a ser percorrida (é preciso dizê-lo assim, e não apenas falar do contêiner ou variável, porque pode-se usar diretamente o retorno de uma função, por exemplo, nessa posição). Assim, a cada iteração, a variável à esquerda dos dois-pontos será inicializada usando um novo valor da expressão à direita, na ordem em que eles estiverem posicionados.

Como o funcionamento desse método para se percorrer um array em C++ pode ser um pouco confuso à primeira vista, vejamos um exemplo para tornar as coisas mais claras.

Exemplo de utilização

No exemplo 3, retomei o mesmo contêiner dos exemplos anteriores, intVector, e o percorri utilizando um range-based for. Na linha 12, vemos que à esquerda dos dois-pontos criei uma variável de tipo referência para const auto, valorInt; mas que moléstia é isso? Calma, isso nada mais é do que uma referência para inteiro constante, ou seja, isso: const auto&, é igual a isso: const int& (neste contexto específico, claro). Como pode? É tudo culpa do auto.

O auto é uma palavra-chave muito útil para ser usada com os laços for desse tipo por lhe facilitarem a leitura: ao se usar auto ao invés do tipo da variável, o compilador detecta qual é o tipo correto e “reescreve” a declaração para você usando esse tipo.

Portanto, essa capacidade do auto permite reescrever declarações complicadas de se ler de um modo mais inteligível. Por exemplo: const std::vector<std::pair<int, double>> vetorDeParesIntDouble = funcaoQueRetornaEsseTipoEstranho() torna-se simplesmente const auto vetorDeParesIntDouble = funcaoQueRetornaEsseTipoEstranho(). Legal, não? Voltemos ao nosso caso.

Sabemos agora que à esquerda dos dois-pontos há uma referência para inteiros constante (usei esse tipo constante para deixar claro que não quero modificar os valores que forem lidos do vetor), e à direita está o próprio vetor a ser percorrido. Tá, mas e o resto? Não tem resto, é só isso mesmo ?. Para usar o valor do elemento do vetor em cada operação, basta usar o nome da variável que foi declarada (valorInt) onde se quiser usar o elemento (como é feito na linha 16).

// Como percorrer um array em C++ - exemplo #3
// Range-based for loop

#include <iostream>
#include <vector>
using namespace std;

int main() {
    vector<int> intVector = {1, 2, 3, 4, 5};

    // Range-based for loop usado para acessar os elementos do vetor.
    // Criei um tipo referência e não uma variável normal para evitar
    // copiar desnecessariamente o elemento do vetor.
    for (const auto& valorInt : intVector)
    {
        cout << "Valor do elemento da vez: " << valorInt << endl;
    }

    return 0;
}
Resultados da execução do código do exemplo 3 sobre como percorrer um array em C++
Figura 3 – Valores impressos na tela após a execução do código do exemplo 3.

Qual método usar para percorrer um array em C++?

Agora que vimos os principais modos de se percorrer um array em C++ (omiti propositalmente aquele que utilizam while), a pergunta que se segue é: qual deles devo usar? Falarei das situações de uso comuns para cada um deles.

For loop usando índices

Esta maneira de se percorrer um array em C++ tem alguns problemas que lhe tornam, normalmente, a última escolha para essa tarefa. O primeiro deles é que ela não expressa bem o intuito do laço, com exceção de às vezes utilizar diretamente o método size() sobre a variável do array na condição de execução (exemplo #1, linha 11)- o que indica que se está percorrendo aquele contêiner.

Além disso, este primeiro modo não é muito fácil de se ler, podendo ser mais ou menos complicada de entender de acordo com as expressões usadas no ‘cabeçalho’ do for.

Quando usá-lo?

O for loop com índices deve ser usado apenas quando não se puder utilizar os outros dois modos, como em alguns casos onde é absolutamente necessário se ter o índice que corresponde a um elemento do array (ainda assim, é possível criar uma “gambiarra” para obtê-lo usando os outros métodos).

For loop usando iteradores

O segundo modo de se percorrer um array em C++, usando iteradores com for loops, expressa sua intençao melhor do que o primeiro (por normalmente se usarem diretamente as funções begin() e end() sobre o contêiner percorrido na “assinatura” do forexemplo #2, linha 12), mas também tem o problema de não ser muito légivel: aqui, sobretudo, a notação pode ficar muito carregada.

Quando usá-lo?

Esse método tem a vantagem de avaliar a condição de execução e verificar onde está o iterador retornado pelo método end() a cada iteração (ao contrário do modo #3, que verifica onde acaba o array com end() apenas na “inicialização” do for), o que o permite se adaptar às possíveis mudanças feitas à estrutura do contêiner (por exemplo, a uma adição de elemento no fim de um vetor) que não invalidem os iteradores para aquele contêiner. Assim, se a estrtura interna do contêiner for ser alterada durante o loop, a melhor alternativa são os for com iteradores.

Ao percorrer um vetor, por exemplo, se é previsto que serão adicionados elementos ao fim do vetor (uma operação que não invalida iteradores para o vetor na maioria dos casos – a menos que haja uma expansão do vetor), é melhor usar o for com iteradores ao invés do range-based for

Range-based for loop

Esse tipo de for é o mais fácil de se ler entre os três, e também o mais simples de se escrever, então na maioria das situações ele é a escolha mais fácil a se fazer para percorrer um array em C++. De fato, na maioria das situações a estrutura interna do array ou contêiner percorrido não é alterada – apenas seus elementos são lidos ou modificados -, e ele torna-se a escolha mais vantajosa. Todavia, como dito na seção anterior, quando a estrutura do contêiner muda este tipo de for não é aconselhado, pois ele avalia onde está o fim do contêiner apenas antes da primeira iteração, e portanto não “acompanha” as mudanças na estrutura do contêiner.

Quando usá-lo?

Pelas vantagens que mencionei no parágrafo anterior, o range-based for pode ser usado – e é a escolha com melhor custo-benefício – na maioria das situações quando se deseja percorrer um array em C++, com exceção de quando a estrutura interna do contêiner é alterada durante o loop (na verdade, esse tipo de modificação deve ser evitada sempre que possível).

Conclusão

Neste artigo vimos 3 maneiras de percorrer um array em C++. Tratamos dos seguintes modos de fazê-lo: 1) for usando índices; 2) for usando iteradores; 3) range-based for. Abordamos como funciona cada um deles (com exemplos), e também discutimos quando usar cada uma das maneiras apresentadas.

Foto de perfil de Emanoel

Sou apaixonado por tecnologia, literatura e também filosofia. O cultivo dessas paixões ao longo da minha trajetória me inspiraram a compartilhar aquilo que aprendo com os outros da maneira mais clara que eu possa. Sou formado em Engenharia Elétrica no Brasil, e também sou engenheiro formado na França. Trabalho atualmente como programador C++ em uma multinacional francesa, uma das maiores empresas de TI do mundo.

Leave a Reply

Your email address will not be published. Required fields are marked *