Siempre desordenar cómo utilizar const int*
, const int * const
y int const *
correctamente. ¿Existe un conjunto de reglas que definan lo que puedes y no puedes hacer?
Quiero saber todo lo que se debe y no se debe hacer en términos de tareas, pasar a las funciones, etc.
int *(*)(char const * const)
. Comience a la derecha de la entre paréntesis*
, entonces tenemos que mover hacia la izquierda:pointer
. Fuera de los parens, podemos mover hacia la derecha:pointer to function of ...
. Entonces tenemos que mover hacia la izquierda:pointer to function of ... that returns pointer to int
. Repita para expandir el parámetro (el...
):pointer to function of (constant pointer to constant char) that returns pointer to int
. ¿Cuál sería la declaración equivalente de una línea en un lenguaje de lectura fácil como Pascal?function(x:^char):^int
. Hay tipos de funciones que implican un puntero a una función, por lo que no es necesario especificarla, y Pascal no impone la corrección constante. Se puede leer de izquierda a derecha.Respuestas:
Léalo al revés (según lo manejado en sentido horario / regla espiral ):
int*
- puntero a intint const *
- puntero a const intint * const
- puntero constante a intint const * const
- puntero const a const intAhora el primero
const
puede estar a cada lado del tipo, así que:const int *
==int const *
const int * const
==int const * const
Si quieres volverte loco, puedes hacer cosas como esta:
int **
- puntero a puntero a intint ** const
- un puntero constante a un puntero a un intint * const *
- un puntero a un puntero constante a un intint const **
- un puntero a un puntero a una constante intint * const * const
- un puntero constante a un puntero constante a un intY para asegurarnos de que tenemos claro el significado de
const
:foo
es un puntero variable a un entero constante. Esto le permite cambiar lo que señala pero no el valor al que apunta. La mayoría de las veces esto se ve con cadenas de estilo C donde tiene un puntero a unconst char
. Puede cambiar a qué cadena apunta pero no puede cambiar el contenido de estas cadenas. Esto es importante cuando la cadena en sí está en el segmento de datos de un programa y no debe cambiarse.bar
es un puntero constante o fijo a un valor que se puede cambiar. Esto es como una referencia sin el azúcar sintáctico adicional. Debido a este hecho, usualmente usaría una referencia donde usaría unT* const
puntero a menos que necesite permitirNULL
punteros.fuente
const int x = 0; const int *const px = &x; const int *const *const p = &px;
funciona bienPara aquellos que no conocen la regla de sentido horario / espiral: comience desde el nombre de la variable, muévase sabiamente (en este caso, retroceda) al siguiente puntero o tipo . Repita hasta que termine la expresión.
Aquí hay una demostración:
fuente
void (*signal(int, void (*fp)(int)))(int);
de archive.is/SsfMXCreo que todo ya está respondido aquí, pero solo quiero agregar que debes tener cuidado con
typedef
s! NO son solo reemplazos de texto.Por ejemplo:
El tipo de
astring
eschar * const
, noconst char *
. Esta es una razón por la que siempre tiendo a ponerconst
a la derecha del tipo, y nunca al principio.fuente
typedef int* PINT
(supongo que es algo que proviene de las prácticas en C y muchos desarrolladores lo siguen haciendo). Genial, lo reemplacé*
con unP
, no acelera la escritura, además de presentar el problema que mencionas.PINT
es, de hecho, un uso bastante tonto de un typedef, especialmente porque me hace pensar que el sistema almacena cerveza de memoria. Sin embargo, los typedef son bastante útiles para manejar punteros a funciones.PVOID
,LPTSTR
materia en Win32 API!Como casi todo el mundo señaló:
¿Cuál es la diferencia entre
const X* p
,X* const p
yconst X* const p
?fuente
const X* p;
==X const * p;
como en"p points to an X that is const": the X object can't be changed via p.
Referencia constante:
Una referencia a una variable (aquí int), que es constante. Pasamos la variable como referencia principalmente, porque las referencias son más pequeñas en tamaño que el valor real, pero hay un efecto secundario y eso es porque es como un alias de la variable real. Podemos cambiar accidentalmente la variable principal a través de nuestro acceso completo al alias, por lo que lo hacemos constante para evitar este efecto secundario.
Punteros constantes
Una vez que un puntero constante apunta a una variable, no puede apuntar a ninguna otra variable.
Puntero a constante
Un puntero a través del cual no se puede cambiar el valor de una variable que señala se conoce como puntero a constante.
Puntero constante a una constante
Un puntero constante a una constante es un puntero que no puede cambiar la dirección a la que apunta y tampoco puede cambiar el valor guardado en esa dirección.
fuente
La regla general es que la
const
palabra clave se aplica a lo que la precede inmediatamente. Excepción, un comienzo seconst
aplica a lo que sigue.const int*
es lo mismoint const*
y significa "puntero a int constante" .const int* const
es lo mismoint const* const
y significa "puntero constante a int constante" .Editar: Para lo que se debe y no se debe hacer, si esta respuesta no es suficiente, ¿podría ser más preciso sobre lo que quiere?
fuente
¿Esta pregunta muestra precisamente por qué me gusta hacer las cosas de la manera que mencioné en mi pregunta ?
En resumen, creo que la forma más fácil de recordar la regla es que el "const" va después de lo que se aplica. Entonces, en su pregunta, "int const *" significa que int es constante, mientras que "int * const" significa que el puntero es constante.
Si alguien decide ponerlo al frente (por ejemplo: "const int *"), como una excepción especial en ese caso, se aplica a la cosa que está después.
A muchas personas les gusta usar esa excepción especial porque piensan que se ve mejor. No me gusta, porque es una excepción y, por lo tanto, confunde las cosas.
fuente
const T*
y se ha vuelto más natural. ¿Con qué frecuencia usas alguna vez? Por loT* const
general, una referencia funcionará bien. Me mordió todo esto una vez cuando quería unboost::shared_ptr<const T>
y en su lugar escribíconst boost::shared_ptr<T>
. Mismo problema en un contexto ligeramente diferente.const
es el tipo de const, y todo a su derecha es lo que en realidad es const. Tomaint const * const * p;
como ejemplo. No, normalmente no escribo así, esto es solo un ejemplo. Primeroconst
: escriba int, y el int que es const es el contenido del puntero const que es el contenido dep
. Segunda const: type es puntero aconst
int, const oblect es el contenido dep
Uso simple de
const
.El uso más simple es declarar una constante con nombre. Para hacer esto, se declara una constante como si fuera una variable pero se agrega
const
antes. Hay que inicializarlo inmediatamente en el constructor porque, por supuesto, no se puede establecer el valor más tarde, ya que eso lo alteraría. Por ejemplo:creará una constante entera, llamada inimaginablemente
Constant1
, con el valor 96.Dichas constantes son útiles para los parámetros que se usan en el programa pero que no necesitan cambiarse después de compilar el programa. Tiene una ventaja para los programadores sobre el
#define
comando del preprocesador C, ya que el compilador lo comprende y lo utiliza, no solo sustituye el texto del programa por el preprocesador antes de llegar al compilador principal, por lo que los mensajes de error son mucho más útiles.También funciona con punteros, pero hay que tener cuidado
const
de determinar si el puntero o lo que apunta es constante o ambos. Por ejemplo:declara que
Constant2
es un puntero variable a un entero constante y:es una sintaxis alternativa que hace lo mismo, mientras que
declara que
Constant3
es un puntero constante a un entero variable ydeclara que
Constant4
es un puntero constante a un entero constante. Básicamente, 'const' se aplica a lo que esté a su izquierda inmediata (excepto si no hay nada allí, en cuyo caso se aplica a lo que sea su derecha inmediata).ref: http://duramecho.com/ComputerInformation/WhyHowCppConst.html
fuente
Tenía la misma duda que tú hasta que encontré este libro del C ++ Guru Scott Meyers. Consulte el tercer elemento en este libro donde habla en detalles sobre el uso
const
.Solo sigue este consejo
const
aparece a la izquierda del asterisco, lo que se señala es constanteconst
aparece a la derecha del asterisco, el puntero en sí es constanteconst
aparece en ambos lados, ambos son constantesfuente
Es simple pero complicado. Tenga en cuenta que puede cambiar la
const
clasificación con cualquier tipo de datos (int
,char
,float
, etc.).Veamos los siguientes ejemplos.
const int *p
==>*p
es de solo lectura [p
es un puntero a un entero constante]int const *p
==>*p
es de solo lectura [p
es un puntero a un entero constante]int *p const
==> Declaración incorrecta . El compilador arroja un error de sintaxis.int *const p
==>p
es de solo lectura [p
es un puntero constante a un entero]. Como el punterop
aquí es de solo lectura, la declaración y la definición deben estar en el mismo lugar.const int *p const
==> Declaración incorrecta . El compilador arroja un error de sintaxis.const int const *p
==>*p
es de solo lecturaconst int *const p1
==>*p
yp
son de solo lectura [p
es un puntero constante a un entero constante]. Como el punterop
aquí es de solo lectura, la declaración y la definición deben estar en el mismo lugar.int const *p const
==> Declaración incorrecta . El compilador arroja un error de sintaxis.int const int *p
==> Declaración incorrecta . El compilador arroja un error de sintaxis.int const const *p
==>*p
es de solo lectura y es equivalente aint const *p
int const *const p
==>*p
yp
son de solo lectura [p
es un puntero constante a un entero constante]. Como el punterop
aquí es de solo lectura, la declaración y la definición deben estar en el mismo lugar.fuente
Hay muchos otros puntos sutiles que rodean la corrección constante en C ++. Supongo que la pregunta aquí ha sido simplemente sobre C, pero daré algunos ejemplos relacionados ya que la etiqueta es C ++:
A menudo pasa argumentos grandes como cadenas
TYPE const &
que evitan que el objeto sea modificado o copiado. Ejemplo:TYPE& TYPE::operator=(const TYPE &rhs) { ... return *this; }
Pero no
TYPE & const
tiene sentido porque las referencias son siempre constantes.Siempre debe etiquetar los métodos de clase que no modifican la clase como
const
, de lo contrario no puede llamar al método desde unaTYPE const &
referencia. Ejemplo:bool TYPE::operator==(const TYPE &rhs) const { ... }
Hay situaciones comunes en las que tanto el valor de retorno como el método deben ser constantes. Ejemplo:
const TYPE TYPE::operator+(const TYPE &rhs) const { ... }
De hecho, los métodos const no deben devolver datos de clase internos como una referencia a no const.
Como resultado, a menudo se debe crear un método const y un método no const utilizando la sobrecarga const. Por ejemplo, si define
T const& operator[] (unsigned i) const;
, entonces probablemente también desee la versión no constante dada por:inline T& operator[] (unsigned i) { return const_cast<char&>( static_cast<const TYPE&>(*this)[](i) ); }
Afaik, no hay funciones const en C, las funciones que no son miembros no pueden ser const en C ++, los métodos const pueden tener efectos secundarios y el compilador no puede usar funciones const para evitar llamadas de funciones duplicadas. De hecho, incluso una simple
int const &
referencia podría ser testigo del cambio al valor al que se refiere en otro lugar.fuente
La sintaxis de la declaración C y C ++ ha sido repetidamente descrita como un experimento fallido por los diseñadores originales.
En su lugar, vamos a nombrar el tipo “puntero a
Type
”; Lo llamaréPtr_
:Ahora
Ptr_<char>
es un puntero achar
.Ptr_<const char>
es un puntero aconst char
.Y
const Ptr_<const char>
es unconst
puntero aconst char
.Allí.
fuente
Para mí, la posición de,
const
es decir, si aparece a la IZQUIERDA o DERECHA o tanto a la IZQUIERDA como a la DERECHA en relación con el*
me ayuda a descubrir el significado real.A
const
a la IZQUIERDA de*
indica que el objeto señalado por el puntero es unconst
objeto.A
const
a la DERECHA de*
indica que el puntero es unconst
puntero.La siguiente tabla está tomada del Stanford CS106L Standard C ++ Programming Laboratory Course Reader.
fuente
Esto aborda principalmente la segunda línea: mejores prácticas, tareas, parámetros de función, etc.
Práctica general. Intenta hacer todo lo
const
que puedas. O para decirlo de otra manera, haga todoconst
para comenzar, y luego elimine exactamente el conjunto mínimo deconst
s necesarios para permitir que el programa funcione. Esto será de gran ayuda para lograr la corrección constante y ayudará a garantizar que no se introduzcan errores sutiles cuando las personas intentan y asignan cosas que no deben modificar.Evite const_cast <> como la plaga. Hay uno o dos casos de uso legítimo para ello, pero son muy pocos y distantes. Si está tratando de cambiar un
const
objeto, será mucho mejor encontrar a quien lo declaróconst
en el primer paso y discutir el asunto con ellos para llegar a un consenso sobre lo que debería suceder.Lo que lleva muy bien a las tareas. Puede asignar a algo solo si no es constante. Si desea asignar algo que sea constante, vea más arriba. Recuerde que en las declaraciones
int const *foo;
yint * const bar;
diferentes cosas hayconst
otras respuestas aquí que han cubierto ese tema admirablemente, por lo que no voy a entrar en él.Parámetros de la función:
Pase por valor: por ejemplo
void func(int param)
, no le importa de una forma u otra en el sitio de llamadas. Se puede argumentar que existen casos de uso para declarar la función como,void func(int const param)
pero que no tiene ningún efecto en la persona que llama, solo en la función en sí misma, ya que cualquier valor que se pase no puede ser cambiado por la función durante la llamada.Pase por referencia: por ejemplo,
void func(int ¶m)
ahora sí hace la diferencia. Como se acaba de declarar,func
está permitido cambiarparam
, y cualquier sitio de llamadas debe estar listo para lidiar con las consecuencias. Cambiar la declaración avoid func(int const ¶m)
cambios en el contrato y garantiza quefunc
ahora no puede cambiarparam
, lo que significa que lo que se pasa es lo que volverá a salir. Como otros han señalado, esto es muy útil para pasar de manera económica un objeto grande que no desea cambiar. Pasar una referencia es mucho más barato que pasar un objeto grande por valor.Pasaremos por el puntero: por ejemplo,
void func(int *param)
yvoid func(int const *param)
estos dos son más o menos sinónimo de sus homólogos de referencia, con la salvedad de que la función llamada ahora necesita para comprobar si haynullptr
menos que alguna otra garantía contractual asegurafunc
que nunca va a recibir unanullptr
enparam
.Artículo de opinión sobre ese tema. Probar la corrección en un caso como este es terriblemente difícil, es demasiado fácil cometer un error. Así que no se arriesgue, y siempre verifique los parámetros del puntero
nullptr
. Se ahorrará dolor y sufrimiento y será difícil encontrar errores a largo plazo. Y en cuanto al costo del cheque, es muy barato, y en los casos en que el análisis estático integrado en el compilador puede administrarlo, el optimizador lo eludirá de todos modos. Active la Generación de código de tiempo de enlace para MSVC, o WOPR (creo) para GCC, y obtendrá todo el programa, es decir, incluso en llamadas de función que cruzan el límite del módulo de código fuente.Al final del día, todo lo anterior hace un caso muy sólido para preferir siempre referencias a punteros. Son más seguros en todos los aspectos.
fuente
La constante con int en ambos lados hará que el puntero a int constante :
o:
const
after*
hará un puntero constante a int :En este caso, todos estos son punteros a enteros constantes , pero ninguno de estos son punteros constantes:
En este caso, todos son punteros a enteros constantes y ptr2 es puntero constante a enteros constantes . Pero ptr1 no es un puntero constante:
fuente
const
está a la izquierda de*
, se refiere al valor (no importa si esconst int
oint const
)const
está a la derecha de*
, se refiere al puntero mismoUn punto importante: ¡
const int *p
no significa que el valor al que se refiere es constante! . Significa que no puede cambiarlo a través de ese puntero (es decir, no puede asignar $ * p = ... `). El valor en sí mismo puede cambiarse de otras maneras. P.ejEstá destinado a ser utilizado principalmente en firmas de funciones, para garantizar que la función no pueda cambiar accidentalmente los argumentos pasados.
fuente
Solo por completo para C siguiendo las otras explicaciones, no estoy seguro de C ++.
x
Puntero
int *p;
int const *p;
int * const p;
int const * const p;
Puntero a puntero
int **pp;
int ** const pp;
int * const *pp;
int const **pp;
int * const * const pp;
int const ** const pp;
int const * const *pp;
int const * const * const pp;
N niveles de desreferencia
Solo sigue adelante, pero que la humanidad te excomulgue.
fuente
const int*
- puntero alint
objeto constante .Puede cambiar el valor del puntero; no puede cambiar el valor del
int
objeto, apunta el puntero.const int * const
- puntero constante alint
objeto constante .No puede cambiar el valor del puntero ni el valor del
int
objeto al que apunta el puntero.int const *
- puntero alint
objeto constante .Esta declaración es equivalente a 1.
const int*
- Puede cambiar el valor del puntero pero no puede cambiar el valor delint
objeto al que apunta el puntero.En realidad, hay una cuarta opción:
int * const
- puntero constante alint
objeto.Puede cambiar el valor del objeto al que apunta el puntero, pero no puede cambiar el valor del puntero en sí. El puntero siempre apuntará al mismo
int
objeto, pero este valor de esteint
objeto se puede cambiar.Si desea determinar un determinado tipo de construcción C o C ++, puede usar la regla en sentido horario / espiral hecha por David Anderson; pero no confundir con la Regla de Anderson hecha por Ross J. Anderson, que es algo bastante distinto.
fuente