No estoy hablando de punteros a valores constantes, sino de punteros constantes.
Estoy aprendiendo C y C ++ más allá de las cosas muy básicas y hasta hoy me di cuenta de que los punteros se pasan por valor a las funciones, lo que tiene sentido. Esto significa que dentro de una función puedo hacer que el puntero copiado apunte a algún otro valor sin afectar el puntero original de la persona que llama.
Entonces, ¿qué sentido tiene tener un encabezado de función que diga:
void foo(int* const ptr);
Dentro de dicha función, no puede hacer que ptr apunte a otra cosa porque es constante y no desea que se modifique, sino una función como esta:
void foo(int* ptr);
¡Funciona igual de bien! porque el puntero se copia de todos modos y el puntero en la persona que llama no se ve afectado incluso si modifica la copia. Entonces, ¿cuál es la ventaja de const?
const
parámetro.const
garantías de corrección. Simplemente nos hace sentir más seguros de que nuestro código es indudablemente correcto.Respuestas:
const
es una herramienta que debe usar para buscar un concepto C ++ muy importante:Aunque no cambia la funcionalidad, agregar
const
genera un error de compilación cuando estás haciendo cosas que no querías hacer. Imagine el siguiente error tipográfico:Si lo usa
int* const
, esto generaría un error de compilación porque está cambiando el valor aptr
. Agregar restricciones a través de la sintaxis es algo bueno en general. Simplemente no lo lleves demasiado lejos: el ejemplo que diste es un caso en el que la mayoría de las personas no se molestan en usarloconst
.fuente
Me propongo usar solo
const
argumentos porque esto permite más comprobaciones del compilador: si accidentalmente reasigno un valor de argumento dentro de la función, el compilador me muerde.Raramente reutilizo variables, es más limpio crear nuevas variables para contener nuevos valores, por lo que esencialmente todas mis declaraciones de variables son
const
(excepto en algunos casos, como las variables de bucle dondeconst
evitaría que el código funcione).Tenga en cuenta que esto solo tiene sentido en la definición de una función. No pertenece a la declaración , que es lo que ve el usuario. Y al usuario no le importa si uso los
const
parámetros dentro de la función.Ejemplo:
Observe cómo son el argumento y la variable local
const
. Tampoco es necesario, pero con funciones que son incluso un poco más grandes, esto me ha salvado repetidamente de cometer errores.fuente
+1
de otroconst
fanático obsesionado. Sin embargo, prefiero que mis compiladores me ladren. Cometo demasiados errores y sufriría mucho si mordieran.const
estrategia "a menos que haya una buena razón". Sin embargo, hay algunas buenas excepciones, por ejemplo, Copiar e intercambiarconst
el argumento y preferiblemente comente por qué. Ese pequeño trabajo adicional no justifica marcar todos los argumentos como noconst
predeterminados y abrirse a todos los posibles errores que crea.Su pregunta toca algo más general: ¿Deberían los argumentos de función ser constantes?
La coherencia de los argumentos de valor (como su puntero) es un detalle de implementación , y no forma parte de la declaración de la función. Esto significa que su función es siempre esta:
Depende completamente del implementador de la función si desea usar la variable de argumento de alcance de funciones de manera mutable o constante:
Por lo tanto, siga la regla simple para nunca poner
const
la declaración (encabezado) y póngala en la definición (implementación) si no desea o necesita modificar la variable.fuente
const
, la declaración y la (parte prototipo de) la definición generalmente serán idénticas.const
en la declaración. Depende completamente de usted si desea agregar el calificador en la implementación.const
la declaración pero no la definición tiene sentido. Simplemente se siente como una falla en el lenguaje que este es el único caso en el que hacer que la declaración y la definición no sean idénticas tiene sentido. (No es la única falla de C, por supuesto).El calificador const de nivel superior se descarta en las declaraciones, por lo que las declaraciones en la pregunta declaran exactamente la misma función. Por otro lado, en la definición (implementación) el compilador verificará que si marca el puntero como constante, no se modifica dentro del cuerpo de la función.
fuente
int* t ptr
es un error de sintaxis Sin eso, los dos son idénticos para propósitos de sobrecarga.Tienes razón, para la persona que llama no hace absolutamente ninguna diferencia. Pero para el escritor de la función puede ser una red de seguridad "está bien, necesito asegurarme de no hacer que este punto sea incorrecto". No muy útil pero tampoco inútil.
Básicamente es lo mismo que tener un
int const the_answer = 42
programa.fuente
const int
yint const
son equivalentes, mientras queconst int*
yint* const
tienen dos significados diferentes!int const
parte; He estado poniendo el tipo antes de const (suena poco natural) durante algún tiempo y soy consciente de las diferencias. Este artículo puede resultar útil. Sin embargo, yo mismo tenía razones ligeramente diferentes para cambiar a este estilo.La
const
palabra clave tiene mucho, es bastante compleja. En general, agregar una gran cantidad de constantes a su programa se considera una buena práctica de programación, busque en la web "corrección constante" y encontrará mucha información al respecto.La palabra clave const es un llamado "calificador de tipo", otros son
volatile
yrestrict
. Al menos volátil sigue las mismas reglas (confusas) que const.En primer lugar, la palabra clave const tiene dos propósitos. La más obvia es proteger los datos (y los punteros) del mal uso intencional o accidental haciéndolos de solo lectura. Cualquier intento de modificar una variable constante será detectado por el compilador en tiempo de compilación.
Pero también hay otro propósito en cualquier sistema con memoria de solo lectura, a saber, garantizar que una determinada variable se asigne dentro de dicha memoria: podría ser EEPROM o flash, por ejemplo. Estos se conocen como memorias no volátiles, NVM. Por supuesto, una variable asignada en NVM seguirá todas las reglas de una variable constante.
Hay varias formas diferentes de usar la
const
palabra clave:Declarar una variable constante.
Esto se puede hacer como
Estas dos formas son completamente equivalentes . El último estilo se considera mal estilo y no debe usarse.
La razón por la cual la segunda fila se considera mal estilo, es probablemente porque los "especificadores de clase de almacenamiento" como static y extern también se pueden declarar después del tipo real,
int static
etc. Pero hacerlo para los especificadores de clase de almacenamiento se etiqueta como una característica obsoleta por el comité C (ISO 9899 N1539 borrador, 6.11.5). Por lo tanto, en aras de la coherencia, tampoco se deben escribir calificadores de tipo de esa manera. No tiene otro propósito que confundir al lector de todos modos.Declarar un puntero a una variable constante.
Esto significa que el contenido de 'X' no puede modificarse. Esta es la forma normal de declarar punteros como este, principalmente como parte de los parámetros de función para "corrección constante". Debido a que 'X' en realidad no tiene que ser declarado como constante, podría ser cualquier variable. En otras palabras, siempre puedes "actualizar" una variable a const. Técnicamente, C también permite la degradación de const a una variable simple mediante tipos de letra explícitos, pero hacerlo se considera una mala programación y los compiladores generalmente dan advertencias en su contra.
Declarar un puntero constante
Esto significa que el puntero en sí mismo es constante. Puede modificar lo que señala, pero no puede modificar el puntero en sí. Esto no tiene muchos usos, hay algunos, como asegurarse de que un puntero señalado (puntero a puntero) no tenga su dirección cambiada mientras se pasa como parámetro a una función. Tendrás que escribir algo no demasiado legible como este:
Dudo que muchos programadores de C puedan obtener el const y * justo allí. Sé que no puedo, tuve que consultar con GCC. Creo que es por eso que rara vez ves esa sintaxis para puntero a puntero, a pesar de que se considera una buena práctica de programación.
Los punteros constantes también se pueden usar para garantizar que la variable del puntero se declare en la memoria de solo lectura, por ejemplo, puede declarar algún tipo de tabla de búsqueda basada en el puntero y asignarla en NVM.
Y, por supuesto, como lo indican otras respuestas, los punteros constantes también se pueden utilizar para hacer cumplir la "corrección constante".
Declarar un puntero constante a datos constantes
Estos son los dos tipos de puntero descritos anteriormente combinados, con todos los atributos de ambos.
Declarar una función miembro de solo lectura (C ++)
Dado que esto está etiquetado como C ++, también debo mencionar que puede declarar funciones miembro de una clase como const. Esto significa que la función no puede modificar a ningún otro miembro de la clase cuando se llama, lo que evita que el programador de la clase cometa errores accidentales, pero también le informa al llamante de la función miembro que no estará molestando nada. arriba llamándolo. La sintaxis es:
fuente
(imo) realmente no tiene sentido como predeterminado. el valor predeterminado más sensato es pasar como puntero no reasignable (
int* const arg
). es decir, hubiera preferido que los punteros pasados como argumentos fueran declarados implícitamente const.La ventaja es que es bastante fácil y, a veces, no está claro cuando modifica la dirección a la que apunta el argumento, de modo que puede introducir un error cuando no es constante con bastante facilidad. alterar la dirección es atípico. es más claro crear una variable local si su intención es modificar la dirección. Además, la manipulación de puntero sin formato es una forma fácil de introducir errores.
por lo tanto, es más claro pasar por una dirección inmutable y crear una copia (en esos casos atípicos) cuando desea modificar la dirección a la que apunta el argumento:
agregando que el local es prácticamente gratuito y reduce la posibilidad de errores, al tiempo que mejora la legibilidad.
en un nivel superior: si está manipulando el argumento como una matriz, generalmente es más claro y menos propenso a errores para que el cliente declare el argumento como un contenedor / colección.
en general, es una buena idea agregar constantes a los valores, argumentos y direcciones porque no siempre se dan cuenta de los efectos secundarios, que el compilador aplica felizmente. por lo tanto, es tan útil como const como se usa en otros casos (por ejemplo, la pregunta es similar a '¿Por qué debería declarar valores const?'). Afortunadamente, también tenemos referencias que no se pueden reasignar.
fuente
const
palabra clave, debería tener unamutable
palabra clave (bueno, la tiene, pero con la semántica incorrecta).Si realiza sistemas embebidos o programación de controladores de dispositivos en los que tiene dispositivos mapeados en memoria, a menudo se utilizan ambas formas de 'constante', una para evitar que el puntero se reasigne (ya que apunta a una dirección de hardware fija) y, si el periférico el registro al que apunta es un registro de hardware de solo lectura, luego otra constante detectará muchos errores en tiempo de compilación en lugar de tiempo de ejecución.
Un registro de chip periférico de solo lectura de 16 bits podría verse así:
static const unsigned short *const peripheral = (unsigned short *)0xfe0000UL;
Luego puede leer fácilmente el registro de hardware sin tener que recurrir al lenguaje ensamblador:
input_word = *peripheral;
fuente
int iVal = 10; int * const ipPtr = & iVal;
Al igual que una variable const normal, un puntero const debe inicializarse a un valor tras la declaración, y su valor no se puede cambiar.
Esto significa que un puntero constante siempre apuntará al mismo valor. En el caso anterior, ipPtr siempre apuntará a la dirección de iVal. Sin embargo, debido a que el valor al que se apunta todavía no es constante, es posible cambiar el valor al que se apunta desreferenciando el puntero:
* ipPtr = 6; // permitido, ya que pnPtr apunta a un int no constante
fuente
Se puede hacer la misma pregunta sobre cualquier otro tipo (no solo punteros):
fuente
Su pregunta es realmente más sobre por qué definir cualquier variable como un parámetro constante de const no solo para una función. Aquí se aplican las mismas reglas que cuando define cualquier variable como constante, si es un parámetro para funcionar o una variable miembro o una variable local.
En su caso particular, funcionalmente no hace diferencia como en muchos otros casos cuando declara una variable local como constante pero pone una restricción de que no puede modificar esta variable.
fuente
Pasar un puntero constante a una función tiene poco sentido, ya que de todos modos será pasado por valor. Es solo una de esas cosas permitidas por el diseño del lenguaje general. Prohibirlo solo porque no tiene sentido solo haría que la especificación del idioma. más grande
Si está dentro de una función, por supuesto, es otro caso. Tener un puntero que no puede cambiar lo que apunta es una afirmación que aclara el código.
fuente
Supongo que una ventaja sería que el compilador puede realizar optimizaciones más agresivas dentro de la función sabiendo que este puntero no puede cambiar.
También evita, por ejemplo. pasar este puntero a una subfunción que acepta una referencia de puntero no constante (y, por lo tanto, podría cambiar el puntero
void f(int *&p)
), pero estoy de acuerdo, que la utilidad es algo limitada en este caso.fuente
Un ejemplo de donde un puntero constante es altamente aplicable puede demostrarse de esta manera. Considere que tiene una clase con una matriz dinámica dentro y desea pasar el acceso del usuario a la matriz pero sin otorgarles los derechos para cambiar el puntero. Considerar:
Que produce:
Pero si intentamos esto:
Obtenemos:
Claramente, podemos modificar el contenido de la matriz, pero no el puntero de la matriz. Es bueno si desea asegurarse de que el puntero tenga un estado coherente cuando se lo devuelva al usuario. Sin embargo, hay una trampa:
Todavía podemos eliminar la referencia de memoria del puntero, incluso si no podemos modificar el puntero en sí.
Entonces, si desea que la referencia de memoria siempre apunte a algo (IE nunca se modifica, similar a cómo funciona actualmente una referencia), entonces es altamente aplicable. Si desea que el usuario tenga acceso completo y lo modifique, entonces no-const es para usted.
Editar:
Después de observar el comentario okorz001 de no poder asignar debido a que GetArray () es un operando de valor correcto, su comentario es completamente correcto, pero lo anterior aún se aplica si tuviera que devolver una referencia al puntero (supongo que supuse que GetArray era refiriendo una referencia), por ejemplo:
Volverá en el primer resultado en un error:
Pero el segundo ocurrirá alegremente a pesar de las posibles consecuencias en la parte inferior.
Obviamente, se planteará la pregunta "¿por qué querría devolver una referencia a un puntero"? Hay casos excepcionales en los que necesita asignar memoria (o datos) directamente al puntero original en cuestión (por ejemplo, construir su propio front-end malloc / free o new / free), pero en esos casos es una referencia no constante . Una referencia a un puntero constante No me he encontrado con una situación que lo justifique (¿a menos tal vez como variables de referencia constantes declaradas en lugar de tipos de retorno?).
Considere si tenemos una función que toma un puntero constante (versus uno que no lo hace):
El error en el const produce así el mensaje:
Lo cual es bueno ya que probablemente no queremos hacer eso, a menos que queramos causar los problemas indicados en los comentarios. Si editamos el decremento en la función const, ocurre lo siguiente:
Claramente, aunque A es 'Datos [1]', se trata como 'Datos [0]' porque el puntero NonConst permitió la operación de disminución. Con el const implementado, como escribe otra persona, detectamos el error potencial antes de que ocurra.
Otra consideración principal es que un puntero constante se puede usar como una pseudo referencia, en el sentido de que no se puede cambiar el punto de referencia (uno se pregunta, si tal vez así fue como se implementó). Considerar:
Al intentar compilar, produce el siguiente error:
Lo cual es probablemente algo malo si se quisiera una referencia constante a A. Si
B = NULL
se comenta, el compilador felizmente nos permitirá modificar*B
y, por lo tanto, A. Esto puede no parecer útil con ints, pero considere si tenía una sola posición de una aplicación gráfica donde deseaba un puntero no modificable que se refiriera a él y que pudiera pasar alrededor.Su uso es variable (disculpe el juego de palabras involuntario), pero se usa correctamente, es otra herramienta en la caja para ayudar con la programación.
fuente
Temp.GetArray() = NULL
falla porqueTemp.GetArray()
es un valor r, no porque esté calificado de manera constante. Además, creo que el calificador const se elimina de todos los tipos de retorno.No hay nada especial en los punteros donde nunca querrías que sean constantes. Del mismo modo que puede tener
int
valores constantes de miembros de clase , también puede tener punteros constantes por razones similares: desea asegurarse de que nadie cambie lo que se señala. Las referencias de C ++ abordan esto, pero el comportamiento del puntero se hereda de C.fuente
Creo que esto evitaría que el código incremente o disminuya el puntero dentro del cuerpo de la función.
fuente
Tipos de declarar cualquier variable como-
(1) Declarar una variable constante.
DataType const varibleName;
const dataType* PointerVaribleName=&X;
dataType* const PointerVaribleName=&X;
fuente