Respostas:
Eles são EXATAMENTE equivalentes. No entanto, em
int *myVariable, myVariable2;
Parece óbvio que myVariable possui o tipo int * , enquanto myVariable2 possui o tipo int . No
int* myVariable, myVariable2;
pode parecer óbvio que ambos são do tipo int * , mas isso não está correto como o myVariable2tipo int .
Portanto, o primeiro estilo de programação é mais intuitivo.
int* someVarprojetos pessoais. Faz mais sentido.
int[10] x. Isso simplesmente não é a sintaxe do C. A gramática analisa explicitamente como: int (*x)e não como (int *) x, portanto, colocar o asterisco à esquerda é simplesmente enganoso e baseado em um mal-entendido da sintaxe da declaração C.
int* myVariable; int myVariable2;.
Se você olhar de outra maneira, *myVariableé do tipo int, o que faz algum sentido.
myVariablepode ser NULL, nesse caso, *myVariablecausa uma falha de segmentação, mas não existe um tipo NULL.
int x = 5; int *pointer = &x;porque sugere que definimos o int *pointercom algum valor, não o pointerpróprio.
Como o * nessa linha se liga mais estreitamente à variável do que ao tipo:
int* varA, varB; // This is misleading
Como o @Lundin aponta abaixo, o const adiciona ainda mais sutilezas para pensar. Você pode evitar isso declarando uma variável por linha, o que nunca é ambíguo:
int* varA;
int varB;
É difícil encontrar o equilíbrio entre código claro e código conciso - uma dúzia de linhas redundantes int a;também não é boa. Ainda assim, eu padronizo uma declaração por linha e me preocupo com a combinação de código posteriormente.
int *const a, b;. Onde o * "liga"? O tipo de aé int* const, então como você pode dizer que o * pertence à variável quando faz parte do próprio tipo?
Algo que ninguém mencionou aqui até agora é que esse asterisco é realmente o " operador de desreferência " em C.
*a = 10;
A linha acima não significa que eu deseja atribuir 10a a, isso significa que eu quero atribuir 10a qualquer local de memória aaponta para. E eu nunca vi ninguém escrevendo
* a = 10;
você já? Portanto, o operador de desreferência é quase sempre escrito sem espaço. Provavelmente é para distingui-lo de uma multiplicação dividida em várias linhas:
x = a * b * c * d
* e * f * g;
Aqui *eseria enganoso, não seria?
Ok, agora o que a seguinte linha realmente significa:
int *a;
A maioria das pessoas diria:
Isso significa que aé um ponteiro para um intvalor.
Isso é tecnicamente correto, a maioria das pessoas gosta de ver / ler dessa maneira e é assim que os padrões C modernos o definiriam (observe que a própria linguagem C é anterior a todos os padrões ANSI e ISO). Mas não é a única maneira de ver isso. Você também pode ler esta linha da seguinte maneira:
O valor referenciado do aé do tipo int.
Portanto, o asterisco nesta declaração também pode ser visto como um operador de desreferência, o que também explica sua localização. E esse aé um ponteiro que não é realmente declarado, está implícito pelo fato de que a única coisa que você pode realmente desreferenciar é um ponteiro.
O padrão C define apenas dois significados para o *operador:
E a indireção é apenas um único significado, não há um significado extra para declarar um ponteiro, há apenas a indireção, que é o que a operação de desreferência faz, ela executa um acesso indireto, portanto, também dentro de uma declaração como int *a;essa é um acesso indireto ( *significa acesso indireto) e, portanto, a segunda declaração acima está muito mais próxima do padrão do que a primeira.
int a, *b, (*c)();como algo como "declare os seguintes objetos como int: o objeto a, o objeto apontado por be o objeto retornado da função apontada por c".
*em int *a;não é um operador, e não é dereferencing a(que não é mesmo ainda definidos)
*(ele só dá um significado à expressão total, e não para o *dentro da expressão!). Diz que "int a;" declara um ponteiro, o que ele faz, nunca reivindicou o contrário, mas sem um significado atribuído *, a leitura como * o valor não referenciado de aé um int ainda é totalmente válido, pois tem o mesmo significado factual. Nada, realmente nada escrito em 6.7.6.1 contradizia essa afirmação.
int *a;é uma declaração, não uma expressão. anão é desreferenciado por int *a;. aainda não existe no momento em que *está sendo processado. Você acha que int *a = NULL;é um bug porque desreferencia um ponteiro nulo?
Vou sair em um membro aqui e dizer que não há uma resposta direta a esta pergunta , tanto para declarações de variáveis e para tipos de parâmetro e de retorno, o que é que o asterisco deve ir ao lado do nome: int *myVariable;. Para entender o motivo, veja como você declara outros tipos de símbolo em C:
int my_function(int arg); para uma função;
float my_array[3] para uma matriz.
O padrão geral, denominado declaração após o uso , é que o tipo de um símbolo é dividido na parte anterior ao nome e nas partes ao redor do nome, e essas partes ao redor do nome imitam a sintaxe que você usaria para obter uma valor do tipo à esquerda:
int a_return_value = my_function(729);
float an_element = my_array[2];
e: int copy_of_value = *myVariable;.
O C ++ lança uma chave inglesa nos trabalhos com referências, porque a sintaxe no ponto em que você usa as referências é idêntica à dos tipos de valor, portanto, você pode argumentar que o C ++ adota uma abordagem diferente de C. Por outro lado, o C ++ mantém o mesmo comportamento de C no caso de ponteiros, então as referências realmente são as mais estranhas a esse respeito.
Isso é apenas uma questão de preferência.
Quando você lê o código, é mais fácil distinguir entre variáveis e ponteiros no segundo caso, mas pode causar confusão quando você coloca variáveis e ponteiros de um tipo comum em uma única linha (o que muitas vezes é desencorajado pelas diretrizes do projeto, porque diminui a legibilidade).
Prefiro declarar ponteiros com o sinal correspondente ao lado do nome do tipo, por exemplo
int* pMyPointer;
Um grande guru disse uma vez "Leia da maneira do compilador, você deve."
http://www.drdobbs.com/conversationsa-midsummer-nights-madness/184403835
Concedido isso foi sobre o tópico const const, mas a mesma regra se aplica aqui.
O compilador lê como:
int (*a);
não como:
(int*) a;
Se você adquirir o hábito de colocar a estrela ao lado da variável, facilitará a leitura das suas declarações. Também evita problemas oculares, como:
int* a[10];
- Editar -
Explicar exatamente o que quero dizer quando digo que é analisado como int (*a), significa que isso *se liga mais firmemente ado que a isso int, da mesma maneira que na expressão 4 + 3 * 7 3se liga mais firmemente 7do que 4devido à maior precedência de *.
Com desculpas pela arte ascii, uma sinopse do AST para análise int *aé mais ou menos assim:
Declaration
/ \
/ \
Declaration- Init-
Secifiers Declarator-
| List
| |
| ...
"int" |
Declarator
/ \
/ ...
Pointer \
| Identifier
| |
"*" |
"a"
Como é claramente mostrado, *vincula-se mais firmemente, ajá que o ancestral comum é Declarator, enquanto você precisa subir a árvore Declarationpara encontrar um ancestral comum que envolva o int.
(int*) a.
intnesse caso. Etapa 2. Leia uma declaração, incluindo qualquer tipo de decoração. *anesse caso. Etapa 3 Leia o próximo caractere. Se vírgula, consuma-a e volte para a etapa 2. Se o ponto-e-vírgula parar. Se alguma outra coisa gerar um erro de sintaxe. ...
int* a, b;e obter um par de indicadores. O que estou dizendo é que o *vínculo com a variável é analisado com ela, não com o tipo para formar o "tipo base" da declaração. Isso também faz parte do motivo pelo qual os typedefs foram introduzidos para permitir a typedef int *iptr; iptr a, b;criação de alguns ponteiros. Usando um typedef, você pode vincular o *ao int.
Declaration-Specifiercom as "decorações" Declaratorpara chegar ao tipo final de cada variável. No entanto, ele não "move" as decorações para o especificador de Declaração, caso contrário int a[10], b;, produziria resultados completamente ridículos. Ele analisa int *a, b[10];como int *a , b[10] ;. Não há outra maneira de descrevê-lo que faça sentido.
int*ano final é lido pelo compilador como: " ahas type int*". Foi isso que eu quis dizer com o meu comentário original.
Porque faz mais sentido quando você tem declarações como:
int *a, *b;
int* a, b;fez bum ponteiro para int. Mas eles não fizeram. E por uma boa razão. No sistema proposto, qual é o tipo da bseguinte declaração int* a[10], b;:?
Para declarar vários ponteiros em uma linha, prefiro o int* a, * b;que mais intuitivamente declara "a" como ponteiro para um número inteiro e não combina estilos ao declarar "b". Como alguém disse, eu não declararia dois tipos diferentes na mesma declaração.
As pessoas que preferem int* x;estão tentando forçar seu código a um mundo fictício, onde o tipo está à esquerda e o identificador (nome), à direita.
Eu digo "fictício" porque:
Em C e C ++, no caso geral, o identificador declarado é cercado pelas informações de tipo.
Pode parecer loucura, mas você sabe que é verdade. aqui estão alguns exemplos:
int main(int argc, char *argv[])significa " mainé uma função que leva um inte uma matriz de ponteiros para chare retorna um int". Em outras palavras, a maioria das informações de tipo está à direita. Algumas pessoas pensam que as declarações de função não contam porque são de alguma forma "especiais". OK, vamos tentar uma variável.
void (*fn)(int)meios fné um ponteiro para uma função que recebe inte não retorna nada.
int a[10]declara 'a' como uma matriz de 10 ints.
pixel bitmap[height][width].
Claramente, escolhi exemplos que têm muitas informações de tipo à direita para expressar minha opinião. Existem muitas declarações em que a maioria - se não todos - do tipo está à esquerda, como struct { int x; int y; } center.
Essa sintaxe da declaração surgiu do desejo da K&R de fazer com que as declarações refletissem o uso. A leitura de declarações simples é intuitiva, e a leitura de declarações mais complexas pode ser dominada pelo aprendizado da regra direita-esquerda-direita (às vezes chamada de regra espiral ou apenas da regra direita-esquerda).
C é simples o suficiente para que muitos programadores adotem esse estilo e escrevam declarações simples como int *p.
No C ++, a sintaxe ficou um pouco mais complexa (com classes, referências, modelos, classes enum) e, como uma reação a essa complexidade, você verá mais esforço para separar o tipo do identificador em muitas declarações. Em outras palavras, você poderá ver mais int* pdeclarações de estilo se verificar uma grande faixa de código C ++.
Em qualquer idioma, você sempre pode ter o tipo no lado esquerdo das declarações de variáveis (1) nunca declarando várias variáveis na mesma declaração e (2) fazendo uso de typedefs (ou alias), que, ironicamente, colocam o alias identificadores à esquerda dos tipos). Por exemplo:
typedef int array_of_10_ints[10];
array_of_10_ints a;
(*fn)mantêm o ponteiro associado fnao tipo de retorno, e não ao tipo.
Eu já respondi a uma pergunta semelhante no PC e, porque ninguém mencionou isso, também aqui eu tenho que apontar que C é uma linguagem de formato livre , qualquer que seja o estilo escolhido, enquanto o analisador pode fazer uma distinção de cada token. Esta peculiaridade de C levar a um tipo muito especial de concurso chamado concurso de ofuscação C .
Muitos dos argumentos deste tópico são claramente subjetivos e o argumento sobre "a estrela se liga ao nome da variável" é ingênuo. Aqui estão alguns argumentos que não são apenas opiniões:
Os qualificadores de tipo de ponteiro esquecidos
Formalmente, a "estrela" não pertence ao tipo nem ao nome da variável; faz parte de seu próprio item gramatical chamado ponteiro . A sintaxe C formal (ISO 9899: 2018) é:
(6.7) declaração:
especificadores de declaração init-declarator-list opt;
Onde os especificadores de declaração contêm o tipo (e o armazenamento) e a lista de declaradores init contém o ponteiro e o nome da variável. Que vemos se dissecarmos mais esta sintaxe da lista de declaradores:
(6.7.6) declarator:
apontador opt direct-declarator
...
(6.7.6) ponteiro:
*type-qualifier-list opt
*tipo-qualifier-list opt ponteiro
Onde um declarador é a declaração inteira, um declarador direto é o identificador (nome da variável) e um ponteiro é a estrela seguida por uma lista de qualificadores de tipo opcional pertencente ao próprio ponteiro.
O que torna inconsistentes os vários argumentos de estilo sobre "a estrela pertence à variável" é que eles se esqueceram desses qualificadores de tipo de ponteiro. int* const x, int *const xOu int*const x?
Considere int *const a, b;, quais são os tipos de ae b? Não é tão óbvio que "a estrela pertence à variável" por mais tempo. Em vez disso, começaria a refletir sobre ondeconst pertence.
Você pode definitivamente argumentar que a estrela pertence ao qualificador de tipo de ponteiro, mas não muito além disso.
A lista de qualificadores de tipo para o ponteiro pode causar problemas para aqueles que usam o int *aestilo. Aqueles que usam ponteiros dentro de um typedef(o que não devemos, muito má prática!) E pensam que "a estrela pertence ao nome da variável" tendem a escrever esse bug muito sutil:
/*** bad code, don't do this ***/
typedef int *bad_idea_t;
...
void func (const bad_idea_t *foo);
Isso compila de forma limpa. Agora você pode pensar que o código está correto. Não tão! Este código é acidentalmente uma falsificação de correção const.
O tipo de fooé realmente int*const*- o ponteiro mais externo foi feito somente leitura, não o apontado para os dados. Então, dentro desta função, podemos fazer **foo = n;e isso mudará o valor da variável no chamador.
Isso ocorre porque na expressão const bad_idea_t *foo, o *não pertence ao nome da variável aqui! No pseudo-código, esta declaração de parâmetro deve ser lida como const (bad_idea_t *) fooe não como (const bad_idea_t) *foo. A estrela pertence ao tipo de ponteiro oculto nesse caso - o tipo é um ponteiro e um ponteiro qualificado como const é escrito como *const.
Mas a raiz do problema no exemplo acima é a prática de ocultar os ponteiros atrás de um typedefe não o* estilo estilo.
Em relação à declaração de várias variáveis em uma única linha
Declarar várias variáveis em uma única linha é amplamente reconhecido como uma má prática 1) . O CERT-C resume muito bem como:
DCL04-C. Não declare mais de uma variável por declaração
Apenas lendo o inglês, o senso comum concorda que uma declaração deve ser uma declaração.
E não importa se as variáveis são indicadores ou não. Declarar cada variável em uma única linha torna o código mais claro em quase todos os casos.
Portanto, a discussão sobre o programador ficar confuso int* a, bé ruim. A raiz do problema é o uso de vários declaradores, não a colocação do *. Independentemente do estilo, você deve escrever isso:
int* a; // or int *a
int b;
Outro argumento sólido, mas subjetivo, seria aquele dado int* ao tipo dea is sem dúvida int*, a estrela pertence ao qualificador de tipo.
Mas, basicamente, minha conclusão é que muitos dos argumentos postados aqui são apenas subjetivos e ingênuos. Você realmente não pode argumentar válido para nenhum dos dois estilos - é realmente uma questão de preferência pessoal subjetiva.
1) CERT-C DCL04-C .
typedef int *bad_idea_t; void func(const bad_idea_t bar); Como o grande profeta Dan Saks ensina "Se você sempre coloca o constmais à direita possível, sem alterar o significado semântico", isso cessa completamente ser um problema. Também torna suas constdeclarações mais consistentes de ler. "Tudo à direita da palavra const é aquilo que é const, tudo à esquerda é seu tipo". Isso se aplicará a todos os consts em uma declaração. Experimente-o comint const * * const x;
Considerar
int *x = new int();
Isso não se traduz em
int *x;
*x = new int();
Na verdade, traduz para
int *x;
x = new int();
Isso torna a int *xnotação um tanto inconsistente.
newé um operador C ++. Sua pergunta está marcada com "C" e não "C ++".
typedefs, mas isso adicionará complexidade desnecessária, IMHO.