Colocação do asterisco em declarações de ponteiro


92

Recentemente, decidi que só preciso aprender C / C ++, e há uma coisa que realmente não entendo sobre ponteiros ou, mais precisamente, sua definição.

Que tal estes exemplos:

  1. int* test;
  2. int *test;
  3. int * test;
  4. int* test,test2;
  5. int *test,test2;
  6. int * test,test2;

Agora, no meu entendimento, os três primeiros casos estão todos fazendo o mesmo: Teste não é um int, mas um indicador para um.

O segundo conjunto de exemplos é um pouco mais complicado. No caso 4, teste e test2 serão ponteiros para um int, enquanto no caso 5, apenas teste é um ponteiro, enquanto test2 é um int "real". E o caso 6? Mesmo que o caso 5?


9
Em C / C ++, os espaços em branco não mudam de significado.
Sulthan de

18
7. int*test;?
Jin Kwon

3
+1 porque só pensei em perguntar sobre 1 - 3. Ler esta pergunta me ensinou algo sobre 4 - 6 que eu nunca tinha pensado.
vastlysuperiorman

@Sulthan Isso é verdade 99% das vezes, mas nem sempre. Do topo da minha cabeça havia o tipo de tipo de modelo no requisito de espaço de tipo de modelo (pré C ++ 11). Foo<Bar<char>>No >>tinha de ser escrita > >para não ser tratado como um shift direita.
AnorZaken

3
@AnorZaken Você está certo, esse é um comentário bastante antigo. Existem várias situações em que um espaço muda de significado, por exemplo, o ++operador de incremento não pode ser dividido por um espaço, os identificadores não podem ser divididos por um espaço (e o resultado ainda pode ser válido para o compilador, mas com comportamento de tempo de execução indefinido). As situações exatas são muito difíceis de definir considerando a bagunça de sintaxe que é C / C ++.
Sulthan

Respostas:


129

4, 5 e 6 são a mesma coisa, apenas teste é um indicador. Se você quiser duas dicas, você deve usar:

int *test, *test2;

Ou, ainda melhor (para deixar tudo claro):

int* test;
int* test2;

3
Então o Caso 4 é realmente uma armadilha mortal? Existe alguma especificação ou leitura adicional que explique por que int * test, test2 apenas torna a primeira variável um ponteiro?
Michael Stum

8
@ Michael Stum É C ++, então você realmente acha que há uma explicação lógica?
Joe Phillips,

6
Leia K&R (a linguagem de programação C). Isso explica tudo muito claramente.
Ferruccio

8
Os casos 4, 5 e 6 são "armadilhas mortais". Esse é um dos motivos pelos quais muitos gudes no estilo C / C ++ sugerem apenas uma declaração por instrução.
Michael Burr,

14
O espaço em branco é insignificante para um compilador C (ignorando o pré-processador). Portanto, não importa quantos espaços haja ou não entre o asterisco e seus arredores, ele tem exatamente o mesmo significado.
efêmero

44

O espaço em branco ao redor dos asteriscos não tem significado. Todos os três significam a mesma coisa:

int* test;
int *test;
int * test;

O " int *var1, var2" é uma sintaxe maligna que visa confundir as pessoas e deve ser evitada. Ele se expande para:

int *var1;
int var2;

15
Não se esqueça int*test.
Mateen Ulhaq

1
o espaço antes ou depois do asterisco é apenas uma questão de estética. No entanto, o padrão do Google Coding acompanha int *test( google-styleguide.googlecode.com/svn/trunk/… ). Apenas seja consistente

@SebastianRaschka O Guia de estilo do Google C ++ permite explicitamente o posicionamento de qualquer asterisco. Talvez tenha mudado desde que você o leu.
Jared Beck

33

Muitas diretrizes de codificação recomendam que você declare apenas uma variável por linha . Isso evita qualquer confusão do tipo que você tinha antes de fazer esta pergunta. A maioria dos programadores de C ++ com quem trabalhei parecem aderir a isso.


Um pouco à parte, eu sei, mas algo que achei útil é ler as declarações de trás para frente.

int* test;   // test is a pointer to an int

Isso começa a funcionar muito bem, especialmente quando você começa a declarar ponteiros const e fica complicado saber se é o ponteiro que é const ou se é para o que o ponteiro está apontando é const.

int* const test; // test is a const pointer to an int

int const * test; // test is a pointer to a const int ... but many people write this as  
const int * test; // test is a pointer to an int that's const

Embora "uma variável por linha" pareça útil, ainda não resolvemos completamente a situação em que o asterisco está mais à esquerda ou mais à direita. Tenho certeza de que no código em estado selvagem uma variante prevalece; um pouco como alguns países conduzem no lado certo e outros conduzem na direção errada, como o Reino Unido. ;-)
shevy

Infelizmente, das minhas aventuras na selva, vejo muitos dos dois estilos. Em minha equipe, agora usamos o formato clang com um estilo que combinamos. Isso significa, pelo menos, que todo o código que nossa equipe produz tem o mesmo estilo para onde vai o espaço em branco.
Scott Langham

33

Use a "Regra da espiral no sentido horário" para ajudar a analisar as declarações C / C ++;

Existem três etapas simples a seguir:

  1. Começando com o elemento desconhecido, mova-se em espiral / sentido horário; ao encontrar os seguintes elementos, substitua-os pelas declarações em inglês correspondentes:

    [X]ou []: Tamanho da matriz X de ... ou Tamanho indefinido da matriz de ...

    (type1, type2): função passando type1 e type2 retornando ...

    *: apontador (es) para ...

  2. Continue fazendo isso em uma espiral / sentido horário até que todas as fichas tenham sido cobertas.
  3. Sempre resolva qualquer coisa entre parênteses primeiro!

Além disso, as declarações devem ser feitas em instruções separadas, quando possível (o que é verdade na grande maioria das vezes).


4
Isso parece assustador e horrível, desculpe dizer.
Joe Phillips,

7
sim, mas parece uma boa explicação para algumas das construções mais complicadas
Michael Stum

@ d03boy: Não há dúvida - as declarações C / C ++ podem ser um pesadelo.
Michael Burr,

2
A "espiral" não faz sentido, muito menos o "sentido horário". Prefiro chamá-la de "regra direita-esquerda", pois a sintaxe não faz com que você pareça direita-inferior-esquerda-superior, apenas direita-esquerda.
Ruslan

Aprendi isso como a regra "direita-esquerda-direita". O pessoal de C ++ geralmente gosta de fingir que todas as informações de tipo estão à esquerda, o que leva ao int* x;estilo em vez do int *x;estilo tradicional . Claro, o espaçamento não importa para o compilador, mas afeta os humanos. A negação da sintaxe real leva a regras de estilo que podem irritar e confundir os leitores.
Adrian McCarthy

12

Como outros mencionaram, 4, 5 e 6 são iguais. Freqüentemente, as pessoas usam esses exemplos para fazer o argumento de que *pertence à variável em vez do tipo. Embora seja uma questão de estilo, há um debate sobre se você deve pensar e escrever desta forma:

int* x; // "x is a pointer to int"

ou assim:

int *x; // "*x is an int"

FWIW Estou no primeiro campo, mas a razão pela qual outros defendem a segunda forma é que ela (principalmente) resolve este problema específico:

int* x,y; // "x is a pointer to int, y is an int"

o que é potencialmente enganoso; em vez disso você escreveria

int *x,y; // it's a little clearer what is going on here

ou se você realmente quer duas dicas,

int *x, *y; // two pointers

Pessoalmente, eu digo que mantenha uma variável por linha, então não importa qual estilo você prefere.


6
isso é bogus, como você chama int *MyFunc(void)? a *MyFuncé uma função que retorna um int? não. Obviamente, devemos escrever int* MyFunc(void), e dizer que MyFuncé uma função que retorna a int*. Então, para mim, isso está claro, as regras de análise gramatical C e C ++ são simplesmente erradas para declaração de variável. eles devem ter incluído a qualificação de ponteiro como parte do tipo compartilhado para toda a sequência de vírgula.
v.oddou

1
Mas *MyFunc() é um int. O problema com a sintaxe C é misturar sintaxe prefixo e pós - fixada - se apenas o pós-fixo fosse usado, não haveria confusão.
Antti Haapala

1
O primeiro campo combate a sintaxe da linguagem, levando a construções confusas como int const* x;, que considero tão enganosas quanto a * x+b * y.
Adrian McCarthy


5

Em 4, 5 e 6, testé sempre um ponteiro e test2não é um ponteiro. O espaço em branco (quase) nunca é significativo em C ++.


3

Na minha opinião, a resposta é AMBAS, dependendo da situação. Geralmente, IMO, é melhor colocar o asterisco ao lado do nome do ponteiro, em vez do tipo. Compare, por exemplo:

int *pointer1, *pointer2; // Fully consistent, two pointers
int* pointer1, pointer2;  // Inconsistent -- because only the first one is a pointer, the second one is an int variable
// The second case is unexpected, and thus prone to errors

Por que o segundo caso é inconsistente? Porque, por exemplo, int x,y;declara duas variáveis ​​do mesmo tipo, mas o tipo é mencionado apenas uma vez na declaração. Isso cria um precedente e um comportamento esperado. E int* pointer1, pointer2;é inconsistente com isso porque declara pointer1como um ponteiro, mas pointer2é uma variável inteira. Está claramente sujeito a erros e, portanto, deve ser evitado (colocando o asterisco ao lado do nome do ponteiro, em vez do tipo).

No entanto , existem algumas exceções em que você pode não ser capaz de colocar o asterisco ao lado do nome de um objeto (e onde importa onde colocá-lo) sem obter resultados indesejados - por exemplo:

MyClass *volatile MyObjName

void test (const char *const p) // const value pointed to by a const pointer

Finalmente, em alguns casos, pode ser indiscutivelmente mais claro colocar o asterisco ao lado do nome do tipo , por exemplo:

void* ClassName::getItemPtr () {return &item;} // Clear at first sight


2

A lógica em C é que você declara as variáveis ​​da maneira como as usa. Por exemplo

char *a[100];

diz que *a[42]será a char. E a[42]um ponteiro char. E assim aé uma matriz de ponteiros char.

Isso porque os escritores do compilador original queriam usar o mesmo analisador para expressões e declarações. (Não é uma razão muito sensata para uma escolha de design de idioma)


No entanto, a escrita char* a[100]; também deduz que *a[42];será um ponteiro chare a[42];um char.
yyny

Bem, todos nós deduzimos as mesmas conclusões, apenas a ordem está variando.
Michel Billaud

Citação: "diz que * a [42] será um char. E um [42] um ponteiro de char". Tem certeza de que não é o contrário?
deLock

Se você preferir o contrário, digamos que a[42]é um charponteiro e *a[42]é um char.
Michel Billaud

2

Eu diria que a convenção inicial era colocar a estrela no lado do nome do ponteiro (lado direito da declaração

Você pode seguir as mesmas regras, mas não é um grande problema colocar estrelas no lado das letras. Lembre-se de que a consistência é importante, portanto, sempre, mas a estrela do mesmo lado, independentemente do lado que você escolheu.


Bem - o analisador parece permitir qualquer uma das variantes, mas se Dennis e Linus dizem que deve estar no lado certo, isso é bastante convincente. Mesmo assim, falta-nos alguma justificativa e também a explicação de por que isso é feito. É um pouco como a situação tab versus espaço - exceto que um foi resolvido, porque as pessoas que usam espaços em vez de tabulações ganham mais dinheiro, de acordo com StackOverflow ... :-)
shevy

1

O ponteiro é um modificador do tipo. É melhor lê-los da direita para a esquerda para entender melhor como o asterisco modifica o tipo. 'int *' pode ser lido como "ponteiro para int '. Em várias declarações, você deve especificar que cada variável é um ponteiro ou será criada como uma variável padrão.

1,2 e 3) O teste é do tipo (int *). O espaço em branco não importa.

4,5 e 6) O teste é do tipo (int *). Test2 é do tipo int. Novamente, o espaço em branco é irrelevante.


-2

Uma boa regra prática, muitas pessoas parecem compreender esses conceitos por: Em C ++, muito significado semântico é derivado pela ligação à esquerda de palavras-chave ou identificadores.

Considere por exemplo:

int const bla;

O const se aplica à palavra "int". O mesmo acontece com os asteriscos dos ponteiros, eles se aplicam à palavra-chave à esquerda deles. E o nome real da variável? Sim, isso é declarado pelo que sobrou dele.


1
Isso não responde à pergunta. Pior ainda, se tentarmos inferir uma resposta a partir dele, isso implica que o asterisco se liga ao tipo à sua esquerda, que, como todos disseram, é falso. Ele se liga ao único nome de variável à sua direita.
underscore_d
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.