¿Cuál es el punto de los punteros constantes?

149

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?

R. Ruiz.
fuente
29
¿Qué sucede si desea garantizar, en tiempo de compilación, que el puntero no puede y no debe modificarse para que apunte a otra cosa?
Platinum Azure
25
Exactamente el mismo punto que cualquier constparámetro.
David Heffernan
2
@PlatinumAzure, tengo algunos años de experiencia en programación, pero faltan mis prácticas de software. Me alegro de haber hecho esta pregunta porque nunca he sentido la necesidad de hacer que el compilador señale mis propios errores lógicos. Mi reacción inicial en el momento en que presenté la pregunta fue "¿por qué debería importarme si se modifica el puntero ?, no afectará a la persona que llama". Hasta que leí todas las respuestas, entendí que debería importarme porque si trato de modificarlo, mi lógica de codificación y yo estamos equivocados (o había un error tipográfico como la falta del asterisco) y podría no haberme dado cuenta si el compilador no lo hizo. no me digas
R. Ruiz.
3
@ R.Ruiz. Sin duda, incluso los más experimentados de nosotros podríamos hacer con garantías adicionales de corrección. Debido a que la ingeniería de software es una forma de ingeniería donde un poco más de margen puede ser aceptable (HTTP 502 aleatorios, conexiones lentas, la imagen ocasional que no se carga no son ocasiones extraordinarias, pero una falla del motor en un avión es inaceptable y probablemente grave) programa de personas con prisa excesiva. El mismo argumento que justifica la escritura de pruebas unitarias explicará el uso de las constgarantías de corrección. Simplemente nos hace sentir más seguros de que nuestro código es indudablemente correcto.
Platinum Azure
3
Una pregunta relacionada: stackoverflow.com/questions/219914/…
Raedwald

Respuestas:

207

const es una herramienta que debe usar para buscar un concepto C ++ muy importante:

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

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

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

Si lo 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 lo lleves demasiado lejos: el ejemplo que diste es un caso en el que la mayoría de las personas no se molestan en usarlo const.

diez cuatro
fuente
8
Gracias, esta es una respuesta que me convence. Pones constante para que el compilador te advierta sobre tus propios errores de asignación. Su ejemplo es perfecto para ilustrar este concepto porque es un error común con los punteros. ¡Que tú!
R. Ruiz.
25
"Ayuda al compilador a ayudarte" es el mantra que normalmente canto por esto.
Flexo
1
+1, pero aquí hay un caso interesante en el que es discutible: stackoverflow.com/questions/6305906/…
Raedwald
3
así que es la misma respuesta que le darías a "¿Por qué las variables son privadas, cuando no puedes usarlas fuera de la clase?
Lee Louviere
Esto podría estar un poco fuera de tema, pero ¿dónde se encuentra este puntero constante en la memoria? Sé que todas las constantes se encuentran en la parte global de la memoria, pero no estoy 100% seguro acerca de una constante que se pasa como una función. gracias y perdón si esto está fuera de tema
Tomer
77

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 donde constevitarí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 constpará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 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.

Konrad Rudolph
fuente
17
+1de otro constfanático obsesionado. Sin embargo, prefiero que mis compiladores me ladren. Cometo demasiados errores y sufriría mucho si mordieran.
sbi
2
+1, recomiendo encarecidamente la constestrategia "a menos que haya una buena razón". Sin embargo, hay algunas buenas excepciones, por ejemplo, Copiar e intercambiar
Flexo
2
Si estuviera diseñando un nuevo lenguaje, los objetos declarados serían de solo lectura ("const") por defecto. Necesitaría una sintaxis especial, tal vez una palabra clave "var", para poder escribir un objeto.
Keith Thompson
@Keith Estoy tentado a decir "por supuesto". Todo lo demás es estúpido en este momento. Pero desafortunadamente, la mayoría de los diseñadores de idiomas parecen estar en desacuerdo con nosotros ... De hecho, ya he publicado tanto (en una pregunta ahora eliminada, "¿Cuál es su opinión de programación más controvertida?").
Konrad Rudolph
1
@KubaOber No veo cómo es una pesimismo en absoluto. Si luego descubre que necesita modificar lo que se pasó dentro del cuerpo de la función, simplemente elimine constel argumento y preferiblemente comente por qué. Ese pequeño trabajo adicional no justifica marcar todos los argumentos como no constpredeterminados y abrirse a todos los posibles errores que crea.
underscore_d
20

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:

void foo(T);

Depende completamente del implementador de la función si desea usar la variable de argumento de alcance de funciones de 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()) { /* ... */ }
}

Por lo tanto, siga la regla simple para nunca poner constla declaración (encabezado) y póngala en la definición (implementación) si no desea o necesita modificar la variable.

Kerrek SB
fuente
55
Estoy de acuerdo con esto, pero estoy un poco incómodo con la idea de hacer que la declaración y la definición sean diferentes. Para otras cosas que no sean const, la declaración y la (parte prototipo de) la definición generalmente serán idénticas.
Keith Thompson
@KeithThompson: Bueno, no tienes que hacerlo si no quieres. Simplemente no ponga consten la declaración. Depende completamente de usted si desea agregar el calificador en la implementación.
Kerrek SB
1
Como dije, estoy de acuerdo en que poner constla 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).
Keith Thompson
16

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.

David Rodríguez - dribeas
fuente
No lo sabia. Entonces, si trato de sobrecargar void foo (int * const ptr) con void foo (int * t ptr) obtendré un error del compilador. ¡Gracias!
R. Ruiz.
@ R.Ruiz. Si su desaceleración es void foo (int * t ptr) y su definición es void foo (int * const ptr), NO obtendrá un error del compilador.
Trevor Hickey
1
@TrevorHickey Lo harán ... pero no por la razón que piensan. int* t ptres un error de sintaxis Sin eso, los dos son idénticos para propósitos de sobrecarga.
underscore_d
14

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 = 42programa.

cnicutar
fuente
1
¿Qué importa dónde lo señalen, es un puntero asignado en la pila? La función no puede estropear eso. Tiene mucho más sentido usar punteros de solo lectura cuando se trata de punteros a punteros. ¡No es lo mismo que int const! Voy a rechazar esto solo por esa declaración engañosa. const inty int constson equivalentes, mientras que const int*y int* consttienen dos significados diferentes!
Lundin
1
@lundin Supongamos que la función es grande y tiene otras cosas. Accidentalmente, uno puede hacer que señale algo más (en la pila de la función). Esto no es problemático para la persona que llama, pero ciertamente podría ser para la persona que llama. No veo nada engañoso en mis declaraciones: " para el escritor de la función puede ser una red de seguridad".
cnicutar
1
@Lundin Acerca de la int constparte; 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.
cnicutar
1
Bueno. Acabo de escribir una respuesta propia y larga en caso de que alguien más que tú y yo estemos leyendo esta publicación. También incluí una razón por la cual int const es de mal estilo y const int es de buen estilo.
Lundin
¡Lundin, también estoy leyendo! Estoy de acuerdo const int es el mejor estilo sin embargo. @cnicutar, su primera respuesta a Lundin es lo que estaba buscando cuando envié esta pregunta. Por lo tanto, el problema no está en la persona que llama (como pensaba sin pensar), sino que const es una garantía para la persona que llama. Me gusta esta idea, gracias.
R. Ruiz.
14

La constpalabra 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 volatiley restrict. 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 constpalabra clave:

Declarar una variable constante.

Esto se puede hacer como

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

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 staticetc. 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.

const int* ptr = &X;

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

int* const ptr = &X;

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:

void func (int*const* ptrptr)

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

const int* const ptr=&X;

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:

void MyClass::func (void) const;
Lundin
fuente
8

... hoy me di cuenta de que los punteros se pasan por valor a las funciones, lo cual tiene sentido.

(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.

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 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:

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

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.

justin
fuente
44
+1 para que este sea el valor predeterminado. C ++, como la mayoría de los lenguajes, lo tiene al revés. En lugar de tener una constpalabra clave, debería tener una mutablepalabra clave (bueno, la tiene, pero con la semántica incorrecta).
Konrad Rudolph el
2
Una idea interesante que tiene const como valor predeterminado. ¡Gracias por tu respuesta!
R. Ruiz.
6

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;

Dan Haynes
fuente
5

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

jusathr
fuente
5

Se puede hacer la misma pregunta 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";
}
pmg
fuente
LOL, tienes razón. El caso de los punteros vino a mi mente primero porque pensé que eran especiales, pero son como cualquier otra variable pasada por valor.
R. Ruiz.
5

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.

zar
fuente
su comentario hace que mi pregunta sea inútil porque tiene razón: es lo mismo que tener cualquier otro parámetro como const. Puede parecer inútil, pero está ahí para ayudar al programador a tener esta restricción y evitar errores
R. Ruiz.
4

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.

Anders Abel
fuente
4

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.

MartinStettner
fuente
+1 para las optimizaciones. Al menos los libros dicen que es verdad. Lo que me gustaría es entender exactamente cuáles son esas optimizaciones
R. Ruiz.
4

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:

#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 izquierdo de la asignación // ¡Drat falló nuevamente!

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:

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 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:

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 primer resultado en un error:

error: asignación de 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í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):

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 el const produce así el mensaje:

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

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:

No
Const: A Const: B

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:

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 quisiera una referencia constante a A. Si B = NULLse 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.

SSight3
fuente
2
Temp.GetArray() = NULLfalla porque Temp.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.
Oscar Korz
@ okorz001: después de las pruebas, está en lo cierto. Sin embargo, lo anterior se aplica si devuelve una referencia al puntero en sí. Editaré mi publicación en consecuencia.
SSight3
3

No hay nada especial en los punteros donde nunca querrías que sean constantes. Del mismo modo que puede tener intvalores 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.

Mark B
fuente
3

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

Mike Christensen
fuente
3

Tipos de declarar cualquier variable 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

Pyadav
fuente
Proporcionamos una variable de puntero como constante en una función como argumento cuando no queremos cambiar su valor real
Pyadav