Pergunta

Eu tenho escrito C por apenas um escasso poucas semanas e não tenham tomado a tempo para preocupar-me muito com malloc(). Recentemente, porém, um programa da mina retornou uma série de rostos em vez dos verdadeiros / falsos valores que eu tinha esperado que felizes.

Se eu criar uma estrutura como esta:

typedef struct Cell {
  struct Cell* subcells;
} 

e depois inicialize-o como este

Cell makeCell(int dim) {
  Cell newCell;

  for(int i = 0; i < dim; i++) {
    newCell.subcells[i] = makeCell(dim -1);
  }

  return newCell; //ha ha ha, this is here in my program don't worry!
}

Am I vai acabar acessando rostos felizes armazenados em algum lugar da memória, ou talvez escrever sobre células previamente existentes, ou o quê? Minha pergunta é: como é que C alocar memória quando eu não tenho realmente malloc () ed a quantidade adequada de memória? Qual é o padrão?

Foi útil?

Solução

Não há valor padrão para o ponteiro. O ponteiro irá apontar para o que quer que armazena atualmente. Como você não inicializou-lo, a linha

newCell.subcells[i] = ...

efetivamente acessa alguma parte incerta da memória. Lembre-se que subcells [i] é equivalente a

*(newCell.subcells + i)

Se o lado esquerdo contém algum lixo, você vai acabar adicionando i para um valor de lixo e acessar a memória nesse local incerto. Como você disse corretamente, você terá que inicializar o ponteiro para apontar para alguma área de memória válido:

newCell.subcells = malloc(bytecount)

Depois de qual linha você pode acessar que muitos bytes. Com relação a outras fontes de memória, existem diferentes tipos de armazenamento que têm todos os seus usos. Que tipo você começa depende de que tipo de objeto que você tem e qual classe de armazenamento que você dizer ao compilador para uso.

  • malloc retorna um ponteiro para um objeto com nenhum tipo. Você pode fazer um ponto ponteiro para essa região da memória e do tipo do objeto irá efetivamente se tornar o tipo de pontas com o tipo de objeto. A memória não é inicializada para qualquer valor e acesso geralmente é mais lento. Objetos assim obtido são chamados allocated objects.
  • Você pode colocar objetos globalmente. Sua memória será inicializada para zero. Para os pontos, você obterá ponteiros NULL, por carros alegóricos você receberá um zero adequada também. Você pode contar com um valor inicial adequado.
  • Se você tiver variáveis ??locais, mas usar a classe especificador de armazenamento static, então você vai ter a mesma regra valor inicial como objetos globais. A memória geralmente é alocada da mesma maneira como os objetos globais, mas isso é de forma alguma uma necessidade.
  • Se você tiver variáveis ??locais sem qualquer especificador de classe de armazenamento ou com auto, então sua variável será alocada na pilha (embora não definida-lo por C, é isso que compiladores fazer praticamente é claro). Você pode tomar seu endereço no caso em que o compilador terá que otimizações omitir como colocá-lo em registros de curso.
  • As variáveis ??locais utilizados com o especificador register classe de armazenamento, são marcadas como tendo um armazenamento especial. Como resultado, você não pode tomar mais seu endereço. Em compiladores recentes, normalmente não há necessidade de usar register mais, por causa dos seus otimizadores sofisticados. Se você estiver realmente especialista, então você pode obter algum desempenho fora dele, se usá-lo, no entanto.

Objetos têm associado durações de armazenamento que podem ser usados ??para mostrar as diferentes regras de inicialização (formalmente, eles só definir por quanto tempo, pelo menos, os objetos vivem). Objetos declarados com auto e register têm duração de armazenamento automático e são não inicializado. Você tem que inicializar explicitamente se você quer que eles contêm algum valor. Se não o fizer, eles vão conter o que o compilador deixado na pilha antes de começarem vida. Objetos que são alocados por malloc (ou outra função do que a família, como calloc) ter alocado duração de armazenamento. Seu armazenamento é não inicializado quer. Uma exceção é quando se usa calloc, caso em que a memória é inicializado para zero ( "real" zero. Ou seja todos os bytes 0x00, sem levar em conta qualquer representação ponteiro NULL). Os objetos que são declarados com variáveis ??static e globais têm duração de armazenamento estático. Seu armazenamento é inicializado a zero adequada para o seu respectivo tipo. Note-se que um objeto não deve ter um tipo, mas a única maneira de obter um objeto tipo-less está usando armazenamento alocado. (Um objecto em C é uma "região de armazenamento").

Então, o que é o quê? Aqui está o código fixo. Porque uma vez que você atribuído um bloco de memória que você não pode voltar mais quantos itens você alocado, melhor é para sempre armazenar que em algum lugar contagem. Eu já introduziu um dim variale para a estrutura que recebe tele contagem armazenado.

Cell makeCell(int dim) {
  /* automatic storage duration => need to init manually */
  Cell newCell;

  /* note that in case dim is zero, we can either get NULL or a 
   * unique non-null value back from malloc. This depends on the
   * implementation. */
  newCell.subcells = malloc(dim * sizeof(*newCell.subcells));
  newCell.dim = dim;

  /* the following can be used as a check for an out-of-memory 
   * situation:
   * if(newCell.subcells == NULL && dim > 0) ... */
  for(int i = 0; i < dim; i++) {
    newCell.subcells[i] = makeCell(dim - 1);
  }

  return newCell;
}

Agora, as coisas parecem como este para dim = 2:

Cell { 
  subcells => { 
    Cell { 
      subcells => { 
        Cell { subcells => {}, dim = 0 }
      }, 
      dim = 1
    },
    Cell { 
      subcells => { 
        Cell { subcells => {}, dim = 0 }
      }, 
      dim = 1
    }
  },
  dim = 2
}

Note que em C, o valor de retorno de uma função não é necessário para ser um objeto. Nenhum armazenamento em tudo é necessário para existir. Conseqüentemente, você não tem permissão para alterá-lo. Por exemplo, o seguinte não é possível:

makeCells(0).dim++

Você vai precisar de uma "função livre" que é livre a memória alocada novamente. Como o armazenamento de objetos alocados não é liberado automaticamente. Você tem que chamar free para libertar essa memória para cada ponteiro subcells em sua árvore. É deixado como um exercício para você escrever que up:)

Outras dicas

Resposta curta:. Não é alocado para você

resposta ligeiramente mais longo: O ponteiro subcells é inicializado e podem apontar qualquer . Este é um bug, e você deve nunca permitir que isso aconteça.

resposta mais longa ainda: variáveis ??automáticas são alocados na pilha, variáveis ??globais são alocados pelo compilador e muitas vezes ocupam um segmento especial ou pode ser na pilha. As variáveis ??globais são inicializados para zero por padrão. variáveis ??automáticas não tem um valor padrão (eles simplesmente obter o valor encontrado na memória) e o programador é responsável por garantir que eles tenham bons valores iniciais (embora muitos compiladores vai tentar você na pista quando você esquecer).

A variável newCell em você função é automática, e não é inicializado. Você deve corrigir isso pronto. Ou dar newCell.subcells um valor significativo prontamente, ou apontá-lo para NULL até que você alocar algum espaço para ele. Dessa forma você vai jogar uma violação de segmentação se você tentar cancelar a referência-lo antes de alocar alguma memória para ele.

Pior ainda, você está retornando a Cell por valor, mas atribuí-la a um Cell * quando você tentar preencher a matriz subcells. Quer retornar um ponteiro para um objeto pilha alocada, ou atribuir o valor para um objeto alocado localmente.

A expressão usual para isso teria a forma de algo como

Cell* makeCell(dim){
  Cell *newCell = malloc(sizeof(Cell));
  // error checking here
  newCell->subcells = malloc(sizeof(Cell*)*dim); // what if dim=0?
  // more error checking
  for (int i=0; i<dim; ++i){
    newCell->subCells[i] = makeCell(dim-1);
    // what error checking do you need here? 
    // depends on your other error checking...
  }
  return newCell;
}

que eu deixei-lhe alguns problemas para elaborar ..

E note que você tem que manter o controle de todos os bits de memória que acabará por ter de ser desalocada ...

Qualquer coisa não alocado no heap (via malloc e chamadas semelhantes) é alocada na pilha, em vez disso. Por isso, qualquer coisa criada em uma determinada função sem ser malloc'd será destruído quando as extremidades de função. Isso inclui objetos retornados; quando a pilha é desenrolado após uma função de chamar o objeto retornado é copiado para conjunto espaço reservado para ele na pilha pela função chamada.

Aviso: Se você quiser retornar um objeto que tem ponteiros para outros objetos nele, certifique-se de que os objetos apontado são criados na pilha, e melhor ainda, criar esse objeto na montão, também, a menos que não se destina a sobreviver à função em que ele é criado.

A minha pergunta é, como é que C alocar memória quando eu não tenho realmente malloc () ed a quantidade adequada de memória? Qual é o padrão?

Para não alocar memória. Você tem que explicitamente criá-lo na pilha ou dinamicamente.

No seu exemplo, subcells aponta para um indefinido local, que é um bug. Sua função deve retornar um ponteiro para um struct celular em algum ponto.

Am I vai acabar acessando rostos felizes armazenados em algum lugar da memória, ou talvez escrever sobre células previamente existentes, ou o quê?

Você tem sorte que você tem uma cara feliz. Em um desses dias de azar, que poderia ter limpado seu sistema limpo;)

A minha pergunta é, como é que C alocar memória quando eu não tenho realmente malloc () ed a quantidade adequada de memória?

Não faz. No entanto, o que acontece é quando você defini-lo Célula newcell, os subcells ponteiro é inicializado com o valor de lixo. Que pode ser um 0 (caso em que você deseja obter um acidente) ou algum inteiro grande o suficiente para fazer com que pareça um endereço de memória real. O compilador, em tais casos, ficaria feliz em buscar o valor que está residindo lá e trazê-lo de volta para você.

Qual é o padrão?

Esta é o comportamento se você não inicializar suas variáveis. E sua função makeCell parece um pouco sub-desenvolvidos.

Há realmente três seções onde as coisas podem ser atribuídas -. De dados, pilha e heap

No caso que você menciona, seria alocada na pilha. O problema com a alocação de algo na pilha é que é válida apenas para a duração da função. Uma vez que seus função retorna, que a memória é recuperada. Então, se você retornar um ponteiro para algo alocada na pilha, esse ponteiro será inválido. Se você devolver o objeto real embora (não um ponteiro), uma cópia do objeto será automaticamente feita para a função de chamada para uso.

Se você tinha declarado como uma variável global (por exemplo, em um arquivo de cabeçalho ou fora de uma função) que seriam alocados na seção de dados da memória. A memória nesta seção é alocado automaticamente quando o programa começa e desalocado automaticamente quando termina.

Se você alocar algo na pilha usando malloc (), que a memória é boa para o tempo que você quiser usá-lo - até que você chamar free () em que ponto ele é liberado. Isso lhe dá a flexibilidade para alocar e desalocar memória que for necessário (em vez de usar globals onde tudo é alocado na frente e liberado somente quando seu termina do programa).

As variáveis ??locais são "alocados" na pilha. A pilha é uma quantidade pré-alocados de memória para armazenar as variáveis ??locais. As variáveis ??deixará de ser válido quando as saídas de função e será substituído pelo que vem a seguir.

No seu caso, o código é não fazer nada, uma vez que não retorna o resultado. Além disso, um ponteiro para um objeto na pilha também deixará de ser válido quando sai do escopo, então eu acho que no seu caso precisa (você parece estar fazendo uma lista ligada), você vai precisar usar malloc ().

Vou fingir que sou o computador aqui, lendo este código ...

typedef struct Cell {
  struct Cell* subcells;
}

Isso me diz:

  • Nós temos um tipo struct chamada celular
  • Ele contém um ponteiro chamado subcells
  • O ponteiro deve ser para algo de celular tipo struct

Não me diga se o ponteiro vai para um celular ou uma matriz de celular. Quando um novo celular é feita, o valor desse ponteiro é indefinido até que um valor é atribuído a ele. É uma má notícia para os ponteiros de uso antes de defini-los.

Cell makeCell(int dim) {
  Cell newCell;

Novo celular struct, com um ponteiro subcells indefinido. Tudo isso faz é reservar um pouco pedaço de memória para ser chamado newcell que é do tamanho de uma estrutura celular. Isso não muda os valores que estavam em que a memória -. Eles poderiam ser qualquer coisa

  for(int i = 0; i < dim; i++) {
    newCell.subcells[i] = makeCell(dim -1);

A fim de obter newCell.subcells [i], é feito um cálculo para compensar a partir subcells pelo i, então, que é referenciado . Especificamente, isso significa que o valor é puxado a partir desse endereço de memória. Tomemos, por exemplo, i == 0 ... Então nós seria dereferencing os subcells ponteiro em si (sem deslocamento). Desde subcells é indefinido, poderia ser qualquer coisa. Literalmente qualquer coisa! Então, isso iria pedir um valor de algum lugar completamente aleatório na memória. Não há nenhuma garantia de qualquer coisa com o resultado. Pode imprimir algo, ele pode falhar. Ele definitivamente não deve ser feito.

  }

  return newCell;
}

Toda vez que você trabalha com um ponteiro, é importante certificar-se ele é definido como um valor antes de excluir a referência dele. Incentive o seu compilador para lhe dar qualquer aviso pode, muitos compiladores modernos pode pegar esse tipo de coisa. Você também pode dar indicações cutesy valores padrão como 0xDEADBEEF (sim! Isso é um número em hexadecimal, é apenas uma palavra que também, por isso parece engraçado) para que eles se destacam. (A opção% p para printf é útil para a exibição de ponteiros, como uma forma grosseira de depuração. Programas do depurador também pode mostrar-lhes muito bem.)

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top