Em C, *
é chamado operador indireto ou operador de desreferência. Entendo como funciona quando é usado em uma declaração. Faz sentido escrever *p
ou * p
, considerando que é um operador unário.
No entanto, às vezes em uma declaração, a *
é usado.
void move(int *units) { ... }
ou
int *p = &x;
Parece-me estranho que um operador seja usado aqui. Você não pode fazer coisas como
void move(int units++) { ... }
onde ++
também é um operador unário. Esse *
também é o operador de indireção ou *
tem um significado diferente, onde diz algo sobre o tipo do ponteiro? (Se for esse o caso, estou pensando em usar, por exemplo int* units
, declarações e de int x = *p;
outra forma para maior clareza.)
Em esta resposta é dito que
O padrão C define apenas dois significados para o operador *:
- operador indireto
- operador de multiplicação
Eu também vi pessoas alegando que isso *
é realmente parte do tipo. Isso me confunde.
void move(int *units) { ... }
, é um operador indireto. Considerado parte do tipo, também pode ser escritovoid move(int* units) { ... }
, embora eu prefira o estilo anterior. Você lê os dois como "int ponteiro". Veja também stackoverflow.com/a/8911253Respostas:
Os menores pedaços da linguagem C são símbolos lexicais , tais como palavras-chave (por exemplo
int
,if
,break
), identificadores (por exemplomove
,units
) e outros.*
é um elemento lexical chamado pontuador (como por exemplo{}()+
). Um pontuador pode ter significados diferentes, dependendo de como é usado:No capítulo C11, padrão 6.5, sobre expressões,
*
é definido como operador unário (seção 6.5.3.2, para indireção) e como operador multiplicativo (seção 6.5.5), exatamente como você descreveu. Mas*
só é interpretado como operador de acordo com as regras gramaticais que fazem expressões válidas.Mas há também um capítulo 6.2 sobre conceitos, que explica que ponteiros para um tipo são tipos derivados. A seção 6.7.6.1 sobre declaradores de ponteiros é mais precisa:
Isso realmente faz
*
parte de um tipo derivado (*
significa "ponteiro", mas sempre é necessário outro tipo para dizer que tipo de coisa aponta para alguma coisa).Observação: Não há contradição nisso. Você usa esse elemento diferenciado da linguagem lexical todos os dias quando fala inglês: "um identificador " designa algo e " manipular " significa fazer algo, dois usos gramaticais completamente diferentes do mesmo elemento lexical.
fonte
Em C, o indireto em uma declaração é melhor lido como parte da variável do que parte do tipo. Se eu tiver:
essa instrução pode ser interpretada como: declare uma variável denominada
a
que quando desreferenciada é do tipoint
.Isso é importante quando várias variáveis são declaradas ao mesmo tempo:
Ambas as declarações são equivalentes, e em ambos
a
eb
não são os mesmos tipos -a
é um ponteiro para um int eb
é um int. Mas declaraction (2) parece que o "tipo de ponteiro" faz parte do tipo quando na verdade é a declaração de*a
.fonte
Para adicionar às outras respostas corretas:
É apenas uma expressão de declaração que reflete possíveis expressões de uso. IIRC, acho que Kernigan ou Ritchie chamaram isso de experimento! Aqui está um texto de apoio marginal:
(Minha ênfase.)
http://codinghighway.com/2013/12/29/the-absolute-definitive-guide-to-decipher-c-declarations/
Então, sim, você está certo, esses são realmente os mesmos operadores aplicados às expressões de declaração, embora como você tenha observado apenas alguns dos operadores façam sentido (por exemplo, ++ não).
Essa estrela é realmente parte do tipo de variável individual que está sendo declarada; no entanto, como outros pôsteres observaram, ele não (mais precisamente, não pode) ser transferido para outras variáveis declaradas ao mesmo tempo (ou seja, na mesma declaração de declaração) - cada variável começa novamente com o mesmo tipo de base que o destino (eventual) e, em seguida, obtém seu próprio (oportunidade de aplicar ou não) ponteiro, matriz e operadores de função para concluir seu tipo.
É por isso que programadores experientes tendem a preferir (1)
int *p;
vs.int* p;
e (2) declarar apenas uma única variável por declaração, mesmo que a linguagem permita mais.Para adicionar a isso
int (*p);
uma declaração legal, esses parênteses são simplesmente agrupamentos e, neste caso simples, e não fazem nada diferenteint *p
. É o mesmo que dizer que uma expressão (não declarativa)(i) >= (0)
é a mesma quei >= 0
, como você sempre pode adicionar legalmente()
.Eu mencionei que como outro exemplo, embora, é por isso que os programadores preferem uma forma sobre o outro, por isso considero
int (*p);
vsint(* p);
. Talvez você possa ver como o parêntese, a estrela etc. se aplica à variável e não ao tipo base (ou seja, adicionando todos os parênteses permitidos).fonte
Nesta declaração de função:
o
*
é parte do tipo, ou sejaint*
. Não é um operador. É por isso que muitas pessoas escreveriam como:fonte
Somente o
*
,[]
e()
os operadores têm qualquer significado em declarações (C ++ acrescenta&
, mas não vamos entrar em detalhes aqui).Na declaração
o
int
-ness dep
é especificado pelo especificador de tipoint
, enquanto o ponteiro dep
é especificado pelo declarador*p
.O tipo de
p
é "ponteiro paraint
"; esse tipo é totalmente especificado pela combinação do especificador de tipoint
mais o declarador*p
.Em uma declaração, o declarador introduz o nome da coisa que está sendo declarada (
p
) junto com informações adicionais sobre o tipo não fornecidas pelo especificador de tipo ("ponteiro para"):Isso é importante - ponteiro, matriz e função são especificados como parte do declarador, não como o especificador de tipo 1 . Se você escrever
será analisado como
portanto, somente
a
será declarado como um ponteiro paraint
;b
ec
são declarados comoint
s regulares .A
*
,[]
e()
os operadores podem ser combinados para criar tipos arbitrariamente complexas:Observe que
*
,[]
e()
obedecem às mesmas regras de precedência em declarações que fazer em expressões.*a[N]
é analisado como*(a[N])
nas declarações e expressões.O que é realmente importante perceber é que a forma de uma declaração corresponde à forma da expressão no código. Voltando ao nosso exemplo original, temos um ponteiro para um número inteiro chamado
p
. Se queremos recuperar esse valor inteiro, usamos o*
operador para desreferenciarp
, da seguinte forma:O tipo da expressão
*p
éint
que segue da declaraçãoDa mesma forma, se tivermos uma matriz de ponteiros para
double
e desejamos recuperar um valor específico, indexamos na matriz e desreferimos o resultado:Novamente, o tipo da expressão
*ap[i]
édouble
, que segue a declaraçãoEntão, por que não
++
jogar um papel em uma declaração como*
,[]
ou()
? Ou qualquer outro operador como+
ou->
ou&&
?Bem, basicamente, porque a definição da linguagem diz isso. Ele só deixa de lado
*
,[]
e()
de desempenhar qualquer papel em uma declaração, uma vez que você tem que ser capaz de especificar ponteiro, matriz e tipos de função. Não há um tipo "increment-this" separado, portanto não há necessidade de++
fazer parte de uma declaração. Não há nenhuma "-este bit a bit" tipo, por isso não há necessidade de unário&
,|
,^
, ou~
para ser parte de uma declaração de qualquer um. Para tipos que usam o.
operador de seleção de membro, usamos as tagsstruct
eunion
na declaração. Para tipos que usam o->
operador, usamos as tagsstruct
eunion
em conjunto com o*
operador no declarador.*iptr
que especifica o ponteiro do nome do typedef.fonte
É verdade que existem dois significados para o
*
operador . Não é verdade que esses são os únicos significados do*
token .Em uma declaração como
o
*
não é um operador; faz parte da sintaxe de uma declaração.A sintaxe da declaração de C visa espelhar sua sintaxe de expressão, para que a declaração acima possa ser lida como "
*p
é do tipoint
", da qual podemos inferir quep
é do tipoint*
. No entanto, essa não é uma regra firme e não está declarada em nenhum lugar do padrão. Em vez disso, a sintaxe das declarações é definida por si só, separadamente da sintaxe das expressões. É por isso que, por exemplo, a maioria dos operadores que podem aparecer em expressões não pode ter o mesmo significado nas declarações (a menos que façam parte de uma expressão que faz parte da declaração, como o+
operadorint array[2+2];
).Um caso em que o princípio "declaração espelha o uso" não funciona muito bem é:
onde
arr[10]
seria do tipoint
se existisse .E, de fato, existe ainda outro uso do
*
token. Um parâmetro de matriz pode ser declarado com[*]
para indicar uma matriz de comprimento variável. (Isso foi adicionado em C99.) Não*
é uma multiplicação nem uma desreferência. Eu suspeito que foi inspirado pela sintaxe do curinga do shell.fonte
int *p = 0;
é semelhante ao uso * p = 0; `mas os significados são muito diferentes.int*
).