Se eu criar uma variável dentro de um novo conjunto de chaves, essa variável saiu da pilha na chave de fechamento ou permanece até o final da função? Por exemplo:
void foo() {
int c[100];
{
int d[200];
}
//code that takes a while
return;
}
Vai d
ocupar memória durante a code that takes a while
seção?
Respostas:
Não, chaves não funcionam como um quadro de pilha. Em C, chaves apenas indicam um escopo de nomenclatura, mas nada é destruído e nada sai da pilha quando o controle passa dela.
Como um programador que escreve código, você pode pensar nele como se fosse um quadro de pilha. Os identificadores declarados entre chaves são acessíveis apenas entre chaves, portanto, do ponto de vista de um programador, é como se eles fossem empurrados para a pilha conforme declarados e, então, apareciam quando o escopo era encerrado. No entanto, os compiladores não precisam gerar código que empurra / abre alguma coisa na entrada / saída (e geralmente não).
Observe também que as variáveis locais podem não usar nenhum espaço de pilha: elas podem ser mantidas nos registros da CPU ou em algum outro local de armazenamento auxiliar ou ser totalmente otimizadas.
Assim, o
d
array, em teoria, poderia consumir memória para toda a função. No entanto, o compilador pode otimizá-lo ou compartilhar sua memória com outras variáveis locais cujas vidas úteis não se sobrepõem.fonte
O tempo durante o qual a variável está realmente ocupando memória é obviamente dependente do compilador (e muitos compiladores não ajustam o ponteiro da pilha quando os blocos internos são inseridos e encerrados nas funções).
No entanto, uma questão intimamente relacionada, mas possivelmente mais interessante, é se o programa tem permissão para acessar esse objeto interno fora do escopo interno (mas dentro da função que o contém), ou seja:
(Em outras palavras: o compilador pode desalocar
d
, mesmo que na prática a maioria não o faça?).A resposta é que o compilador é permitido retirar a atribuição
d
, e acessarp[0]
onde o comentário indica é um comportamento indefinido (o programa é não permitido o acesso do exterior objeto interno do âmbito interno). A parte relevante do padrão C é 6.2.4p5:fonte
Sua pergunta não é clara o suficiente para ser respondida sem ambiguidade.
Por um lado, os compiladores normalmente não fazem nenhuma alocação-desalocação de memória local para escopos de blocos aninhados. A memória local é normalmente alocada apenas uma vez na entrada da função e liberada na saída da função.
Por outro lado, quando a vida útil de um objeto local termina, a memória ocupada por esse objeto pode ser reutilizada posteriormente para outro objeto local. Por exemplo, neste código
as duas matrizes geralmente ocupam a mesma área de memória, o que significa que a quantidade total de armazenamento local necessária para a função
foo
é o que for necessário para a maior das duas matrizes, não para as duas ao mesmo tempo.Se este último se qualifica como
d
continuando a ocupar memória até o final da função no contexto de sua pergunta, é sua decisão.fonte
É dependente da implementação. Eu escrevi um programa curto para testar o que o gcc 4.3.4 faz e aloca todo o espaço da pilha de uma só vez no início da função. Você pode examinar a montagem que o gcc produz usando o sinalizador -S.
fonte
Não, d [] não estará na pilha pelo restante da rotina. Mas alloca () é diferente.
Edit: Kristopher Johnson (e Simon e Daniel) estão certos , e minha resposta inicial estava errada . Com o gcc 4.3.4.em CYGWIN, o código:
dá:
Viva e aprenda! E um teste rápido parece mostrar que o AndreyT também está correto sobre várias alocações.
Adicionado muito mais tarde : O teste acima mostra que a documentação do gcc não está correta. Durante anos, ele disse (grifo nosso):
fonte
alloca
função. Estou realmente surpreso que o cygwin gcc faria isso. Não é nem mesmo uma matriz de comprimento variável, então IDK por que você traz isso à tona.Eles podem. Eles podem não. A resposta que você realmente precisa é: nunca assuma nada. Compiladores modernos executam todos os tipos de arquitetura e mágica específica de implementação. Escreva seu código de forma simples e legível para humanos e deixe o compilador fazer as coisas boas. Se você tentar codificar em torno do compilador, precisará de problemas - e o problema que geralmente ocorre nessas situações geralmente é terrivelmente sutil e difícil de diagnosticar.
fonte
Sua variável
d
normalmente não é removida da pilha. Chaves entre dentes não indicam um quadro de pilha. Caso contrário, você não seria capaz de fazer algo assim:Se chaves entre chaves causassem um push / pop verdadeiro da pilha (como faria uma chamada de função), o código acima não seria compilado porque o código dentro das chaves não seria capaz de acessar a variável
var
que vive fora das chaves (como uma sub- A função não pode acessar diretamente variáveis na função de chamada). Sabemos que esse não é o caso.Aparelhos encaracolados são simplesmente usados para escopo. O compilador tratará qualquer acesso à variável "interna" de fora dos colchetes como inválido e poderá reutilizar essa memória para outra coisa (isso depende da implementação). No entanto, ele não pode ser retirado da pilha até que a função de fechamento retorne.
Atualização: Aqui está o que a especificação C tem a dizer. Em relação a objetos com duração de armazenamento automático (seção 6.4.2):
A mesma seção define o termo "tempo de vida" como (ênfase minha):
A palavra-chave aqui é, obviamente, 'garantida'. Depois que você sai do escopo do conjunto interno de chaves, a vida útil da matriz termina. O armazenamento pode ou não ser ainda alocado para ele (seu compilador pode reutilizar o espaço para outra coisa), mas qualquer tentativa de acessar a matriz invoca um comportamento indefinido e produz resultados imprevisíveis.
A especificação C não tem noção de quadros de pilha. Ele fala apenas de como o programa resultante se comportará e deixa os detalhes da implementação para o compilador (afinal, a implementação seria bem diferente em uma CPU sem pilha do que em uma CPU com uma pilha de hardware). Não há nada na especificação C que determine onde um quadro de pilha terminará ou não. A única maneira real de saber é compilar o código no seu compilador / plataforma específico e examinar o assembly resultante. O conjunto atual de opções de otimização do seu compilador provavelmente também desempenhará um papel nisso.
Se você deseja garantir que a matriz
d
não consuma mais memória enquanto o código está em execução, é possível converter o código entre chaves em uma função separada ou explicitamentemalloc
efree
a memória em vez de usar o armazenamento automático.fonte
Eu acredito que ele sai do escopo, mas não é extraído da pilha até que a função retorne. Portanto, ele continuará ocupando memória na pilha até que a função seja concluída, mas não acessível a jusante da primeira chave de fechamento.
fonte
Já foram fornecidas muitas informações sobre o padrão, indicando que ele é realmente específico da implementação.
Portanto, um experimento pode ser interessante. Se tentarmos o seguinte código:
Usando o gcc, obtemos aqui duas vezes o mesmo endereço: Coliro
Mas se tentarmos o seguinte código:
Usando o gcc, obtemos aqui dois endereços diferentes: Coliro
Então, você não pode ter certeza do que está acontecendo.
fonte