Quantos níveis de pointers podemos ter?

Quantos pointers ( * ) são permitidos em uma única variável?

Vamos considerar o seguinte exemplo.

 int a = 10; int *p = &a; 

Da mesma forma podemos ter

 int **q = &p; int ***r = &q; 

e assim por diante.

Por exemplo,

 int ****************zz; 

O padrão C especifica o limite inferior:

5.2.4.1 Limites de tradução

276 A implementação deve ser capaz de traduzir e executar pelo menos um programa que contenha pelo menos uma instância de cada um dos seguintes limites: […]

279 – 12 declaradores de ponteiro, matriz e function (em qualquer combinação) modificando um tipo de aritmética, estrutura, união ou vazio em uma declaração

O limite superior é específico da implementação.

Na verdade, os programas em C geralmente fazem uso de indiretos de ponteiro infinito. Um ou dois níveis estáticos são comuns. A indireção tripla é rara. Mas infinito é muito comum.

A indireta indireta do ponteiro é conseguida com a ajuda de um struct, naturalmente, não com um declarator direto, que seria impossível. E uma estrutura é necessária para que você possa include outros dados nessa estrutura nos diferentes níveis em que isso pode terminar.

 struct list { struct list *next; ... }; 

agora você pode ter list->next->next->next->...->next . Isso é realmente apenas indiretos de múltiplos apontadores: *(*(..(*(*(*list).next).next).next...).next).next . E o .next é basicamente um noop quando é o primeiro membro da estrutura, então podemos imaginar isso como ***..***ptr .

Não há realmente nenhum limite nisso, porque os links podem ser percorridos com um laço, em vez de uma expressão gigante como essa, e além disso, a estrutura pode ser facilmente feita circularmente.

Assim, em outras palavras, listas encadeadas podem ser o melhor exemplo de adicionar outro nível de indireção para resolver um problema, já que você está fazendo isso dinamicamente a cada operação de envio. 🙂

Teoricamente:

Você pode ter tantos níveis de indireções quanto quiser.

Praticamente:

Claro, nada que consuma memory pode ser indefinido, haverá limitações devido a resources disponíveis no ambiente host. Portanto, praticamente existe um limite máximo para o que uma implementação pode suportar e a implementação deve documentá-la apropriadamente. Portanto, em todos esses artefatos, o padrão não especifica o limite máximo, mas especifica os limites inferiores.

Aqui está a referência:

Norma C99 5.2.4.1 Limites de tradução:

– 12 declaradores de ponteiro, matriz e function (em qualquer combinação) modificando um tipo de aritmética, estrutura, união ou vazio em uma declaração.

Isso especifica o limite inferior que toda implementação deve suportar. Note que em um footenote o padrão diz ainda:

18) As implementações devem evitar a imposição de limites fixos de tradução sempre que possível.

Como as pessoas disseram, não há limite “em teoria”. No entanto, por interesse eu corri isso com o g + + 4.1.2, e ele trabalhou com tamanho até 20.000. Compile era bem lento entretanto, assim eu não tentei mais alto. Então eu acho que g + + também não impõe nenhum limite. (Tente definir o size = 10 e procurando em ptr.cpp, se não for imediatamente óbvio).

g++ create.cpp -o create ; ./create > ptr.cpp ; g++ ptr.cpp -o ptr ; ./ptr

create.cpp

 #include  int main() { const int size = 200; std::cout << "#include \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; } 

Soa divertido para verificar.

  • Visual Studio 2010 (no Windows 7), você pode ter 1011 níveis antes de receber este erro:

    erro fatal C1026: estouro de pilha do analisador, programa muito complexo

  • gcc (Ubuntu), 100k + * sem um acidente! Eu acho que o hardware é o limite aqui.

(testado com apenas uma declaração variável)

Não há limite, veja exemplo aqui .

A resposta depende do que você entende por “níveis de pointers”. Se você quer dizer “quantos níveis de indireção você pode ter em uma única declaração?” a resposta é “pelo 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 */ 

Se você quer dizer “quantos níveis de ponteiro você pode usar antes que o programa fique difícil de ler”, isso é uma questão de gosto, mas há um limite. Ter dois níveis de indireção (um ponteiro para um ponteiro para algo) é comum. Mais do que isso fica um pouco mais difícil de pensar com facilidade; não faça isso a menos que a alternativa seja pior.

Se você quer dizer “quantos níveis de indireção de ponteiro você pode ter em tempo de execução”, não há limite. Esse ponto é particularmente importante para listas circulares, nas quais cada nó aponta para o próximo. Seu programa pode seguir os pointers para sempre.

Na verdade, é ainda mais divertido com o ponteiro para funções.

 #include  typedef void (*FuncType)(); static void Print() { std::printf("%s", "Hello, World!\n"); } int main() { FuncType const ft = &Print; ft(); (*ft)(); (**ft)(); /* ... */ } 

Como ilustrado aqui, isso dá:

Olá Mundo!
Olá Mundo!
Olá Mundo!

E isso não envolve qualquer sobrecarga de tempo de execução, então você provavelmente pode empilhá-los tanto quanto quiser … até que seu compilador engasgue com o arquivo.

Não há limite . Um ponteiro é um pedaço de memory cujo conteúdo é um endereço.
Como você disse

 int a = 10; int *p = &a; 

Um ponteiro para um ponteiro também é uma variável que contém um endereço de outro ponteiro.

 int **q = &p; 

Aqui q é o ponteiro para o ponteiro segurando o endereço de p que já está segurando o endereço de a .

Não há nada particularmente especial sobre um ponteiro para um ponteiro.
Portanto, não há limite na cadeia de poniters que estão mantendo o endereço de outro ponteiro.
ie.

  int **************************************************************************z; 

é permitido.

Note que há duas questões possíveis aqui: quantos níveis de indireção de ponteiro podemos alcançar em um tipo C, e quantos níveis de indireção de ponteiro podemos encher em um único declarador.

O padrão C permite que um máximo seja imposto ao primeiro (e dá um valor mínimo para isso). Mas isso pode ser contornado através de múltiplas declarações typedef:

 typedef int *type0; typedef type0 *type1; typedef type1 *type2; /* etc */ 

Então, em última análise, esta é uma questão de implementação ligada à idéia de quão grande / complexo um programa C pode ser feito antes de ser rejeitado, o que é muito específico do compilador.

Todo desenvolvedor de C ++ deve ter ouvido falar do famoso programador de três estrelas (in)

E realmente parece haver alguma “barreira de ponteiro” mágica que tem que ser camuflada

Citação de C2:

Programador de três estrelas

Um sistema de sorting para programadores-C. Quanto mais indiretos forem seus indicadores (ou seja, quanto mais “*” antes de suas variables), maior será sua reputação. Os programadores C sem estrelas são praticamente inexistentes, já que praticamente todos os programas não triviais requerem o uso de pointers. A maioria é programadora de uma estrela. Nos velhos tempos (bem, eu sou jovem, então parece que esses são os velhos tempos, pelo menos), alguém ocasionalmente encontra um código feito por um programador de três estrelas e estremece de admiração. Algumas pessoas até alegaram ter visto código de três estrelas com pointers de function envolvidos, em mais de um nível de indireção. Soou tão real quanto os OVNIs para mim.

A regra 17.5 do padrão MISRA C de 2004 proíbe mais de dois níveis de indireção de ponteiro.

Gostaria de salientar que produzir um tipo com um número arbitrário de * é algo que pode acontecer com a metaprogramação de modelos. Eu esqueci o que eu estava fazendo exatamente, mas foi sugerido que eu poderia produzir novos tipos distintos que tivessem algum tipo de meta manobra entre eles usando tipos T * recursivos .

Template Metaprogramming é uma lenta descida à loucura, por isso não é necessário dar desculpas ao gerar um tipo com vários milhares de níveis de indireção. É apenas uma maneira prática de mapear inteiros peano, por exemplo, na expansão de modelos como uma linguagem funcional.

Não existe tal coisa como limite real, mas o limite existe. Todos os pointers são variables ​​que geralmente são armazenadas na pilha e não no heap . A pilha é geralmente pequena (é possível alterar seu tamanho durante alguma binding). Então digamos que você tenha 4MB de stack, o que é um tamanho bastante normal. E digamos que temos um ponteiro que tem 4 bytes de tamanho (os tamanhos dos pointers não são os mesmos dependendo das configurações de arquitetura, destino e compilador).

Nesse caso, 4 MB / 4 b = 1024 , o número máximo possível seria 1048576, mas não devemos ignorar o fato de que algumas outras coisas estão na pilha.

No entanto, alguns compiladores podem ter o número máximo de cadeias de pointers, mas o limite é o tamanho da pilha. Portanto, se você aumentar o tamanho da pilha durante a vinculação com o infinito e tiver uma máquina com memory infinita que execute o SO que lida com essa memory, você terá uma cadeia ilimitada de pointers.

Se você usar int *ptr = new int; e colocar seu ponteiro em heap, que não é tão comum , o limite seria tamanho de heap, não pilha.

EDITAR Apenas perceba que infinity / 2 = infinity . Se a máquina tiver mais memory, o tamanho do ponteiro aumentará. Então, se a memory é infinita e o tamanho do ponteiro é infinito, então é uma má notícia … 🙂

Depende do lugar onde você armazena pointers. Se eles estiverem na pilha, você tem um limite bastante baixo . Se você armazená-lo em heap, o limite é muito muito maior.

Olhe para este programa:

 #include  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 (&ptr[i - 1]); for (int i = CBlockSize-1; i >= 0; --i) std::cout << i << " " << (int)ptr[i] << "->" << *ptr[i] << std::endl; return 0; } 

Ele cria pointers de 1M e nos shows que apontam para o que é fácil perceber o que a cadeia vai para o primeiro number variável.

BTW. Ele usa 92K de RAM, então imagine o quão profundo você pode ir.