paulo1205
(usa Ubuntu)
Enviado em 12/10/2022 - 03:19h
Os três são absolutamente sinônimos.
De cara, “
int *vetor” e “
int* vetor” são exatamente a mesma coisa do ponto de vista sintático, mudando apenas a forma de usar espaços; seria a mesma se a pessoa fizesse “
int*vetor” ou “
int * vetor”.
Já a forma “
int vetor[]” é ligeiramente diferente porque, embora nesse caso ela seja sinônima das apresentadas acima, esta forma só pode ser usada na declaração de parâmetros de funções, não podendo ser usada em outras declarações do C++, tais como variáveis ou campos de estruturas ou classes. Essa forma sintática é, ao mesmo tempo, uma reminiscência da linguagem B (ancestral do C, que é ancestral do C++), que usava essa mesma sintaxe para seus ponteiros e não possuía a notação com asteriscos, e também uma decorrência do fato que, na maioria dos contextos, o uso de um
array como parte de uma expressão provoca o aparecimento de um valor que é meramente um ponteiro para o primeiro elemento de tal
array. Esse segundo ponto, inclusive, faz com que sejam sinônimas também a forma que use um valor inteiro entre os colchetes (por exemplo, algo como “
int vetor[50]”), porque quando tal forma é usada na declaração de um parâmetro de função, ali não se está provocando a alocação de memória, mas preparando a função para receber o endereço inicial de um bloco de memória que foi alocado em algum outro lugar, de modo que apenas um endereço é passado à função, em vez de todos os elementos do
array.
Já vi em alguns contextos se advogar o uso de “
int vec[]” (ou mesmo “
int vec[n]”, sendo
n um valor inteiro positivo) em lugar de “
int *vec” como parâmetro da função sempre que for esperado que o argumento passado à função seja um
array, e não um ponteiro genérico, que poderia se referir a apenas um valor inteiro ou mesmo ser um ponteiro nulo ou inválido. Entretanto, esse uso é meramente uma convenção, não alterando de modo nenhum o comportamento da linguagem, que continua tratando tais parâmetros como meros ponteiros. É por isso, por exemplo, que você vê, por exemplo, as duas formas abaixo sendo usadas na declaração da função
main, embora ambas tenham o sentido de, no segundo argumento, receber um ponteiro para ponteiro para caracteres, o qual é usado para receber um vetor de tamanho não predeterminado de ponteiros para caracteres.
int main(int argc, char *argv[])
int main(int argc, char **argv)
Eu particularmente não sigo essa convenção (por exemplo, das duas forma acima, eu geralmente uso a segunda), e o faço justamente porque, além de digitar um caráter a menos, no fim das contas, o argumento será um ponteiro e o parâmetro é um ponteiro, não vetores. Creio que isso é bem ilustrado pelo código abaixo.
#include <iostream>
int vglobal[50];
void f(int vparam[50]){
std::cout << "Tamanho: " << sizeof vparam << "\tEndereço 1º elemento: " << vparam << "\tEndereço da variável: " << &vparam << '\n';
}
int main(void){
int vlocal[50];
std::cout << "Tamanho: " << sizeof vglobal << "\tEndereço 1º elemento: " << vglobal << "\tEndereço da variável: " << &vglobal << '\n';
std::cout << "Tamanho: " << sizeof vlocal << "\tEndereço 1º elemento: " << vlocal << "\tEndereço da variável: " << &vlocal << "\n\n";
f(vglobal);
f(vlocal);
}
De cara, ao compilar tal código, vem uma mensagem de alerta.
x.cc: In function ‘void f(int*)’:
x.cc:6:40: warning: ‘sizeof’ on array function parameter ‘vparam’ will return size of ‘int*’ [-Wsizeof-array-argument]
6 | std::cout << "Tamanho: " << sizeof vparam << "\tEndereço 1º elemento: " << vparam << "\tEndereço da variável: " << &vparam << '\n';
| ^~~~~~
x.cc:5:12: note: declared here
5 | void f(int vparam[50]){
| ~~~~^~~~~~~~~~
E, de fato, a saída do programa executado mostra essa diferença. Note como, nas duas primeiras linhas, os endereços dos respectivos primeiros elementos são numericamente equivalentes aos endereços dos
arrays como um todo (i.e. o primeiro dos 200 bytes que compõem cada um deles, com 4 bytes por elemento, portanto), e como, nas duas últimas, o tamanho do suposto
array não tem nada a ver com a quantidade suposta de 50 elementos nem com o tamanho de cada elemento (4 bytes), mas sim o tamanho de um ponteiro, e como o endereço do primeiro elemento não coincide com o endereço do suposto
array, mas que é, na verdade, o endereço onde reside o ponteiro recebido como parâmetro.
Tamanho: 200 Endereço 1º elemento: 0x563300222160 Endereço da variável: 0x563300222160
Tamanho: 200 Endereço 1º elemento: 0x7ffe6fae0820 Endereço da variável: 0x7ffe6fae0820
Tamanho: 8 Endereço 1º elemento: 0x563300222160 Endereço da variável: 0x7ffe6fae0808
Tamanho: 8 Endereço 1º elemento: 0x7ffe6fae0820 Endereço da variável: 0x7ffe6fae0808
Para ter
arrays de tamanho fixo como argumentos de funções, há três abordagens que me ocorrem agora (certamente entre outras possíveis).
A primeira é usar referências (C++) ou ponteiros (C e C++) para os
arrays como parâmetros das funções.
// Tradicional: indicação de array é mera convenção; o que se tem é ponteiro para primeiro elemento.
void func1(int vec[50]){ /* bla bla bla */ }
// Ponteiro para array: válida em C e C++.
void func2(int (*pvec)[50]){ /* bla bla bla */ }
// Referência para array (somente C++).
void func3(int (&rvec)[50]){ /* bla bla bla */ }
int main(void){
int v50[50], v30[30];
func1(v50); // OK.
func2(&v50); // OK. Note que foi necessário usar o “&”.
func3(v50); // OK, mas note que visualmente não é distinguível de func1, embora seja funcionalmente bem diferente.
func1(v30); // Sintaticamente OK, porém possivelmente problemático se a função tentar mexer do 31º elemento em diante.
func2(&v30); // ERRO: tipo de ponteiro incompatível.
func3(v30); // ERRO: tipo incompatível.
int *p;
p=v50;
func1(p); // OK (sintaticamente e logicamente).
func2(&p); // ERRO: tipo de ponteiro incompatível
func3(p); // ERRO: tipo incompatível.
p=v30;
func1(p); // Sintaticamente OK, porém possivelmente problemático se a função tentar mexer do 31º elemento em diante.
func2(&p); // ERRO: tipo de ponteiro incompatível
func3(p); // ERRO: tipo incompatível.
func1(nullptr); // Sintaticamente OK. Logicamente, se a função tiver tratamento de ponteiro nulo, pode fazer sentido; caso contrário, pode dar problema.
func2(nullptr); // Sintaticamente OK. Logicamente, se a função tiver tratamento de ponteiro nulo, pode fazer sentido; caso contrário, pode dar problema.
func3(*(p=nullptr)); // Sintaticamente OK, mas certamente vai dar SIGSEGV em tempo de execução.
}
A segunda abordagem seria substituir o uso de
array nativos por
std::array, no caso de funções cujos argumentos tenham de ter tamanho fixo, ou por
std::vector, caso tenham de ser
arrays de tamanho não previamente conhecido ou que possa variar dinamicamente. Dependendo das operações, pode necessário passar objetos dessas classes por referência.
A terceira é possivelmente uma forma ancestral da segunda: encapsular um
array dentro de uma estrutura ou união contendo apenas tal
array.
... Então Jesus afirmou de novo: “(...) eu vim para que tenham vida, e a tenham plenamente.” (João 10:7-10)