Slackjeff Wiki

Bits que significam

Ferramentas do usuário

Ferramentas do site


prog:guia-de-estudo-c

Essa é uma revisão anterior do documento!


Guia de estudo: C

Aprender uma nova linguagem de programação e suas peculiaridades tende a ser uma atividade árdua e desafiadora, especialmente quando não se tem referências de estudo adequadas.

O objetivo deste documento é apresentar algumas sugestões para você que está interessado em aprender C, dando prioridade, mas não exclusividade a materiais em português.

Sugiro ler o guia até o fim antes de iniciar os estudos, propriamente, para que possa ter uma visão geral dessa proposta de estudo. Talvez você considere que alguns assuntos possuem maior prioridade do que outros, ou que alguns temas são supérfluos para os seus objetivos.

O guia usa apenas fontes de estudo abertas, que você pode acessar, já que estamos em uma wiki e a ideia aqui é remover barreiras, mas evidentemente eu não poderia deixar de citar o clássico The C Programming Language, de Kernighan e Ritchie.

História

Antes de se aprofundar no estudo de C, você talvez queira entender em que contexto essa linguagem surgiu, e para que ela serviu inicialmente, além de como se popularizou e ganhou a notoriedade que possui hoje.

Isto dará uma melhor noção dos pontos fortes e fracos dessa linguagem, e ajudar a melhorar sua compreensão sobre quando pode fazer sentido recorrer a esse ambiente de desenvolvimento.

O artigo a seguir foi publicado por ninguém menos que Dennis Ritchie, autor da linguagem C, relatando o cenário em que ela foi criada, nos laboratórios Bell:

Este documento é uma versão traduzida para português, o original, em inglês, se chama The Development of the C Language.

É curioso notar que Ritchie se refere a C como uma linguagem de alto nível (o que especialmente para aquela época não é nenhum exagero), e frisa sua importância por ser uma linguagem portável, característica que por vezes é deixada de lado ou mal compreendida quando o assunto é C.

Explico: quando a linguagem C ainda estava sendo criada, era comum usar linguagem de montagem (Assembly) para escrever os programas que executariam nos minicomputadores da época (como o PDP-7 onde o UNIX foi criado).

Isso significa que para cada modelo de minicomputador, era preciso reescrever o programa, já que Assembly não é uma linguagem portável, e varia conforme a arquitetura utilizada. Com C, esse problema foi gradualmente superado, na medida em que os compiladores C eram disponibilizados para diferentes arquiteturas.

E ainda dentro de uma mesma arquitetura a linguagem C é também portável entre sistemas operacionais, desde que para cada sistema alvo exista uma biblioteca C e um compilador aderente ao padrão estabelecido para C.

A fama de C como uma linguagem não portável, deve-se ao amplo uso de bibliotecas que são específicas de algum sistema operacional. Se você usa, por exemplo, a biblioteca POSIX, então é esperado que o programa funcione apenas em sistemas que possuem essa biblioteca. Mas isso não quer dizer que o programa não seja portável por causa da linguagem C.

O artigo da Wikipédia também é útil para obter uma visão geral sobreo tema.

Familiarize-se com C

Sempre friso que programar e compreender uma linguagem de programação são habilidades diferentes. Portanto, se você já tiver algum conhecimento prévio dos conceitos, terá mais facilidade para aprender a usar a linguagem C.

Se você acredita que ainda precisa reforçar seu conhecimento sobre programação de um modo geral, talvez queira conferir o seguinte livro:

Os primeiros passos com C envolvem uma familiarização com a sua sintaxe.

A seguir você encontra dois materiais muito bem elaborados, e em português, que auxiliam nesse primeiro contato:

O segundo possui uma ênfase maior na compreensão de algoritmos, mas também possui seções dedicadas a tópicos mais básicos da linguagem C.

O artigo a seguir funciona bem como um resumo e ao mesmo tempo mostra exemplos práticos com os conceitos mais importantes e corriqueiros:

Alguns livros que você pode também usar como base de estudo são:

Adianto que o terceiro possui uma leitura mais densa e menos amigável a iniciantes, porém o rigor com que trata o assunto é importante para escrever programas robustos, especialmente em uma linguagem como C.

Portanto, aconselho a leitura desse livro depois que já tiver adquirido alguma vivência e maturidade com a linguagem.

Você também pode se interessar pelo seguinte livro:

Ele é inspirado no material “Projeto de Algoritmos” que citei anteriormente, porém de versões mais antigas desse material (publicação de 2009). Ainda assim, o fundamento permanece igualmente válido.

Além de materiais com finalidade mais didática, você pode também querer consultar algumas referências mais formais sobre como essa linguagem funciona e/ou está implementada.

O primeiro link acima apresenta os documentos formais de cada versão de C. Os documentos em si são fechados, porém os rascunhos (entenda-se, documento que ainda estava passando por revisão, então oficialmente não é o padrão) estão disponíveis em PDF para baixar.

Já o segundo documenta como o compilador do GNU (o GCC) implementa a linguagem, e portanto como se espera que o padrão seja tratado nele.

Como você pode perceber, existem diferentes padrões de C (conhecidos como C89, C99, C11, dentre outros), e talvez esteja se perguntando qual deles deveria usar como referência. Isso depende muito da abrangência e do público-alvo do programa.

Especificações mais antigas possuem uma maior abrangência e suporte, pelo fato de já existirem muitos compiladores aderentes a eles. Quanto mais novo um padrão, menos compiladores estarão totalmente aderentes e com pleno suporte.

Por outro lado, especificações mais novas estabelecem novos recursos, que podem ser importantes para um projeto, algumas vezes evitando a dependência sobre uma biblioteca externa, o que tende a aumentar a portabilidade do programa, especialmente no longo prazo.

A especificação do padrão C, como eu disse, não é um documento didático, mas pode contribuir para seu estudo como material de consulta, quando estiver em dúvida sobre como C trata algum tópico em específico.

Ferramentas essenciais

Bem, só aprender como a linguagem funciona rapidamente pode ficar entediante se você não puder praticar. E para praticar com C, você vai precisar basicamente de:

  • Um editor de texto simples (qualquer um, da sua preferência);
  • Um compilador (ou coleção de ferramentas de compilação, para ser mais específico).

Agora repita consigo mesmo as seguintes palavras: “Eu não preciso de uma IDE!”

Repita até entender. É importante frisar que um ambiente de desenvolvimento integrado (IDE, na sigla em inglês), embora possa sim ser uma ferramenta útil para aumentar a sua produtividade, não é um item essencial para programar em C, e especialmente, pode até dificultar o seu processo de aprendizado.

Portanto, concentre-se primeiro em entender os fundamentos e use apenas o básico. Você não precisa de um kit de ferramentas com trocentas funções que ainda não entende, enquanto está ainda nos primeiros passos. Isso só vai servir para tirar o seu foco.

Se ainda assim você quer uma ferramenta que integre a edição do texto com o acionamento de alguns comandos e execuções, talvez se interesse pelo editor Geany. Ele consegue um bom equilíbrio entre funcionalidade e simplicidade.

Compiladores

Muito bem, vamos detalhar mais sobre os compiladores. A rigor, o processo que chamamos de “compilação” envolve um conjunto de etapas, notadamente:

  1. Pré-processamento
  2. Compilação propriamente
  3. Montagem
  4. Ligação

As ferramentas que desempenham essas funções podem ou não estar agrupadas em uma mesma coleção (compiladores mais conhecidos, como GCC e Clang, por exemplo, fazem esse agrupamento de funções).

Os artigos a seguir explicam por alto como funciona o processo de compilação:

Existem vários compiladores C, alguns mais conhecidos e populares, outros menos. Você não precisa conhecer todos eles agora (e aliás, dificilmente em algum momento vai precisar conhecer todos), mas é importante compreender ao menos um pouco essa variedade e quais são os pontos fortes e fracos de ao menos alguns deles.

A seguir você encontra uma lista contemplando e categorizando alguns compiladores C:

Dois compiladores de grande destaque são o GCC, do projeto GNU, e o Clang, que faz parte da infraestrutura de compilação LLVM. Conhecer ao menos esses dois já será suficiente para compreender as funções centrais desse tipo de ferramenta.

Biblioteca C

Ainda no âmbito de ferramentas, é importante que você entenda o papel das bibliotecas C nesse processo. Um compilador, como você já deve ter visto nos materiais anteriores, é responsável por manipular o código-fonte do programa e gerar como saída os programas executáveis em código de máquina.

Porém, para fazer isso, ele também precisará usar como insumo alguma biblioteca C, que é a implementação de funcionalidades que fazem parte do padrão da linguagem, conforme especificado nos padrões ISO/IEC, que mencionei antes.

Bibliotecas C são um componente central em sistemas operacionais implementados em C, pois serão a principal interface entre o núcleo do sistema e suas aplicações. Cada sistema operacional pode optar por uma implementação diferente.

Você pode compreender melhor para que serve a biblioteca C a partir dos seguintes documentos:

Algumas implementações de biblioteca C são:

Como se pode ver, algumas dessas implementações contemplam não apenas a biblioteca C em si, mas também a biblioteca POSIX.

O comparativo a seguir também é interessante para compreender os recursos implementados e características dessas bibliotecas.

Gerenciadores / Construtores

Quando se trabalha com programas pequenos, de um único arquivo, talvez não seja tão tedioso ou trabalhoso usar o compilador diretamente. Mas para programas maiores, isso começa a ficar pouco prático. Nesses casos, usar ferramentas de construção ajuda a organizar o projeto e evitar a necessidade de realizar manualmente tarefas repetitivas.

O mais comum para programas em C, nesse quesito, é o utilitário make.

Embora seja parte do padrão POSIX, existem diferenças significativas entre diferentes implementações dessa ferramenta.

A implementação do FreeBSD por acaso é a mesma utilizada no NetBSD, conforme descrito na página a seguir:

Estilos de programação

Não raro, as convenções de código e estilos de programação são um tema tratado como secundário ou de menor importância. Porém esse assunto é mais relevante do que pode parecer.

Estilos podem dizer respeito a vários tipos de convenção, alguns mais frequentes, outros menos. Dentre os mais frequentes, temos convenções de nomes (de variáveis, macros, funções, tipos, etc.), estilos de comentários, regras de indentação e espaçamento, posições de abertura e fechamento de chaves ({}), e separação de linhas por exemplo.

Alguns fatores que podem influenciar nas escolhas de um estilo proposto são:

  • Gosto pessoal prevalente entre desenvolvedores de um projeto;
  • Gosto pessoal de desenvolvedores-chave ou “autoridades” em um projeto;
  • Economia de espaço (vertical ou horizontal);
  • Facilitação de compreensão do código;
  • Facilitação de pesquisa de texto;
  • Facilitação de geração de documentação do código;
  • Redução de margem para o cometimento de erros.

A melhor maneira de aprender sobre regras de estilo é conhecendo algumas delas, e comparando. Alguns exemplos de estilo de código C:

Não se preocupe em decorar essas regras (ao menos não enquanto não estiver estudando ou contribuindo código para esses projetos), a ideia é que você obtenha algumas dicas de estilo ou reflita sobre essas proposições. Algumas delas podem ser mais detalhadas, então se não entender algum ponto ou conceito abordado, anote para rever quando tiver uma base mais sólida.

Caso essas não sejam do seu agrado, existem muitas outras que pode consultar. Mas independente de gosto, o que realmente importa é entender quais são as decisões que podem ser tomadas sobre um estilo, e por que elas são escolhidas para cada projeto.

Algumas escolhas podem fazer sentido para um projeto mas serem inadequadas em outro. Compreender essas escolhas é o que realmente importa, e comparar estilos diferentes, especialmente estilos incompatíveis entre si, é uma forma de ampliar essa compreensão.

Em seus projetos, você pode adotar um estilo próprio, mas é importante manter a consistência. Começar por um estilo e depois no mesmo projeto adotar um outro diferente, certamente vai ficar confuso até mesmo para você, quando precisar reler o código.

Para trabalhos em equipe, é importante que haja consenso sobre o estilo a ser usado, e que uma vez decidido, ele efetivamente seja seguido em todo o código do projeto.

Ferramentas para formatação automática

Existem ferramentas que facilitam a adequação de programas a um estilo em particular. Geralmente elas possuem uma série de opções que você pode definir para aderir ao estilo proposto, e uma vez aplicadas sobre o código, ele é automaticamente formatado.

Porém é importante saber que essas ferramentas são apenas um instrumento auxiliar. Elas não eximem o programador de compreender o estilo do projeto em que estão trabalhando, e nem de verificar, após o uso da ferramenta, que de fato o resultado ficou aderente ao estilo.

Existem limites para o que pode ser automatizado nesse sentido, e enquanto desenvolvedor de um projeto, não pense que colocar a culpa na ferramenta depois de enviar código fora do padrão funcionará como desculpa.

Isso dito, você pode se interessar por algumas das ferramentas a seguir:

Depuração

Quando se percebe que um programa possui comportamento diferente do esperado, é necessário realizar uma depuração, tarefa que consiste em reproduzir o erro e identificar a sua causa para poder corrigir o problema.

Em alguns casos, esse processo pode ser não apenas tedioso, mas propenso a falhas, caso haja uma cadeia de causas e efeitos complexa demais para se analisar manualmente.

Isso pode ser um indicador importante de que o programa está mais complexo do que deveria, mas ainda assim, você talvez não tenha a opção de reescrever o programa, ou grande parte dele, e precisa de um instrumento de “precisão cirúrgica” para identificar o que está acontecendo na estrutura atual.

Nesses casos, os depuradores podem ser ferramentas bastante úteis, pois eles conseguem ler as entranhas do programa enquanto ele é executado, e com eles você consegue simular uma execução, avançando ou retrocedendo no tempo para observar como ele se comporta internamente.

Para entender melhor o que é um processo de depuração e como depuradores auxiliam nesse processo, você pode conferir os seguintes artigos:

Depuradores

Dois depuradores bastante difundidos, em grande medida por causa das infraestruturas do GCC e do LLVM, são o GDB (também do projeto GNU) e o LLDB (do LLVM).

Para se familiarizar com o uso desses depuradores, você pode conferir os tutoriais a seguir.

Análise estática e dinâmica

Em geral, o processo de depuração é orientado por problemas que já foram detectados em um programa. Porém, apenas esperar que problemas sejam detectados para então resolvê-los não é uma boa prática, especialmente quando existem meios de detecção automatizada para alguns tipos de falha.

A detecção automatizada não quer dizer que você não precisará de qualquer modo ler o código e apurar alguns problemas manualmente. Mas o uso de algumas ferramentas pode agilizar essa busca e reduzir o tempo que gastaria com algumas falhas mais comuns e mais fáceis de detectar.

Existem diferentes técnicas para analisar o código de um programa e automaticamente detectar potenciais falhas e então emitir alertas para correção. Em geral, agrupamos essas técnicas como análise estática ou análise dinâmica.

Como se pode deduzir, a análise estática é aquela que fazemos sem executar o programa. Confira os artigos a seguir para saber um pouco mais sobre esse assunto.

A análise estática é prática e tende a consumir pouco tempo. Mas não é capaz de detectar tudo, especialmente os problemas mais insidiosos. As ferramentas de análise dinâmica avaliam o programa durante sua execução, e com isso conseguem traçar um perfil de execução.

Isso pode servir para detectar o uso indevido de memória, partes do programa que nunca são executadas, ou possibilidades de otimização para o desempenho.

Evidentemente, a análise dinâmica não se resume ao uso de ferramentas específicas, mas também sobre uma disciplina de avaliação e leitura dos resultados desses testes.

Para saber mais sobre a análise dinâmica, você pode conferir os seguintes artigos:

Ferramentas de análise estática

A seguir, você pode conferir algumas ferramentas de análise estática:

Você pode encontrar uma lista mais extensa na Wikipédia:

Ferramentas de análise dinâmica

A seguir, você pode conferir algumas ferramentas de análise dinâmica:

Inspiração

Parte do estudo de C envolve ler código C já produzido, de programas reais, pois assim você consegue visualizar a aplicação desse conhecimento na prática. Alguns critérios que você pode considerar para decidir quais programas quer estudar:

  • Programas pequenos (menos conceitos para compreender e memorizar);
  • Programas que você já utiliza (familiaridade com as funções do programa);
  • Programas bem documentados;
  • Programas com um estilo de código que você se identifica;
  • Programas portáveis (para evitar vícios que comprometem a portabilidade).

Alguns exemplos que considero particularmente inspiradores:

Para bibliotecas, você pode querer começar o estudo pela API, já que será um ponto de partida para o uso delas. A biblioteca do SQLite, por exemplo possui uma excelente documentação, não apenas no próprio código-fonte, mas também alguns manuais de estudo.

E possui ainda uma descrição em alto nível da sua arquitetura (em inglês).

A biblioteca Lua, por sua vez, além de ser reconhecida pelo rigor com a portabilidade, é também bastante enxuta e pequena. O livro Programming in Lua é uma referência de facto para o estudo da linguagem e da API C.

A primeira edição, embora antiga, permanece válida em muitos pontos e está disponível online. Nela, você consegue uma descrição bem didática de como funciona a API C (em inglês). Detalhes sobre cada função também são apresentados na documentação oficial. A versão 5.2, inclusive, possui uma tradução para português.

Uma vez que tenha entendido a API de Lua, você pode em seguida querer ler os arquivos que implementam a biblioteca padrão de Lua, que faz uso dessa API, e é relativamente fácil de entender (especialmente, mas não exclusivamente para quem já usou a linguagem Lua).

Já o compilador TCC, embora não possua uma documentação tão detalhada, possui o essencial para compreender como o código está organizado, especialmente na seção 8.

A biblioteca musl libc ainda não possui uma documentação completa, mas é interessante por ser uma biblioteca pequena e ainda assim com um bom custo-benefício. Sua documentação oficial ainda está em construção.

De qualquer modo, conhecendo a estrutura da biblioteca padrão de C (vide especificação de C, que mencionei antes), já é possível explorar o seu repositório com alguma confiança.

As ferramentas suckless são conhecidas pelo minimalismo e código limpo. Apesar de não haver muita documentação sobre o código, isso acaba não sendo tão necessário assim, se você souber o que essas ferramentas fazem (o que não é difícil de descobrir, porque elas tendem a fazer apenas uma coisa e bem, seguindo a filosofia UNIX).

Uma característica comum em ferramentas desse projeto, como dmenu ou st é a presença do arquivo config.def.h que possui uma documentação das opções do programa na forma de comentários. Ao copiar esse arquivo e renomear para config.h, e então editar essas opções, você pode compilar o programa com as opções desejadas.

Ainda outro projeto interessante é o signify. Você pode saber mais sobre ele neste artigo, do próprio autor:

Possui uma base de código bem pequena, e o cerne da funcionalidade está no arquivo signify.c.

prog/guia-de-estudo-c.1715744565.txt.gz · Última modificação: 2024/05/15 00:42 por hrcerq