c++ - que - punteros y arreglos en c
¿Por qué el operador de desreferencia(*) también se usa para declarar un puntero? (6)
No estoy seguro de si esta es una pregunta de programación adecuada, pero es algo que siempre me ha molestado, y me pregunto si soy el único.
Cuando inicialmente aprendí C ++, entendí el concepto de referencias, pero las indicaciones me confundieron. ¿Porque preguntas? Por la forma en que declaras un puntero.
Considera lo siguiente:
void foo(int* bar)
{
}
int main()
{
int x = 5;
int* y = NULL;
y = &x;
*y = 15;
foo(y);
}
La función foo(int*)
toma un puntero int
como parámetro. Como he declarado y
como int
pointer, puedo pasar y
a foo
, pero cuando aprendí C ++ por primera vez, asocié el símbolo *
con la desreferenciación, por lo que calculé que era necesario pasar una referencia desreferenciada. Intentaría pasar *y
a foo
, lo que obviamente no funciona.
¿No hubiera sido más fácil tener un operador separado para declarar un puntero? (o para desreferenciar). Por ejemplo:
void test(int@ x)
{
}
Debido a que el comité, y aquellos que desarrollaron C ++ en las décadas previas a su estandarización, decidieron que *
debería conservar sus tres significados originales :
- Un tipo de puntero
- El operador de desreferencia
- Multiplicación
Tiene razón al sugerir que los significados múltiples de *
(y, de manera similar, &
) son confusos. He sido de la opinión durante algunos años de que son una barrera importante para la comprensión de los recién llegados al idioma.
¿Por qué no elegir otro símbolo para C ++?
La compatibilidad con versiones anteriores es la causa raíz ... es mejor reutilizar símbolos existentes en un nuevo contexto que romper programas C traduciendo significados nuevos a operadores que no pertenecían previamente.
¿Por qué no elegir otro símbolo para C?
Es imposible saberlo con certeza, pero hay varios argumentos que pueden ser, y han sido, hechos. En primer lugar está la idea de que:
cuando [un] identificador aparece en una expresión de la misma forma que el declarador, produce un objeto del tipo especificado. {K & R, p216}
Esta es también la razón por la que los programadores de C tienden a [citar necesidad] prefieren alinear sus asteriscos a la derecha en lugar de a la izquierda, es decir:
int *ptr1; // roughly C-style
int* ptr2; // roughly C++-style
aunque ambas variedades se encuentran en programas de ambos idiomas, de forma variada.
En El desarrollo del lenguaje C , Dennis Ritchie explica su razonamiento de esta manera:
La segunda innovación que distingue más claramente a C de sus predecesores es esta estructura de tipo más completa y especialmente su expresión en la sintaxis de las declaraciones ... dado un objeto de cualquier tipo, debería ser posible describir un nuevo objeto que reúne varios en una matriz , lo produce desde una función, o es un puntero a él ... [Esto] dio lugar a una sintaxis de declaración para nombres que reflejan el de la sintaxis de expresión en la que normalmente aparecen los nombres. Así,
int i, *pi, **ppi;
declare un entero, un puntero a un entero, un puntero a un puntero a un entero. La sintaxis de estas declaraciones refleja la observación de quei, *pi, and **ppi
producen un tipoint
cuando se usan en una expresión.Del mismo modo,
int f(), *f(), (*f)();
declara una función que devuelve un entero, una función que devuelve un puntero a un entero, un puntero a una función que devuelve un entero.int *api[10], (*pai)[10];
declara una matriz de punteros a enteros y un puntero a una matriz de enteros.En todos estos casos, la declaración de una variable se asemeja a su uso en una expresión cuyo tipo es el nombrado al principio de la declaración .
Un accidente de sintaxis contribuyó a la complejidad percibida del lenguaje. El operador de direccionamiento indirecto, deletreado * en C, es sintácticamente un operador de prefijo unario, al igual que en BCPL y B. Esto funciona bien en expresiones simples, pero en casos más complejos, se requieren paréntesis para dirigir el análisis sintáctico. Por ejemplo, para distinguir la indirección a través del valor devuelto por una función de llamar a una función designada por un puntero, se escribe
*fp() and (*pf)()
respectivamente. El estilo utilizado en las expresiones lleva a las declaraciones, por lo que los nombres pueden ser declarados
int *fp(); int (*pf)();
En casos más ornamentados pero realistas, las cosas empeoran:
int *(*pfp)();
es un puntero a una función que devuelve un puntero a un número entero.Hay dos efectos que ocurren. Lo más importante, C tiene un conjunto relativamente rico de formas de describir tipos (comparado, por ejemplo, con Pascal). Las declaraciones en idiomas tan expresivos como C-Algol 68, por ejemplo, describen objetos igualmente difíciles de entender, simplemente porque los objetos mismos son complejos. Un segundo efecto se debe a los detalles de la sintaxis. Las declaraciones en C deben leerse en un estilo de "adentro hacia afuera" que a muchos les resulta difícil de entender. Sethi [Sethi 81] observó que muchas de las declaraciones y expresiones anidadas se volverían más simples si el operador de indirección hubiera sido tomado como un operador de sufijo en lugar del prefijo, pero para entonces era demasiado tarde para cambiar.
Esa es una decisión del lenguaje que es anterior a C ++, ya que C ++ lo heredó de C. Una vez escuché que la motivación era que la declaración y el uso serían equivalentes, es decir, se daría una declaración int *p;
la expresión *p
es de tipo int
de la misma manera que con int i;
la expresión i
es de tipo int
.
Jaja, siento tu dolor, tuve exactamente el mismo problema.
Pensé que un puntero debería declararse como &int
porque tiene sentido que un puntero sea una dirección de algo.
Después de un tiempo pensé, todo tipo de C debe leerse al revés , como
int * const a
es para mí
a constant something, when dereferenced equals an int
. Algo que tiene que ser desreferenciado , tiene que ser un puntero.
La razón es más clara si la escribes así:
int x, *y;
Es decir, tanto x como * y son enteros. Por lo tanto, y es un int *.
Página 65 de la Programación de Expert C: Deep C Secrets incluye lo siguiente: Y luego, existe la filosofía C de que la declaración de un objeto debería parecerse a su uso.
La página 216 de The C Programming Language, 2ª edición (también conocida como K & R) incluye: Un declarador se lee como una afirmación de que cuando su identificador aparece en una expresión de la misma forma que el declarador, produce un objeto del tipo especificado.
Prefiero la forma en que van der Linden lo dice.