Existem dois locais onde as variáveis podem ser colocadas na memória. Quando você cria uma variável como esta:
int a;
char c;
char d[16];
As variáveis são criadas na " pilha ". As variáveis de pilha são liberadas automaticamente quando saem do escopo (ou seja, quando o código não pode mais alcançá-las). Você pode ouvi-las chamadas de variáveis "automáticas", mas isso saiu de moda.
Muitos exemplos para iniciantes usarão apenas variáveis de pilha.
A pilha é boa porque é automática, mas também tem duas desvantagens: (1) O compilador precisa saber com antecedência o tamanho das variáveis e (b) o espaço da pilha é um tanto limitado. Por exemplo: no Windows, nas configurações padrão do vinculador da Microsoft, a pilha é definida como 1 MB e nem toda ela está disponível para suas variáveis.
Se você não sabe em tempo de compilação o quão grande é o seu array, ou se você precisa de um grande array ou struct, você precisa do "plano B".
O plano B é chamado de " heap ". Normalmente, você pode criar variáveis tão grandes quanto o sistema operacional permitir, mas você tem que fazer isso sozinho. Postagens anteriores mostraram uma maneira de fazer isso, embora existam outras maneiras:
int size;
// ...
// Set size to some value, based on information available at run-time. Then:
// ...
char *p = (char *)malloc(size);
(Observe que as variáveis na pilha não são manipuladas diretamente, mas por meio de ponteiros)
Depois de criar uma variável de heap, o problema é que o compilador não pode dizer quando você terminou com ela, então você perde a liberação automática. É aí que entra a "liberação manual" a que você se referia. Seu código agora é responsável por decidir quando a variável não é mais necessária e liberá-la para que a memória possa ser usada para outros fins. Para o caso acima, com:
free(p);
O que torna essa segunda opção um "negócio desagradável" é que nem sempre é fácil saber quando a variável não é mais necessária. Esquecer de liberar uma variável quando você não precisa dela fará com que seu programa consuma mais memória do que precisa. Essa situação é chamada de "vazamento". A memória "vazada" não pode ser usada para nada até que o programa termine e o sistema operacional recupere todos os seus recursos. Problemas ainda mais desagradáveis são possíveis se você liberar uma variável de heap por engano antes de realmente terminar com ela.
Em C e C ++, você é responsável por limpar suas variáveis de heap como mostrado acima. No entanto, existem linguagens e ambientes como Java e linguagens .NET como C # que usam uma abordagem diferente, onde o heap é limpo por conta própria. Este segundo método, chamado de "coleta de lixo", é muito mais fácil para o desenvolvedor, mas você paga uma penalidade na sobrecarga e no desempenho. É um equilíbrio.
(Eu encostei muitos detalhes para dar uma resposta mais simples, mas espero que mais nivelada)