c++ - perros - ¿Cuántos niveles de punteros podemos tener?
pointers significado (14)
¿Cuántos punteros ( *
) se permiten en una sola variable?
Consideremos el siguiente ejemplo.
int a = 10;
int *p = &a;
Del mismo modo podemos tener
int **q = &p;
int ***r = &q;
y así.
Por ejemplo,
int ****************zz;
Cada desarrollador de C ++ debería haber oído hablar del (in) famoso programador de tres estrellas
Y realmente parece haber alguna "barrera de puntero" mágica que tiene que ser camuflada
Cita de C2:
Programador tres estrellas
Un sistema de clasificación para programadores C Cuanto más indirectos sean sus punteros (es decir, cuanto más "*" antes de sus variables), mayor será su reputación. Los programadores C sin estrellas prácticamente no existen, ya que prácticamente todos los programas no triviales requieren el uso de punteros. La mayoría son programadores de una estrella. En los viejos tiempos (bueno, soy joven, por lo menos estos me parecen viejos al menos), de vez en cuando uno encontraba un código hecho por un programador de tres estrellas y temblaba de asombro. Algunas personas incluso afirmaron haber visto el código de tres estrellas con indicadores de función involucrados, en más de un nivel de direccionamiento indirecto. Sonaba tan real como los ovnis para mí.
Como ha dicho la gente, no hay límite "en teoría". Sin embargo, por interés lo ejecuté con g ++ 4.1.2, y funcionó con un tamaño de hasta 20,000. Sin embargo, la compilación fue bastante lenta, así que no lo intenté más. Así que supongo que g ++ tampoco impone ningún límite. (Intente establecer size = 10
y buscar en ptr.cpp si no es inmediatamente obvio).
g++ create.cpp -o create ; ./create > ptr.cpp ; g++ ptr.cpp -o ptr ; ./ptr
crear.cpp
#include <iostream>
int main()
{
const int size = 200;
std::cout << "#include <iostream>/n/n";
std::cout << "int main()/n{/n";
std::cout << " int i0 = " << size << ";";
for (int i = 1; i < size; ++i)
{
std::cout << " int ";
for (int j = 0; j < i; ++j) std::cout << "*";
std::cout << " i" << i << " = &i" << i-1 << ";/n";
}
std::cout << " std::cout << ";
for (int i = 1; i < size; ++i) std::cout << "*";
std::cout << "i" << size-1 << " << /"//n/";/n";
std::cout << " return 0;/n}/n";
return 0;
}
Depende del lugar donde se guarden los punteros. Si están en la pila tienes un límite bastante bajo . Si lo guardas en un montón, tu límite es mucho más alto.
Mira este programa:
#include <iostream>
const int CBlockSize = 1048576;
int main()
{
int number = 0;
int** ptr = new int*[CBlockSize];
ptr[0] = &number;
for (int i = 1; i < CBlockSize; ++i)
ptr[i] = reinterpret_cast<int *> (&ptr[i - 1]);
for (int i = CBlockSize-1; i >= 0; --i)
std::cout << i << " " << (int)ptr[i] << "->" << *ptr[i] << std::endl;
return 0;
}
Crea 1M punteros y en la muestra lo que apunta a lo que es fácil notar lo que la cadena va al primer number
variable.
Por cierto Utiliza 92K
de RAM, así que imagina cuán profundo puedes llegar.
El estándar C
especifica el límite inferior:
5.2.4.1 Límites de traducción
276 La implementación podrá traducir y ejecutar al menos un programa que contenga al menos una instancia de cada uno de los siguientes límites: [...]
279 - 12 declaradores de punteros, matrices y funciones (en cualquier combinación) que modifican un tipo aritmético, de estructura, de unión o vacío en una declaración
El límite superior es la implementación específica.
En realidad, es incluso más divertido con puntero a las funciones.
#include <cstdio>
typedef void (*FuncType)();
static void Print() { std::printf("%s", "Hello, World!/n"); }
int main() {
FuncType const ft = &Print;
ft();
(*ft)();
(**ft)();
/* ... */
}
Como se ilustra here esto da:
¡Hola Mundo!
¡Hola Mundo!
¡Hola Mundo!
Y no implica una sobrecarga de tiempo de ejecución, por lo que probablemente pueda apilarlos tanto como desee ... hasta que su compilador se atragante con el archivo.
En realidad, los programas en C comúnmente hacen uso de la indirección de puntero infinito. Uno o dos niveles estáticos son comunes. La triple indirección es rara. Pero el infinito es muy común.
La indirección del puntero infinito se logra con la ayuda de una estructura, por supuesto, no con un declarador directo, lo que sería imposible. Y se necesita una estructura para que pueda incluir otros datos en esta estructura en los diferentes niveles donde esto puede terminar.
struct list { struct list *next; ... };
ahora puede tener la list->next->next->next->...->next
. Esto es realmente solo múltiples indicaciones de puntero: *(*(..(*(*(*list).next).next).next...).next).next
. Y el .next
es básicamente un noop cuando es el primer miembro de la estructura, así que podemos imaginar esto como ***..***ptr
.
Realmente no hay límite para esto porque los enlaces se pueden atravesar con un bucle en lugar de una expresión gigante como esta, y además, la estructura se puede hacer fácilmente circular.
Por lo tanto, en otras palabras, las listas vinculadas pueden ser el último ejemplo de agregar otro nivel de direccionamiento indirecto para resolver un problema, ya que lo está haciendo dinámicamente con cada operación de inserción. :)
Me gustaría señalar que producir un tipo con un número arbitrario de * ''s es algo que puede suceder con la metaprogramación de plantillas. Olvidé exactamente lo que estaba haciendo, pero se me sugirió que podía producir nuevos tipos distintos que tuvieran algún tipo de meta maniobra entre ellos utilizando tipos T * recursivos .
La metaprogramación de plantillas es un lento descenso a la locura, por lo que no es necesario dar excusas al generar un tipo con varios miles de niveles de indirección. Es solo una forma práctica de asignar los enteros peano, por ejemplo, a la expansión de plantillas como un lenguaje funcional.
No hay límite . Un puntero es una porción de memoria cuyo contenido es una dirección.
Como dijiste
int a = 10;
int *p = &a;
Un puntero a un puntero es también una variable que contiene una dirección de otro puntero.
int **q = &p;
Aquí q
es puntero a puntero que contiene la dirección de p
que ya tiene la dirección de a
.
No hay nada particularmente especial acerca de un puntero a un puntero.
Por lo tanto, no hay límite en la cadena de poniters que contienen la dirección de otro puntero.
es decir.
int **************************************************************************z;
esta permitido.
No existe tal cosa como el límite real, pero el límite existe. Todos los punteros son variables que normalmente se almacenan en la pila, no en el montón . La pila suele ser pequeña (es posible cambiar su tamaño durante algunos enlaces). Así que digamos que tienes una pila de 4MB, lo que es un tamaño bastante normal. Y digamos que tenemos un puntero que tiene un tamaño de 4 bytes (los tamaños del puntero no son los mismos según la configuración de la arquitectura, el destino y el compilador).
En este caso, 4 MB / 4 b = 1024
por lo que el número máximo posible sería 1048576, pero no debemos ignorar el hecho de que algunas otras cosas están en la pila.
Sin embargo, algunos compiladores pueden tener un número máximo de cadenas de punteros, pero el límite es el tamaño de la pila. Entonces, si aumenta el tamaño de la pila durante la vinculación con el infinito y tiene una máquina con memoria infinita que ejecuta el sistema operativo que maneja esa memoria, tendrá una cadena de punteros ilimitada.
Si usas int *ptr = new int;
y ponga el puntero en el montón, que no es la forma habitual de limitar el tamaño del montón, no la pila.
EDITAR Simplemente darse cuenta de que el infinity / 2 = infinity
. Si la máquina tiene más memoria, el tamaño del puntero aumenta. Entonces, si la memoria es infinita y el tamaño del puntero es infinito, entonces son malas noticias ... :)
No hay límite, ver ejemplo here .
La respuesta depende de lo que entiendas por "niveles de punteros". Si quiere decir "¿Cuántos niveles de direccionamiento indirecto puede tener en una sola declaración?" la respuesta es "al menos 12".
int i = 0;
int *ip01 = & i;
int **ip02 = & ip01;
int ***ip03 = & ip02;
int ****ip04 = & ip03;
int *****ip05 = & ip04;
int ******ip06 = & ip05;
int *******ip07 = & ip06;
int ********ip08 = & ip07;
int *********ip09 = & ip08;
int **********ip10 = & ip09;
int ***********ip11 = & ip10;
int ************ip12 = & ip11;
************ip12 = 1; /* i = 1 */
Si te refieres a "cuántos niveles de puntero puedes usar antes de que el programa se vuelva difícil de leer", eso es cuestión de gustos, pero hay un límite. Tener dos niveles de indirección (un puntero a un puntero a algo) es común. Más de eso se vuelve un poco más difícil de pensar fácilmente; No lo hagas a menos que la alternativa sea peor.
Si se refiere a "cuántos niveles de direccionamiento de puntero puede tener en tiempo de ejecución", no hay límite. Este punto es particularmente importante para las listas circulares, en las que cada nodo apunta al siguiente. Tu programa puede seguir los punteros para siempre.
Suena divertido de comprobar.
Visual Studio 2010 (en Windows 7), puede tener 1011 niveles antes de recibir este error:
error grave C1026: desbordamiento de la pila del analizador, programa demasiado complejo
gcc (Ubuntu), 100k +
*
sin bloqueo! Supongo que el hardware es el límite aquí.
(probado con solo una declaración de variable)
Tenga en cuenta que aquí hay dos posibles preguntas: cuántos niveles de direccionamiento indirecto de puntero podemos lograr en un tipo C y cuántos niveles de direccionamiento indirecto de puntero podemos incluir en un solo declarador.
El estándar C permite que se imponga un máximo sobre el primero (y da un valor mínimo para eso). Pero eso puede ser evitado a través de múltiples declaraciones typedef:
typedef int *type0;
typedef type0 *type1;
typedef type1 *type2; /* etc */
Entonces, en última instancia, este es un problema de implementación relacionado con la idea de qué tan grande / complejo se puede hacer un programa de C antes de que sea rechazado, lo cual es muy específico del compilador.
La regla 17.5 de la norma MISRA C 2004 prohíbe más de 2 niveles de direccionamiento indirecto de puntero.
Teóricamente
Puedes tener tantos niveles de direccionamiento como quieras.
Prácticamente:
Por supuesto, nada de lo que consume memoria puede ser indefinido, habrá limitaciones debido a los recursos disponibles en el entorno host. Así que prácticamente hay un límite máximo a lo que una implementación puede soportar y la implementación debe documentarlo adecuadamente. Por lo tanto, en todos estos artefactos, el estándar no especifica el límite máximo, pero sí especifica los límites inferiores.
Aquí está la referencia:
Estándar C99 5.2.4.1 Límites de traducción:
- 12 declaradores de puntero, matriz y función (en cualquier combinación) que modifican un tipo aritmético, de estructura, de unión o vacío en una declaración.
Esto especifica el límite inferior que debe soportar cada implementación. Tenga en cuenta que en una nota al pie la norma dice además:
18) Las implementaciones deben evitar imponer límites de traducción fijos siempre que sea posible.