Colocación del asterisco en declaraciones de punteros

92

Recientemente decidí que finalmente tengo que aprender C / C ++, y hay una cosa que realmente no entiendo sobre los punteros o, más precisamente, su definición.

¿Qué tal estos ejemplos?

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

Ahora, a mi entender, los primeros tres casos están haciendo lo mismo: Test no es un int, sino un puntero a uno.

El segundo conjunto de ejemplos es un poco más complicado. En el caso 4, tanto test como test2 serán punteros a un int, mientras que en el caso 5, solo test es un puntero, mientras que test2 es un int "real". ¿Y el caso 6? ¿Igual que en el caso 5?

Michael Stum
fuente
10
En C / C ++, los espacios en blanco no cambian de significado.
Sulthan
19
7. int*test;?
Jin Kwon
3
+1 porque solo había pensado en preguntar sobre 1 - 3. Leer esta pregunta me enseñó algo sobre 4 - 6 que nunca había pensado.
vastlysuperiorman
@Sulthan Eso es cierto el 99% de las veces, pero no siempre. En la parte superior de mi cabeza estaba el tipo de tipo con plantilla en el requisito de espacio de tipo con plantilla (anterior a C ++ 11). En Foo<Bar<char>>el >>tenía que estar escrito > >para no ser tratado como un desplazamiento a la derecha.
AnorZaken
3
@AnorZaken Tienes razón, ese es un comentario bastante antiguo. Hay múltiples situaciones en las que un espacio cambiará de significado, por ejemplo, el ++operador de incremento no se puede dividir por un espacio, los identificadores no se pueden dividir por un espacio (y el resultado puede ser legal para el compilador pero con un comportamiento de tiempo de ejecución indefinido). Las situaciones exactas son muy difíciles de definir considerando el desorden de sintaxis que es C / C ++.
Sulthan

Respuestas:

129

4, 5 y 6 son lo mismo, solo la prueba es un puntero. Si quieres dos punteros, debes usar:

int *test, *test2;

O, mejor aún (para dejarlo todo claro):

int* test;
int* test2;
Milán Babuškov
fuente
3
Entonces, ¿el caso 4 es en realidad una trampa mortal? ¿Hay alguna especificación o lectura adicional que explique por qué int * test, test2 solo hace que la primera variable sea un puntero?
Michael Stum
8
@ Michael Stum Es C ++, ¿de verdad crees que hay una explicación lógica?
Joe Phillips
6
Leer K&R (El lenguaje de programación C). Explica todo esto muy claramente.
Ferruccio
8
Los casos 4, 5 y 6 son "trampas mortales". Esta es una de las razones por las que muchas guías de estilo C / C ++ sugieren solo una declaración por declaración.
Michael Burr
14
Los espacios en blanco son insignificantes para un compilador de C (ignorando el preprocesador). Entonces, no importa cuántos espacios haya o no entre el asterisco y sus alrededores, tiene exactamente el mismo significado.
Ephemient
45

Los espacios en blanco alrededor de los asteriscos no tienen importancia. Los tres significan lo mismo:

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

El " int *var1, var2" es una sintaxis maligna que solo pretende confundir a la gente y debe evitarse. Se expande a:

int *var1;
int var2;
Ates Goral
fuente
16
No olvides int*test.
Mateen Ulhaq
2
el espacio antes o después del asterisco es solo una cuestión de estética. Sin embargo, el estándar de codificación de Google va con int *test( google-styleguide.googlecode.com/svn/trunk/… ).
@SebastianRaschka La Guía de estilo de Google C ++ permite explícitamente la colocación de asteriscos. Quizás haya cambiado desde que lo leíste.
Jared Beck
33

Muchas pautas de codificación recomiendan que solo declare una variable por línea . Esto evita cualquier confusión del tipo que tenía antes de hacer esta pregunta. La mayoría de los programadores de C ++ con los que he trabajado parecen ceñirse a esto.


Un poco al margen, lo sé, pero algo que encontré útil es leer las declaraciones al revés.

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

Esto comienza a funcionar muy bien, especialmente cuando comienzas a declarar punteros constantes y se vuelve complicado saber si es el puntero lo que es constante, o si es a lo que apunta el puntero lo que es constante.

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
Scott Langham
fuente
Si bien "una variable por línea" parece útil, todavía no hemos resuelto completamente la situación en la que el asterisco está más a la izquierda o más a la derecha. Estoy bastante seguro de que en el código en estado salvaje prevalece una variante; un poco como algunos países conducen por el lado correcto y otros conducen en la dirección equivocada, como el Reino Unido. ;-)
shevy
Desafortunadamente, de mis aventuras en la naturaleza, veo muchos de ambos estilos. En mi equipo ahora usamos el formato clang con un estilo que hemos acordado. Esto al menos significa que todo el código que produce nuestro equipo tiene el mismo estilo para el lugar donde va el espacio en blanco.
Scott Langham
33

Utilice la "Regla de la espiral en sentido horario" para ayudar a analizar las declaraciones de C / C ++;

Hay tres sencillos pasos a seguir:

  1. Comenzando con el elemento desconocido, muévase en espiral / en el sentido de las agujas del reloj; cuando encuentre los siguientes elementos reemplácelos con las correspondientes declaraciones en inglés:

    [X]o []: tamaño de matriz X de ... o tamaño indefinido de matriz de ...

    (type1, type2): función que pasa type1 y type2 regresando ...

    *: puntero (s) a ...

  2. Siga haciendo esto en una dirección en espiral / en el sentido de las agujas del reloj hasta que todas las fichas estén cubiertas.
  3. ¡Siempre resuelva todo lo que esté entre paréntesis primero!

Además, las declaraciones deben estar en declaraciones separadas cuando sea posible (lo cual es cierto la gran mayoría de las veces).

Michael Burr
fuente
4
Eso parece desalentador y bastante horrible, lamento decirlo.
Joe Phillips
7
sí, pero parece una buena explicación para algunas de las construcciones más complicadas
Michael Stum
@ d03boy: No hay duda: las declaraciones de C / C ++ pueden ser una pesadilla.
Michael Burr
2
La "espiral" no tiene ningún sentido, mucho menos el "sentido horario". Prefiero llamarla "regla derecha-izquierda", ya que la sintaxis no te hace ver derecha-abajo-izquierda-arriba, solo derecha-izquierda.
Ruslan
Aprendí esto como la regla "derecha-izquierda-derecha". A la gente de C ++ a menudo le gusta fingir que toda la información de tipo está a la izquierda, lo que lleva al int* x;estilo en lugar del int *x;estilo tradicional . Por supuesto, el espaciado no le importa al compilador, pero afecta a los humanos. La negación de la sintaxis real conduce a reglas de estilo que pueden molestar y confundir a los lectores.
Adrian McCarthy
12

Como otros mencionaron, 4, 5 y 6 son iguales. A menudo, la gente usa estos ejemplos para argumentar que *pertenece a la variable en lugar del tipo. Si bien es una cuestión de estilo, existe cierto debate sobre si debe pensar y escribirlo de esta manera:

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

o de esta manera:

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

FWIW Estoy en el primer campo, pero la razón por la que otros argumentan a favor de la segunda forma es que (en su mayoría) resuelve este problema en particular:

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

que es potencialmente engañoso; en su lugar escribirías

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

o si realmente quieres dos punteros,

int *x, *y; // two pointers

Personalmente, digo que lo mantenga en una variable por línea, entonces no importa qué estilo prefiera.

huskerchad
fuente
6
esto es falso, ¿cómo se llama int *MyFunc(void)? a *MyFunc¿una función devuelve un int? No. Obviamente, deberíamos escribir int* MyFunc(void), y decir MyFunces una función que devuelve a int*. Entonces para mí esto está claro, las reglas de análisis gramatical de C y C ++ son simplemente incorrectas para la declaración de variables. deberían haber incluido la calificación de puntero como parte del tipo compartido para toda la secuencia de coma.
v.oddou
1
Pero *MyFunc() es un int. El problema con la sintaxis de C es mezclar la sintaxis de prefijo y sufijo ; si solo se usara el prefijo , no habría confusión.
Antti Haapala
1
El primer campo lucha contra la sintaxis del lenguaje, lo que lleva a construcciones confusas como int const* x;, que encuentro tan engañosas como a * x+b * y.
Adrian McCarthy
11
#include <type_traits>

std::add_pointer<int>::type test, test2;
fredoverflow
fuente
#include <windows.h>LPINT test, test2;
Stefan Dragnev
5

En 4, 5 y 6, testsiempre es un puntero y test2no es un puntero. El espacio en blanco (casi) nunca es significativo en C ++.

1800 INFORMACIÓN
fuente
3

En mi opinión, la respuesta es AMBAS, dependiendo de la situación. Generalmente, en mi opinión, es mejor poner el asterisco al lado del nombre del puntero, en lugar del tipo. Compare, por ejemplo:

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 qué el segundo caso es inconsistente? Porque, por ejemplo, int x,y;declara dos variables del mismo tipo pero el tipo se menciona solo una vez en la declaración. Esto crea un precedente y un comportamiento esperado. Y int* pointer1, pointer2;es inconsistente con eso porque se declara pointer1como un puntero, pero pointer2es una variable entera. Claramente propenso a errores y, por lo tanto, debe evitarse (colocando el asterisco al lado del nombre del puntero, en lugar del tipo).

Sin embargo , existen algunas excepciones en las que es posible que no pueda colocar el asterisco junto al nombre de un objeto (y dónde importa dónde lo ponga) sin obtener un resultado no deseado, por ejemplo:

MyClass *volatile MyObjName

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

Finalmente, en algunos casos, podría decirse que sería más claro poner el asterisco al lado del nombre del tipo , por ejemplo:

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

deLock
fuente
2

El fundamento de C es que declaras las variables de la forma en que las usas. Por ejemplo

char *a[100];

dice que *a[42]será un char. Y a[42]un puntero de char. Y así aes una matriz de punteros char.

Esto porque los escritores del compilador original querían usar el mismo analizador para expresiones y declaraciones. (No es una razón muy sensata para elegir un diseño de idioma)

Michel Billaud
fuente
Sin embargo, la escritura char* a[100]; también deduce que *a[42];será un puntero de carácter chary a[42];carácter.
yyny
Bueno, todos deducimos las mismas conclusiones, solo que el orden varía.
Michel Billaud
Cita: "dice que * a [42] será un char. Y a [42] un puntero char". ¿Estás seguro de que no es al revés?
deLock
Si prefiere lo contrario, digamos que a[42]es un charpuntero y *a[42]es un carácter.
Michel Billaud
2

Yo diría que la convención inicial era poner la estrella en el lado del nombre del puntero (lado derecho de la declaración

Puede seguir las mismas reglas, pero no es un gran problema si coloca estrellas en el lado de la letra. Recuerda que la consistencia es importante, por eso siempre pero la estrella del mismo lado independientemente del lado que hayas elegido.

TheDrev
fuente
Bueno, el analizador parece permitir cualquiera de las variantes, pero si Dennis y Linus dicen que debería estar en el lado derecho, es bastante convincente. Pero aún así, nos falta algo de razón, y luego también la explicación de por qué se hace esto. Es un poco como la situación de tabulación versus espacio, excepto que una se resolvió, porque las personas que usan espacios en lugar de tabulaciones, ganan más dinero, según StackOverflow ... :-)
shevy
1

El puntero es un modificador del tipo. Es mejor leerlos de derecha a izquierda para comprender mejor cómo el asterisco modifica el tipo. 'int *' se puede leer como "puntero a int '. En declaraciones múltiples, debe especificar que cada variable es un puntero o se creará como una variable estándar.

1, 2 y 3) La prueba es de tipo (int *). El espacio en blanco no importa.

4, 5 y 6) La prueba es de tipo (int *). Test2 es de tipo int. Nuevamente, el espacio en blanco es intrascendente.

Ron Warholic
fuente
-3

Como regla general, mucha gente parece comprender estos conceptos de la siguiente manera: En C ++, una gran cantidad de significado semántico se deriva de la unión a la izquierda de palabras clave o identificadores.

Toma por ejemplo:

int const bla;

La constante se aplica a la palabra "int". Lo mismo ocurre con los asteriscos de los punteros, se aplican a la palabra clave que queda a la izquierda de ellos. ¿Y el nombre de la variable real? Sí, eso lo declara lo que queda de él.

mstrobl
fuente
1
Esto no responde a la pregunta. Peor aún, si tratamos de inferir una respuesta de él, entonces implica que el asterisco se une al tipo a su izquierda, que como todos los demás han dicho, es falso. Se une al nombre de la variable única a su derecha.
underscore_d