Siempre desordenar cómo utilizar const int*, const int * consty 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
constpuede estar a cada lado del tipo, así que:const int *==int const *const int * const==int const * constSi 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:fooes 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.bares 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* constpuntero a menos que necesite permitirNULLpunteros.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
typedefs! NO son solo reemplazos de texto.Por ejemplo:
El tipo de
astringeschar * const, noconst char *. Esta es una razón por la que siempre tiendo a ponerconsta 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.PINTes, 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,LPTSTRmateria en Win32 API!Como casi todo el mundo señaló:
¿Cuál es la diferencia entre
const X* p,X* const pyconst 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
constpalabra clave se aplica a lo que la precede inmediatamente. Excepción, un comienzo seconstaplica a lo que sigue.const int*es lo mismoint const*y significa "puntero a int constante" .const int* constes lo mismoint const* consty 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* constgeneral, 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.constes 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 aconstint, const oblect es el contenido depUso 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
constantes. 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
#definecomando 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
constde determinar si el puntero o lo que apunta es constante o ambos. Por ejemplo:declara que
Constant2es un puntero variable a un entero constante y:es una sintaxis alternativa que hace lo mismo, mientras que
declara que
Constant3es un puntero constante a un entero variable ydeclara que
Constant4es 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
constaparece a la izquierda del asterisco, lo que se señala es constanteconstaparece a la derecha del asterisco, el puntero en sí es constanteconstaparece en ambos lados, ambos son constantesfuente
Es simple pero complicado. Tenga en cuenta que puede cambiar la
constclasificación con cualquier tipo de datos (int,char,float, etc.).Veamos los siguientes ejemplos.
const int *p==>*pes de solo lectura [pes un puntero a un entero constante]int const *p==>*pes de solo lectura [pes un puntero a un entero constante]int *p const==> Declaración incorrecta . El compilador arroja un error de sintaxis.int *const p==>pes de solo lectura [pes un puntero constante a un entero]. Como el punteropaquí 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==>*pes de solo lecturaconst int *const p1==>*pypson de solo lectura [pes un puntero constante a un entero constante]. Como el punteropaquí 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==>*pes de solo lectura y es equivalente aint const *pint const *const p==>*pypson de solo lectura [pes un puntero constante a un entero constante]. Como el punteropaquí 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 & consttiene 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 unconstpuntero aconst char.Allí.
fuente
Para mí, la posición de,
constes 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
consta la IZQUIERDA de*indica que el objeto señalado por el puntero es unconstobjeto.A
consta la DERECHA de*indica que el puntero es unconstpuntero.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
constque puedas. O para decirlo de otra manera, haga todoconstpara comenzar, y luego elimine exactamente el conjunto mínimo deconsts 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
constobjeto, será mucho mejor encontrar a quien lo declaróconsten 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 hayconstotras 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,funcestá 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 quefuncahora 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 haynullptrmenos que alguna otra garantía contractual asegurafuncque nunca va a recibir unanullptrenparam.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:
constafter*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
constestá a la izquierda de*, se refiere al valor (no importa si esconst intoint const)constestá a la derecha de*, se refiere al puntero mismoUn punto importante: ¡
const int *pno 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 ++.
xPuntero
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 alintobjeto constante .Puede cambiar el valor del puntero; no puede cambiar el valor del
intobjeto, apunta el puntero.const int * const- puntero constante alintobjeto constante .No puede cambiar el valor del puntero ni el valor del
intobjeto al que apunta el puntero.int const *- puntero alintobjeto constante .Esta declaración es equivalente a 1.
const int*- Puede cambiar el valor del puntero pero no puede cambiar el valor delintobjeto al que apunta el puntero.En realidad, hay una cuarta opción:
int * const- puntero constante alintobjeto.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
intobjeto, pero este valor de esteintobjeto 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