valor sentencia referencia hace float dev definir declarar constantes como calificador c++ c pointers const

c++ - sentencia - ¿Cuál es el punto de const punteros?



definir un valor en c++ (17)

... hoy me di cuenta de que los indicadores se transmiten por valor a las funciones, lo que tiene sentido.

(imo) realmente no tiene sentido como el predeterminado. el valor predeterminado más razonable es pasar como puntero no reasignable ( int* const arg ). es decir, hubiera preferido que pasaran los punteros ya que los argumentos se declaraban implícitamente const.

Entonces, ¿cuál es la ventaja de 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 const bastante fácil. la alteración de la dirección es atípica. es más claro crear una variable local si tu intención es modificar la dirección. también, la manipulación del puntero sin formato es una forma fácil de introducir errores.

por lo tanto, es más fácil pasar por una dirección inmutable y crear una copia (en esos casos atípicos) cuando desee modificar la dirección a la que apunta el argumento:

void func(int* const arg) { int* a(arg); ... *a++ = value; }

agregando que local es prácticamente gratis, y reduce la posibilidad de errores, al tiempo que mejora la legibilidad.

en un nivel superior: si está manipulando el argumento como una matriz, normalmente es más claro y menos propenso a errores para que el cliente declare el argumento como un contenedor / colección.

en general, agregar const a valores, argumentos y direcciones es una buena idea porque no siempre se dan cuenta de los efectos secundarios, que el compilador aplica con mucho gusto. por lo tanto, es tan útil como const como se usa en otros varios casos (por ejemplo, la pregunta es similar a ''¿Por qué debo declarar que los valores son const?''). afortunadamente, también tenemos referencias, que no se pueden reasignar.

No estoy hablando de punteros para construir valores, sino de punteros.

Estoy aprendiendo C y C ++ más allá de las cosas más básicas y hasta hoy me di cuenta de que los punteros se transmiten 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 dice:

void foo(int* const ptr);

Dentro de tal función no puedes hacer que ptr señale algo más porque es const y no quieres 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?


Creo que esto evitaría que el código incremente o disminuya el puntero dentro del cuerpo de la función.


El calificador const nivel superior se descarta en 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 const, no se modifica dentro del cuerpo de la función.


Hay mucho para la palabra clave const , es bastante compleja. En general, agregar una gran cantidad de const a su programa se considera una buena práctica de programación, busque en la web "const correctness" y encontrará mucha información al respecto.

La palabra clave const es un llamado "calificador de tipo", otros son volatile y restrict . Al menos volátil sigue las mismas reglas (confusas) que const.

En primer lugar, la palabra clave const tiene dos propósitos. El más obvio es proteger los datos (y los indicadores) del uso indebido intencional o accidental al hacerlos de solo lectura. Cualquier intento de modificar una variable const 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, es decir, garantizar que se asigne una cierta variable dentro de dicha memoria, por ejemplo, podría ser EEPROM o flash. Estos se conocen como memorias no volátiles, NVM. Una variable asignada en NVM seguirá, por supuesto, todas las reglas de una variable const.

Hay varias formas diferentes de usar la palabra clave const :

Declara una variable constante.

Esto se puede hacer ya sea como

const int X=1; or int const X=1;

Estas dos formas son completamente equivalentes . El último estilo se considera de mal estilo y no debe usarse.

La razón por la cual la segunda fila se considera estilo incorrecto es probablemente porque los "especificadores de clase de almacenamiento" como estáticos y externos también pueden declararse después del tipo real, int static etc. Pero hacerlo para los especificadores de clase de almacenamiento está etiquetado como característica obsoleta por el comité C (borrador ISO 9899 N1539, 6.11.5). Por lo tanto, en aras de la coherencia, tampoco se deben escribir calificadores de tipo de esa manera. No sirve para otro fin sino confundir al lector de todos modos.

Declara un puntero a una variable constante.

const int* ptr = &X;

Esto significa que el contenido de ''X'' no se puede modificar. Esta es la forma normal de declarar punteros como este, principalmente como parte de los parámetros de función para "const correctness". Como ''X'' en realidad no tiene que declararse como const, podría ser cualquier variable. En otras palabras, siempre puede "actualizar" una variable a const. Técnicamente, C también permite la degradación de const a una variable simple mediante typecasts explícitos, pero hacerlo se considera una mala programación y los compiladores suelen dar advertencias contra ello.

Declarar un puntero constante

int* const ptr = &X;

Esto significa que el puntero en sí mismo es constante. Puede modificar lo que apunta, pero no puede modificar el puntero. Esto no tiene muchos usos, hay algunos, como asegurar que un puntero-apunta-a (puntero-a-puntero) no tiene su dirección cambiada mientras pasó como parámetro a una función. Tendrás que escribir algo no muy legible como este:

void func (int*const* ptrptr)

Dudo que muchos programadores C puedan obtener la const y * directamente allí. Sé que no puedo, tuve que consultar con GCC. Creo que es por eso que rara vez se ve 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 de puntero se declare en la memoria de solo lectura, por ejemplo, podría querer declarar algún tipo de tabla de búsqueda basada en puntero y asignarla en NVM.

Y, por supuesto, como se indica en otras respuestas, los punteros constantes también se pueden usar para imponer "const correctness".

Declarar un puntero constante a datos constantes

const int* const ptr=&X;

Estos son los dos tipos de punteros descritos anteriormente combinados, con todos los atributos de ambos.

Declarar una función de miembro de solo lectura (C ++)

Como esto está etiquetado como C ++, también debería mencionar que puede declarar las funciones miembro de una clase como const. Esto significa que la función no tiene permitido modificar a ningún otro miembro de la clase cuando se invoca, lo que impide que el programador de la clase cometa errores accidentales, pero también informa a la persona que llama de la función miembro que no se equivocarán. arriba llamándolo. La sintaxis es:

void MyClass::func (void) const;


La misma pregunta puede hacerse sobre cualquier otro tipo (no solo punteros):

/* Why is n const? */ const char *expand(const int n) { if (n == 1) return "one"; if (n == 2) return "two"; if (n == 3) return "three"; return "many"; }


Me refiero al uso de solo argumentos const porque esto permite más verificaciones del compilador: si reasigno accidentalmente un valor de argumento dentro de la función, el compilador me muerde.

Raramente reutilizo las variables, es más limpio crear nuevas variables para contener nuevos valores, así que esencialmente todas mis declaraciones de variables son const (excepto en algunos casos como variables de bucle donde const 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 const para los parámetros dentro de la función.

Ejemplo:

// foo.h int frob(int x);

// foo.cpp int frob(int const x) { MyConfigType const config = get_the_config(); return x * config.scaling; }

Observe cómo tanto el argumento como la variable local son const . Ninguno es necesario pero con funciones que son incluso un poco más grandes, esto me ha salvado repetidamente de cometer errores.


No hay nada especial acerca de los punteros en donde nunca quisieras que fueran const. Del mismo modo que puede tener los valores int constantes de miembro de clase, también puede tener punteros constantes por motivos similares: desea asegurarse de que nadie cambie nunca lo que se está señalando. Algunas referencias de C ++ abordan esto, pero el comportamiento del puntero se hereda de C.


Pasar un puntero de const a una función tiene poco sentido, ya que se pasará de todos modos por el valor. Es solo una de esas cosas permitidas por el diseño general del lenguaje. Prohibirlo solo porque no tiene sentido simplemente haría que el lenguaje sea específico. más grande.

Si estás 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.


Si realiza sistemas embebidos o programación de controladores de dispositivo donde tiene dispositivos mapeados en memoria, entonces se usan a menudo ambas formas de ''const'', una para evitar que se reasigne el puntero (ya que apunta a una dirección de hardware fija) y, si el periférico registrarlo señala que es un registro de hardware de solo lectura, entonces otro const detectará muchos errores en tiempo de compilación en lugar de tiempo de ejecución.

Un registro de chip periférico de solo 16 bits de solo lectura podría ser algo así como:

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;


Su pregunta es realmente más acerca de por qué definir cualquier variable como un parámetro no consistente const puntero a 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 la diferencia como en muchos otros casos cuando declara una variable local como const pero pone una restricción de que no puede modificar esta variable.


Su pregunta toca algo más general: ¿deberían los argumentos de función ser const?

Los argumentos de constness of value (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 la siguiente:

void foo(T);

Depende totalmente del implementador de la función si quiere usar la variable del argumento del alcance de las funciones de una manera mutable o constante:

// implementation 1 void foo(T const x) { // I won''t touch x T y = x; // ... } // implementation 2 void foo(T x) { // l33t coding skillz while (*x-- = zap()) { /* ... */ } }

Entonces, siga la regla simple de nunca poner const en la declaración (encabezado), y ponerlo en la definición (implementación) si no quiere o no necesita modificar la variable.


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. pasando este puntero a una subfunción que acepta una referencia de puntero no const (y por lo tanto podría cambiar el puntero como void f(int *&p) ), pero estoy de acuerdo, que la utilidad es algo limitada en este caso.


Tienes razón, para quien llama no hace ninguna diferencia. Pero para el escritor de la función, puede ser una red de seguridad "está bien, necesito asegurarme de no señalar este punto a la cosa incorrecta". No es muy útil, pero tampoco inútil.

Es básicamente lo mismo que tener una int const the_answer = 42 en tu programa.


Un ejemplo de donde un puntero de const es altamente aplicable se puede demostrar de esta manera. Considere que tiene una clase con una matriz dinámica dentro y desea pasar el acceso de los usuarios a la matriz, pero sin otorgarles el derecho de cambiar el puntero. Considerar:

#include <new> #include <string.h> class TestA { private: char *Array; public: TestA(){Array = NULL; Array = new (std::nothrow) char[20]; if(Array != NULL){ strcpy(Array,"Input data"); } } ~TestA(){if(Array != NULL){ delete [] Array;} } char * const GetArray(){ return Array; } }; int main() { TestA Temp; printf("%s/n",Temp.GetArray()); Temp.GetArray()[0] = '' ''; //You can still modify the chars in the array, user has access Temp.GetArray()[1] = '' ''; printf("%s/n",Temp.GetArray()); }

Que produce:

Datos de entrada
poner datos

Pero si intentamos esto:

int main() { TestA Temp; printf("%s/n",Temp.GetArray()); Temp.GetArray()[0] = '' ''; Temp.GetArray()[1] = '' ''; printf("%s/n",Temp.GetArray()); Temp.GetArray() = NULL; //Bwuahahahaa attempt to set it to null }

Obtenemos:

error: lvalue requerido como operando de asignación izquierdo // ¡Drat frustrado nuevamente!

Así que claramente podemos modificar el contenido de la matriz, pero no el puntero de la matriz. Es bueno si quiere asegurarse de que el puntero tenga un estado consistente cuando se lo devuelva al usuario. Sin embargo, hay una trampa:

int main() { TestA Temp; printf("%s/n",Temp.GetArray()); Temp.GetArray()[0] = '' ''; Temp.GetArray()[1] = '' ''; printf("%s/n",Temp.GetArray()); delete [] Temp.GetArray(); //Bwuahaha this actually works! }

Todavía podemos eliminar la referencia de memoria del puntero, incluso si no podemos modificar el puntero.

Por lo tanto, si desea que la referencia de memoria apunte siempre 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 anotar 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 devuelve una referencia al puntero (supongo que asumí que GetArray estaba refiriendo una referencia), por ejemplo:

class TestA { private: char *Array; public: TestA(){Array = NULL; Array = new (std::nothrow) char[20]; if(Array != NULL){ strcpy(Array,"Input data"); } } ~TestA(){if(Array != NULL){ delete [] Array;} } char * const &GetArray(){ return Array; } //Note & reference operator char * &GetNonConstArray(){ return Array; } //Note non-const }; int main() { TestA Temp; Temp.GetArray() = NULL; //Returns error Temp.GetNonConstArray() = NULL; //Returns no error }

Volverá en el primero dando como resultado un error:

error: asignación de la ubicación de solo lectura ''Temp.TestA :: GetArray ()''

Pero el segundo ocurrirá alegremente a pesar de las posibles consecuencias en la parte inferior.

Obviamente, se planteará la pregunta ''¿por qué querrías 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 const . Una referencia a un puntero de const No he encontrado una situación que lo justifique (a menos que tal vez como variables de referencia const declaradas en lugar de devolver tipos?).

Considere si tenemos una función que toma un puntero const (contra uno que no lo hace):

class TestA { private: char *Array; public: TestA(){Array = NULL; Array = new (std::nothrow) char[20]; if(Array != NULL){ strcpy(Array,"Input data"); } } ~TestA(){if(Array != NULL){ delete [] Array;} } char * const &GetArray(){ return Array; } void ModifyArrayConst(char * const Data) { Data[1]; //This is okay, this refers to Data[1] Data--; //Produces an error. Don''t want to Decrement that. printf("Const: %c/n",Data[1]); } void ModifyArrayNonConst(char * Data) { Data--; //Argh noo what are you doing?! Data[1]; //This is actually the same as ''Data[0]'' because it''s relative to Data''s position printf("NonConst: %c/n",Data[1]); } }; int main() { TestA Temp; Temp.ModifyArrayNonConst("ABCD"); Temp.ModifyArrayConst("ABCD"); }

El error en la const produce un mensaje así:

error: disminución del parámetro de solo lectura ''Datos''

Lo cual es bueno ya que probablemente no queremos hacer eso, a menos que deseemos causar los problemas indicados en los comentarios. Si editamos la disminución en la función const, ocurre lo siguiente:

NonConst: A
Const: B

Claramente, aunque A es ''Datos [1]'', está siendo tratado como ''Datos [0]'' porque el puntero No Const permitió la operación de decremento. Con la implementación implementada, como escribe otra persona, detectamos el error potencial antes de que ocurra.

Otra consideración principal es que un puntero const se puede usar como una pseudo referencia, en el sentido de que lo que señala la referencia no se puede cambiar (uno se pregunta si tal vez fue así como se implementó). Considerar:

int main() { int A = 10; int * const B = &A; *B = 20; //This is permitted printf("%d/n",A); B = NULL; //This produces an error }

Al intentar compilar, produce el siguiente error:

error: asignación de la variable de solo lectura ''B''

Lo cual es probablemente algo malo si se quería una referencia constante a A. Si B = NULL está comentado, el compilador nos dejará modificar felizmente *B y, por lo tanto, A. Esto podría no parecerle útil con los ints, pero considere si tenía una única posición de una aplicación gráfica en la que deseara un puntero inmodificable que hiciera referencia a es por lo que podrías pasar.

Su uso es variable (disculpe el juego de palabras no intencionado), pero se usa correctamente, es otra herramienta en la caja para ayudar con la programación.


int iVal = 10; int * const ipPtr = & iVal;

Al igual que una variable const normal, un puntero const se debe inicializar a un valor después de la declaración, y su valor no se puede cambiar.

Esto significa que un puntero const siempre apuntará al mismo valor. En el caso anterior, ipPtr siempre apuntará a la dirección de iVal. Sin embargo, como el valor al que se apunta sigue siendo no const, es posible cambiar el valor al que se apunta desreferenciando el puntero:

* ipPtr = 6; // permitido, ya que pnPtr apunta a una int no-const


Tipos de declaración de variables como-
(1) Declarar una variable constante.
DataType const varibleName;

int const x; x=4; //you can assign its value only One time (2) Declarar un puntero a una variable constante
const dataType* PointerVaribleName=&X;

const int* ptr = &X; //Here pointer variable refer contents of ''X'' that is const Such that its cannot be modified dataType* const PointerVaribleName=&X;

int* const ptr = &X; //Here pointer variable itself is constant Such that value of ''X'' can be modified But pointer can''t be modified


const es una herramienta que deberías utilizar en la búsqueda de un concepto de C ++ muy importante:

Encuentre errores en tiempo de compilación, en lugar de en tiempo de ejecución, haciendo que el compilador aplique lo que quiere decir.

Aunque no cambia la funcionalidad, la adición de const genera un error de compilación cuando estás haciendo cosas que no querías hacer. Imagina el siguiente error tipográfico:

void foo(int* ptr) { ptr = 0;// oops, I meant *ptr = 0 }

Si usa int* const , esto generaría un error de compilación porque está cambiando el valor a ptr . Agregar restricciones a través de la sintaxis es algo bueno en general. Simplemente no lleves demasiado lejos: el ejemplo que diste es un caso en el que la mayoría de las personas no se molestan en utilizar const .