Por que a maioria dos programadores C nomeia variáveis como esta:
int *myVariable;
em vez de assim:
int* myVariable;
Ambos são válidos. Parece-me que o asterisco faz parte do tipo, não parte do nome da variável. Alguém pode explicar essa lógica?
c
pointers
variables
naming-conventions
WBlasko
fonte
fonte
typedefs
, mas isso adicionará complexidade desnecessária, IMHO.Respostas:
Eles são EXATAMENTE equivalentes. No entanto, em
Parece óbvio que myVariable possui o tipo int * , enquanto myVariable2 possui o tipo int . No
pode parecer óbvio que ambos são do tipo int * , mas isso não está correto como o
myVariable2
tipo int .Portanto, o primeiro estilo de programação é mais intuitivo.
fonte
int* someVar
projetos 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 tipoint
, o que faz algum sentido.fonte
myVariable
pode ser NULL, nesse caso,*myVariable
causa uma falha de segmentação, mas não existe um tipo NULL.int x = 5; int *pointer = &x;
porque sugere que definimos o int*pointer
com algum valor, não opointer
próprio.Como o * nessa linha se liga mais estreitamente à variável do que ao tipo:
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:
É 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.fonte
int *const a, b;
. Onde o * "liga"? O tipo dea
é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 linha acima não significa que eu deseja atribuir
10
aa
, isso significa que eu quero atribuir10
a qualquer local de memóriaa
aponta para. E eu nunca vi ninguém escrevendovocê 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:
Aqui
*e
seria enganoso, não seria?Ok, agora o que a seguinte linha realmente significa:
A maioria das pessoas diria:
Isso significa que
a
é um ponteiro para umint
valor.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 tipoint
.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.fonte
int a, *b, (*c)();
como algo como "declare os seguintes objetos comoint
: o objetoa
, o objeto apontado porb
e o objeto retornado da função apontada porc
".*
emint *a;
não é um operador, e não é dereferencinga
(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 dea
é 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.a
não é desreferenciado porint *a;
.a
ainda não existe no momento em que*
está sendo processado. Você acha queint *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.
fonte
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
fonte
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:
não como:
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:
- Editar -
Explicar exatamente o que quero dizer quando digo que é analisado como
int (*a)
, significa que isso*
se liga mais firmementea
do que a issoint
, da mesma maneira que na expressão4 + 3 * 7
3
se liga mais firmemente7
do que4
devido à maior precedência de*
.Com desculpas pela arte ascii, uma sinopse do AST para análise
int *a
é mais ou menos assim:Como é claramente mostrado,
*
vincula-se mais firmemente,a
já que o ancestral comum éDeclarator
, enquanto você precisa subir a árvoreDeclaration
para encontrar um ancestral comum que envolva oint
.fonte
(int*) a
.int
nesse caso. Etapa 2. Leia uma declaração, incluindo qualquer tipo de decoração.*a
nesse 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 atypedef int *iptr;
iptr a, b;
criação de alguns ponteiros. Usando um typedef, você pode vincular o*
aoint
.Declaration-Specifier
com as "decorações"Declarator
para 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árioint a[10], b;
, produziria resultados completamente ridículos. Ele analisaint *a, b[10];
comoint
*a
,
b[10]
;
. Não há outra maneira de descrevê-lo que faça sentido.int*a
no final é lido pelo compilador como: "a
has typeint*
". Foi isso que eu quis dizer com o meu comentário original.Porque faz mais sentido quando você tem declarações como:
fonte
int* a, b;
fezb
um ponteiro paraint
. Mas eles não fizeram. E por uma boa razão. No sistema proposto, qual é o tipo dab
seguinte declaraçãoint* 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.fonte
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:
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 umint
e uma matriz de ponteiros parachar
e retorna umint
". 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)
meiosfn
é um ponteiro para uma função que recebeint
e não retorna nada.int a[10]
declara 'a' como uma matriz de 10int
s.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* p
declaraçõ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
typedef
s (ou alias), que, ironicamente, colocam o alias identificadores à esquerda dos tipos). Por exemplo:fonte
(*fn)
mantêm o ponteiro associadofn
ao 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 .
fonte
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) é:
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:
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 x
Ouint*const x
?Considere
int *const a, b;
, quais são os tipos dea
eb
? 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 *a
estilo. Aqueles que usam ponteiros dentro de umtypedef
(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: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
é realmenteint*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 comoconst (bad_idea_t *) foo
e 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
typedef
e 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:
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:Outro argumento sólido, mas subjetivo, seria aquele dado
int* a
o tipo dea
is sem dúvidaint*
, 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 .
fonte
typedef int *bad_idea_t;
void func(const bad_idea_t bar);
Como o grande profeta Dan Saks ensina "Se você sempre coloca oconst
mais à direita possível, sem alterar o significado semântico", isso cessa completamente ser um problema. Também torna suasconst
declaraçõ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
Isso não se traduz em
Na verdade, traduz para
Isso torna a
int *x
notação um tanto inconsistente.fonte
new
é um operador C ++. Sua pergunta está marcada com "C" e não "C ++".