¿Por qué la mayoría de los programadores de C nombran variables como esta:
int *myVariable;
en lugar de esto:
int* myVariable;
Ambos son validos. Me parece que el asterisco es una parte del tipo, no una parte del nombre de la variable. ¿Alguien puede explicar esta lógica?
c
pointers
variables
naming-conventions
WBlasko
fuente
fuente
typedefs
, pero eso agregará una complejidad innecesaria, en mi humilde opinión.Respuestas:
Son EXACTAMENTE equivalentes. Sin embargo, en
Parece obvio que myVariable tiene el tipo int * , mientras que myVariable2 tiene el tipo int . En
Puede parecer obvio que ambos son del tipo int * , pero eso no es correcto, ya que
myVariable2
tiene el tipo int .Por lo tanto, el primer estilo de programación es más intuitivo.
fuente
int* someVar
proyectos personales. Tiene más sentidoint[10] x
. Esto simplemente no es la sintaxis de C. La gramática se analiza explícitamente como:,int (*x)
y no como(int *) x
, por lo que colocar el asterisco a la izquierda es simplemente engañoso y se basa en un malentendido de la sintaxis de la declaración C.int* myVariable; int myVariable2;
lugar.Si lo miras de otra manera,
*myVariable
es de tipoint
, lo que tiene sentido.fuente
myVariable
puede ser NULL, en cuyo caso*myVariable
causa una falla de segmentación, pero no hay ningún tipo NULL.int x = 5; int *pointer = &x;
porque sugiere que establecemos el int*pointer
en algún valor, no enpointer
sí mismo.Debido a que * en esa línea se une más estrechamente a la variable que al tipo:
Como @Lundin señala a continuación, const agrega aún más sutilezas para pensar. Puede eludir esto completamente al declarar una variable por línea, que nunca es ambigua:
El equilibrio entre el código claro y el código conciso es difícil de alcanzar: una docena de líneas redundantes
int a;
tampoco son buenas. Aún así, prefiero una declaración por línea y me preocupo por combinar el código más adelante.fuente
int *const a, b;
. ¿De dónde viene el * "enlace"? El tipo dea
esint* const
, entonces, ¿cómo puedes decir que el * pertenece a la variable cuando es parte del tipo en sí?Algo que nadie ha mencionado aquí hasta ahora es que este asterisco es en realidad el " operador de desreferencia " en C.
La línea anterior no quiere decir que quiera asignar
10
aa
, significa que desea asignar10
a cualquier ubicación de memoriaa
señala. Y nunca he visto a nadie escribiendo¿Tienes? Por lo tanto, el operador de desreferencia se escribe casi siempre sin un espacio. Esto es probablemente para distinguirlo de una multiplicación dividida en varias líneas:
Aquí
*e
sería engañoso, ¿no?Bien, ahora, ¿qué significa realmente la siguiente línea:
La mayoría de la gente diría:
Significa que
a
es un puntero a unint
valor.Esto es técnicamente correcto, a la mayoría de la gente le gusta verlo / leerlo de esa manera y así es como los estándares C modernos lo definirían (tenga en cuenta que el lenguaje C en sí mismo es anterior a todos los estándares ANSI e ISO). Pero no es la única forma de verlo. También puede leer esta línea de la siguiente manera:
El valor desreferenciado de
a
es de tipoint
.De hecho, el asterisco en esta declaración también puede verse como un operador de desreferencia, lo que también explica su ubicación. Y ese
a
es un puntero que no se declara realmente, está implícito por el hecho de que lo único que puede desreferenciar realmente es un puntero.El estándar C solo define dos significados para el
*
operador:Y la indirección es solo un significado único, no hay un significado adicional para declarar un puntero, solo hay indirección, que es lo que hace la operación de desreferenciación, realiza un acceso indirecto, por lo que también dentro de una declaración como
int *a;
esta es un acceso indirecto (*
significa acceso indirecto) y, por lo tanto, la segunda declaración anterior está mucho más cerca del estándar que la primera.fuente
int a, *b, (*c)();
como algo así como "declarar los siguientes objetos comoint
: el objetoa
, el objeto señalado porb
, y el objeto devuelto de la función señalada porc
".*
enint *a;
no es un operador, y no se eliminación de referenciasa
(que no está aún definido aún)*
(sólo da un significado a la expresión total y no a la*
dentro de la expresión!). Dice que "int a;" declara un puntero, lo que hace, nunca afirmó lo contrario, pero sin un significado dado*
, leerlo como * el valor desreferenciado dea
es un int sigue siendo totalmente válido, ya que tiene el mismo significado fáctico. Nada, realmente nada escrito en 6.7.6.1 contradeciría esa afirmación.int *a;
es una declaración, no una expresión.a
no está desreferenciado porint *a;
.a
ni siquiera existe en el momento en que*
se está procesando. ¿Crees queint *a = NULL;
es un error porque desreferencia un puntero nulo?Voy a salir en un miembro aquí y decir que hay una respuesta clara a esta pregunta , tanto para las declaraciones de variables y tipos de parámetros y retorno, y es que el asterisco debe ir al lado del nombre:
int *myVariable;
. Para apreciar por qué, mire cómo declara otros tipos de símbolos en C:int my_function(int arg);
para una función;float my_array[3]
para una matrizEl patrón general, denominado declaración después del uso , es que el tipo de símbolo se divide en la parte anterior al nombre, y las partes alrededor del nombre, y estas partes alrededor del nombre imitan la sintaxis que usaría para obtener un símbolo. valor del tipo a la izquierda:
int a_return_value = my_function(729);
float an_element = my_array[2];
y:
int copy_of_value = *myVariable;
.C ++ arroja una llave en las obras con referencias, porque la sintaxis en el punto donde usa referencias es idéntica a la de los tipos de valor, por lo que podría argumentar que C ++ adopta un enfoque diferente a C. Por otro lado, C ++ conserva el mismo comportamiento de C en el caso de punteros, por lo que las referencias realmente se destacan como las extrañas a este respecto.
fuente
Eso es solo una cuestión de preferencia.
Cuando lee el código, distinguir entre variables y punteros es más fácil en el segundo caso, pero puede generar confusión cuando coloca variables y punteros de un tipo común en una sola línea (que a menudo se desalienta por las pautas del proyecto, porque disminuye la legibilidad).
Prefiero declarar punteros con su signo correspondiente junto al nombre del tipo, por ejemplo
fuente
Un gran gurú dijo una vez: "Léelo a la manera del compilador, debes hacerlo".
http://www.drdobbs.com/conversationsa-midsummer-nights-madness/184403835
De acuerdo, esto era sobre el tema de la colocación constante, pero la misma regla se aplica aquí.
El compilador lo lee como:
no como:
Si tiene el hábito de colocar la estrella al lado de la variable, hará que sus declaraciones sean más fáciles de leer. También evita los ojos como:
- Editar -
Explicar exactamente lo que quiero decir cuando digo que se analiza como
int (*a)
, eso significa que se*
une más estrechamentea
de lo que lo haceint
, de la misma manera que en la expresión se4 + 3 * 7
3
une más estrechamente7
de lo que lo hace4
debido a la mayor precedencia de*
.Con disculpas por el arte ascii, una sinopsis de la AST para analizar se
int *a
ve más o menos así:Como se muestra claramente, se
*
une más estrechamentea
ya que su antepasado común lo esDeclarator
, mientras que debe subir todo el árbolDeclaration
para encontrar un antepasado común que involucre alint
.fuente
(int*) a
.int
en este caso. Paso 2. Lea una declaración, incluyendo cualquier tipo de decoración.*a
en este caso. Paso 3 Lee el siguiente personaje. Si es una coma, consúmalo y vuelve al paso 2. Si el punto y coma se detiene. Si algo más arroja un error de sintaxis. ...int* a, b;
y obtener un par de punteros. El punto que estoy señalando es que se*
une a la variable y se analiza con ella, no con el tipo para formar el "tipo base" de la declaración. Esa también es parte de la razón por la que se introdujeron typedefs para permitirtypedef int *iptr;
iptr a, b;
crear un par de punteros. Al usar un typedef puede vincular el*
aint
.Declaration-Specifier
con las "decoraciones" en elDeclarator
para llegar al tipo final para cada variable. Sin embargo, no "mueve" las decoraciones al especificador de Declaración de lo contrarioint a[10], b;
produciría resultados completamente ridículos, analizaint *a, b[10];
comoint
*a
,
b[10]
;
. No hay otra manera de describirlo que tenga sentido.int*a
es finalmente leído por el compilador como: "a
tiene tipoint*
". A eso me refería con mi comentario original.Porque tiene más sentido cuando tienes declaraciones como:
fuente
int* a, b;
hizob
un puntero aint
. Pero no lo hicieron. Y con buen motivo. Bajo su sistema propuesto, ¿cuál es el tipo deb
en la siguiente declaraciónint* a[10], b;
:?Para declarar múltiples punteros en una línea, prefiero
int* a, * b;
que declare más intuitivamente "a" como un puntero a un número entero, y no mezcle estilos cuando también declare "b". Como alguien dijo, no declararía dos tipos diferentes en la misma declaración de todos modos.fuente
Personas que prefieren
int* x;
están tratando de forzar su código en un mundo ficticio donde el tipo está a la izquierda y el identificador (nombre) está a la derecha.Digo "ficticio" porque:
Eso puede sonar loco, pero sabes que es verdad. Aquí hay unos ejemplos:
int main(int argc, char *argv[])
significa "main
es una función que toma unint
y una matriz de punteroschar
y devuelve unint
". En otras palabras, la mayoría de la información de tipo está a la derecha. Algunas personas piensan que las declaraciones de funciones no cuentan porque de alguna manera son "especiales". OK, intentemos una variable.void (*fn)(int)
significafn
es un puntero a una función que toma unint
y no devuelve nada.int a[10]
declara 'a' como una matriz de 10int
s.pixel bitmap[height][width]
.Claramente, he seleccionado ejemplos que tienen mucha información de tipo a la derecha para aclarar mi punto. Hay muchas declaraciones donde la mayoría, si no todas, del tipo está a la izquierda, como
struct { int x; int y; } center
.Esta sintaxis de declaración surgió del deseo de K&R de que las declaraciones reflejen el uso. La lectura de declaraciones simples es intuitiva, y la lectura de las más complejas se puede dominar aprendiendo la regla derecha-izquierda-derecha (a veces llamada la regla espiral o simplemente la regla derecha-izquierda).
C es lo suficientemente simple como para que muchos programadores de C adopten este estilo y escriban declaraciones simples como
int *p
.En C ++, la sintaxis se volvió un poco más compleja (con clases, referencias, plantillas, clases de enumeración) y, como reacción a esa complejidad, verás un mayor esfuerzo para separar el tipo del identificador en muchas declaraciones. En otras palabras, es posible que vea más
int* p
declaraciones de estilo si revisa una gran franja de código C ++.En cualquier idioma, siempre puede tener el tipo en el lado izquierdo de las declaraciones de variables al (1) nunca declarar múltiples variables en la misma declaración y (2) haciendo uso de
typedef
s (o declaraciones de alias, que, irónicamente, ponen el alias identificadores a la izquierda de los tipos). Por ejemplo:fuente
(*fn)
mantienen el puntero asociado enfn
lugar del tipo de retorno.Ya respondí a una pregunta similar en CP, y, como nadie mencionó eso, también tengo que señalar que C es un lenguaje de formato libre , sea cual sea el estilo que elija, está bien, mientras que el analizador puede hacer una distinción de cada token. Esta peculiaridad de C conducen a un tipo muy especial de concurso denominado C concurso de ofuscación .
fuente
Muchos de los argumentos en este tema son completamente subjetivos y el argumento sobre "la estrella se une al nombre de la variable" es ingenuo. Aquí hay algunos argumentos que no son solo opiniones:
Los calificadores de tipo puntero olvidados
Formalmente, la "estrella" no pertenece al tipo ni al nombre de la variable, es parte de su propio elemento gramatical llamado puntero . La sintaxis formal de C (ISO 9899: 2018) es:
Donde los especificadores de declaración contienen el tipo (y el almacenamiento), y la lista de declaradores de inicio contiene el puntero y el nombre de la variable. Lo que vemos si diseccionamos aún más esta sintaxis de la lista de declarantes:
Cuando un declarador es la declaración completa, un declarador directo es el identificador (nombre de la variable), y un puntero es la estrella seguida de una lista de calificador de tipo opcional que pertenece al puntero mismo.
Lo que hace que los diversos argumentos de estilo sobre "la estrella pertenece a la variable" sean inconsistentes, es que se han olvidado de estos calificadores de tipo puntero.
int* const x
,int *const x
Oint*const x
?Considere
int *const a, b;
, ¿cuáles son los tipos dea
yb
? Ya no es tan obvio que "la estrella pertenece a la variable" por más tiempo. Más bien, uno comenzaría a reflexionar sobre dóndeconst
pertenece.Definitivamente puede hacer un argumento sólido de que la estrella pertenece al calificador de tipo de puntero, pero no mucho más allá de eso.
La lista de calificadores de tipo para el puntero puede causar problemas a quienes usan el
int *a
estilo. Aquellos que usan punteros dentro de untypedef
(que no deberíamos, ¡muy mala práctica!) Y piensan que "la estrella pertenece al nombre de la variable" tienden a escribir este error muy sutil:Esto se compila limpiamente. Ahora puede pensar que el código está hecho correctamente. ¡No tan! Este código es accidentalmente una corrección falsa constante.
El tipo de
foo
es en realidadint*const*
: el puntero más externo se hizo de solo lectura, no los datos apuntados. Entonces dentro de esta función podemos hacer**foo = n;
y cambiará el valor de la variable en la persona que llama.Esto se debe a que en la expresión
const bad_idea_t *foo
, ¡*
no pertenece al nombre de la variable aquí! En pseudocódigo, esta declaración de parámetro debe leerse comoconst (bad_idea_t *) foo
y no como(const bad_idea_t) *foo
. La estrella pertenece al tipo de puntero oculto en este caso: el tipo es un puntero y un puntero calificado const se escribe como*const
.Pero entonces la raíz del problema en el ejemplo anterior es la práctica de ocultar punteros detrás de ay
typedef
no del*
estilo.En cuanto a la declaración de múltiples variables en una sola línea
Declarar múltiples variables en una sola línea es ampliamente reconocido como una mala práctica 1) . CERT-C lo resume muy bien como:
Simplemente leyendo el inglés, el sentido común está de acuerdo en que una declaración debería ser una declaración.
Y no importa si las variables son punteros o no. Declarar cada variable en una sola línea hace que el código sea más claro en casi todos los casos.
Entonces, la discusión sobre la confusión del programador
int* a, b
es mala. La raíz del problema es el uso de múltiples declaradores, no la colocación de*
. Independientemente del estilo, deberías escribir esto en su lugar:Otro argumento sólido pero subjetivo sería que dado
int* a
el tipo dea
es sin dudaint*
la estrella pertenece al calificador de tipo.Pero básicamente mi conclusión es que muchos de los argumentos publicados aquí son solo subjetivos e ingenuos. Realmente no puede hacer un argumento válido para ninguno de los estilos: es realmente una cuestión de preferencia personal subjetiva.
1) CERT-C DCL04-C .
fuente
typedef int *bad_idea_t;
void func(const bad_idea_t bar);
Como el gran profeta Dan Saks enseña "Si siempre colocas loconst
más a la derecha posible, sin cambiar el significado semántico", esto cesa por completo ser un problema También hace que susconst
declaraciones sean más consistentes para leer. "Todo a la derecha de la palabra const es lo que es const, todo a la izquierda es su tipo". Esto se aplicará a todos los concursos en una declaración. Pruébelo conint const * * const x;
Considerar
Esto no se traduce en
En realidad se traduce en
Esto hace que la
int *x
notación sea algo inconsistente.fuente
new
es un operador de C ++. Su pregunta está etiquetada "C" y no "C ++".