Existe alguma orientação comumente aceita sobre como escrever C moderno?


13

Tenho uma sólida experiência em Java / Groovy e fui designado para uma equipe que mantém uma grande base de código C para um software administrativo.

Alguns pontos problemáticos, como lidar com blob no banco de dados ou gerar relatórios em PDF e Excel, foram externalizados para o serviço web java.

No entanto, como desenvolvedor Java, estou um pouco confuso com alguns aspectos do código:

  • é detalhado (especialmente quando se lida com 'exceção')
  • existem muitos métodos enormes (muitos mais de 2000 linhas)
  • não há estruturas de dados avançadas (sinto muita falta de Listar, Definir e Mapear)
  • sem separação de preocupações (o SQL é alegremente misturado em todo o código)

Como resultado, sinto que o negócio está oculto em toneladas de código técnico e meu cérebro, moldado com Orientação a Objetos e uma pitada de programação Funcional, não fica à vontade.

O lado bom do projeto é que o código é direto: não há estrutura, nem manipulação de código de bytes em tempo de execução, nem AOP. E o servidor pode atender simultaneamente a mais de 10000 usuários com uma única máquina usando menos memória do que o java precisa cuspir "olá mundo".

Quero aprender a escrever código C de acordo com os princípios modernos comumente aceitos. Existe algum princípio comumente aceito sobre como o C moderno deve ser escrito e estruturado?

Algo parecido com o equivalente ao livro 'Effective Java', mas para C.

Edite à luz das respostas e comentários:

  • Vou tentar adaptar minha mentalidade ao código C e não tentar espelhá-lo no OOP.
  • Comecei a ler os guias de estilo de codificação recomendados a partir do comentário (Os padrões de codificação GNU e O estilo de codificação do kernel do Linux).
  • Tentarei propor esse estilo de código aos meus colegas de trabalho. A parte mais difícil pode ser convencer os colegas de trabalho de que um método enorme pode ser dividido em partes menores e que repetir as mesmas 4 linhas de código de tratamento de erros pode ser evitado com a ajuda de um método.

5
O aplicativo realmente precisa de modernização, ou você acha que sim, porque a maneira como foi escrita não é familiar?
Blrfl


1
@ Blrfl, sinto que o aplicativo foi escrito com padrão desatualizado. Eu só quero saber qual é o padrão de hoje (2016) para o C. (administrativo). Se houver um. Não quero refatorar ou remodelar o aplicativo atual. Quero ter uma idéia de como devo escrever a próxima parte do código.
Guillaume


3
@antlersoft: Uma função de 2.000 linhas que faz uma longa lista de coisas simples, uma após a outra, não é absolutamente um problema e não precisa de desculpa. Por favor, não responda com argumentos circulares como "você não deve escrever 2.000 funções de linha, porque você não deve escrever 2.000 funções de linha".
precisa saber é o seguinte

Respostas:


14

Posso ler da sua pergunta que o problema não é que o código seja C antigo, mas apenas uma programação ruim . A maioria dos problemas que você mencionou, como verbosidade, imensas funções de mais de 2000 linhas ou nenhuma separação de interesses, é aplicável a qualquer idioma, C ou Java.

A verbosidade foi mencionada no contexto do tratamento de erros. Você não forneceu um exemplo, portanto, só posso lembrar que o código de tratamento de erros também é código . Não há desculpa para seções repetitivas do código padrão. Fatore isso; para uma função ou (se não valer a pena criar uma função separada), faça o goto Error;padrão e mova a manipulação de erros e a limpeza de recursos para uma Error:seção na parte inferior da função.

Se passar o erro pela cadeia de chamadas parece ser o problema, pergunte-se: a função lá em cima realmente precisa saber que algum rapaz aqui embaixo teve algum problema? Os mecanismos de exceção embutidos em uma linguagem facilitam a tarefa, mas, em geral, é melhor lidar com exceções mais cedo (em qualquer idioma) para que a condição de erro não polua a lógica do código de alto nível. E se a função lá em cima realmente precisa saber, há maneiras de emular exceções com setjmpe longjmp.

Eu acho que o único problema realmente relacionado ao C mencionado é a falta de contêineres padrão. Embora Setpossa, em geral, ser substituído por uma matriz classificada e Map(na maior parte) por uma matriz de pares ou a struct(se você souber a chave definida anteriormente, se map[key] = valuetransforma em s.key = value), mas o fato é que não há contêiner dinâmico de matriz no padrão biblioteca. No C99, você pode pelo menos declarar uma matriz de comprimento variável na pilha ( int array[len]), mas precisa calcular lenantecipadamente (geralmente não é difícil) e, é claro, não pode devolvê-la como qualquer objeto alocado pela pilha. A maioria dos projetos acaba criando seu próprio contêiner de matriz dinâmica ou adotando um contêiner de código aberto.

Em uma nota final, gostaria de salientar que já estive lá. Fui o programador Java que se mudou para C ++ e C. puro. Gostaria de aconselhar "leia o livro X para aprender um bom C", mas não existe um como o Java. O caminho a seguir é absorver todas as complexidades da linguagem e da biblioteca padrão; pesquise bastante no Google, leia muito e codifique bastante até começar a pensar em C. Tentar escrever coisas em C como faria em Java é tão frustrante quanto tentar escrever uma frase em um idioma estrangeiro com palavras traduzidas diretamente de sua mãe língua; você e o leitor se encolherão. A boa notícia é que aprender boa programação é lento, mas aprender outro idioma é rápido. então, se você escrever um código decente em Java,


1
Em suma, esta é uma resposta muito boa. Eu apenas me oponho a ver setjmp()/ longjmp()como uma ferramenta válida: ela nem tenta executar qualquer limpeza. Qualquer alocação será vazada, qualquer bloqueio retido não será liberado, qualquer arquivo aberto não será fechado e qualquer inconsistência temporária nos dados se tornará permanente. IMHO, esse par de funções é basicamente o pior hack já inventado, com a única justificativa de que foi possível implementá-lo. No final, existe realmente apenas uma maneira válida de lidar com erros em C: códigos de erro explícitos.
cmaster - reinstate monica 11/11

@cmaster sim. Pessoalmente, setjmp/longjmpparece um peixe fora d'água em C e eu nunca o usei. Senti-me compelido a incluí-los apenas por causa dos inúmeros tutoriais / bibliotecas da Internet para imitar exceções, então pensei que existem pessoas que realmente o usam.
Uma coruja

7

O lado bom do projeto é que o código é direto: não há estrutura, nem manipulação de código de bytes em tempo de execução, nem AOP. E o servidor pode atender simultaneamente a mais de 10000 usuários com uma única máquina usando menos memória do que o java precisa cuspir "olá mundo".

Eu recomendo que você seja cauteloso se isso vale o seu tempo e o dinheiro da empresa para gastar recursos na "modernização" de um software funcional com baixa complexidade de código e com bom desempenho. É provável que você introduza novos bugs, especialmente porque parece ser um sistema com o qual você não está familiarizado.

Se você ainda deseja seguir esse caminho, sugiro o seguinte:

  • Faça (ou gere) um diagrama de estado do software / código
  • Mergulhe no código e faça uma lista das partes mais complexas ou críticas do código, respectivamente
  • Encontre alguém que tenha conhecimento sobre essa base de código e pergunte por que ela foi criada dessa maneira e o que é conhecido por causar problemas.
  • Escreva a documentação do que aprendeu

Nesse ponto, você decidirá se vale a pena explorar isso. Se a cultura da sua empresa não recompensar o fracasso, obtenha a luz verde de um gerente superior ou superior.

  • Compartimentalize os diferentes componentes do software e escreva testes de unidade para cada um.
  • Iterar até que você possa colar os diferentes módulos
  • Faça testes adicionais que simulem a interação real do usuário (testes de estresse etc.)

Eu acho que esse é um bom roteiro e o levamos aonde você precisa. Sem conhecer as especificidades deste projeto, é difícil ajudá-lo. Por favor, não descarte meu aviso como excessivamente alarmista. Toneladas de excelentes programadores derrotaram o pó, tentando reescrever um projeto existente no seu idioma favorito ou usando ferramentas "modernas". Essa é uma decisão que deve ser cuidadosamente pensada, e peço que você não seja desonesto e faça isso sozinho, sem o apoio da gerência ou a assistência de seus colegas.


2
Percebo que minha pergunta não estava clara. Não quero refatorar o código. Em absoluto. Eu quero manter a base de código existente do jeito que está. No entanto, eu quero aprender a escrever C moderno para o novo recurso. E aqui estou perdido. A maioria da documentação que eu encontrei é sobre como código em C, não sobre como escrever 'moderno' C. Talvez não há tal coisa como 'moderno' C ...
Guillaume

1

Se você preferir uma linguagem de nível superior, existem algumas linguagens como C ++ ou Objective-C que podem ser misturadas com o código C com bastante facilidade.

Como alternativa, C e C ++ são razoavelmente compatíveis. Você pode apenas compilar toda a base de código como C ++ com poucas alterações - terá a variável ocasional denominada "classe" ou "modelo" que precisa renomear, mas na prática isso será tudo. (sizeof ('a') é diferente em C e C ++, mas acho que nunca usei isso).

Se você seguir esse caminho, considere que o próximo mantenedor pode não ser muito fluente com o C ++. Não se empolgue. Aproveite o C ++, mas apenas até o momento em que um programador de C possa entender com facilidade.


1
Eu tenho que discordar aqui. C e C ++ são linguagens distintas, e algum código exigido por um compilador C ++ (explicitamente lançando o valor de retorno malloc) é considerado uma prática ruim em C. O significado conste inlinetambém é muito diferente entre C e C ++ e, é claro, C ++ não entende __restrict. Não trate os idiomas como intercambiáveis, mesmo no subconjunto de fontes que compilam em ambos.
Angew não está mais orgulhoso de SO

1

Basicamente, escrever um bom código C é o mesmo que escrever um bom código C ++ ou Java: você deseja uma classe, use a struct. Você deseja herança, inclua a base structcomo um primeiro membro sem nome. Você deseja funções virtuais, adicione um ponteiro a uma estática structde ponteiros de função. E assim por diante, etc. É exatamente o que o C ++ faz sob o capô, a única diferença é que é explícito em C. Assim, você pode fazer uma programação perfeitamente orientada a objetos em C, apenas um pouco diferente e mais compacta do que você estão acostumados.

O ponto é que a boa programação é sobre paradigmas, não sobre recursos da linguagem. É verdade que é sempre bom que os recursos do idioma ofereçam um bom suporte aos paradigmas que você deseja usar, mas os recursos do idioma não são um requisito. Depois que você perceber isso, poderá escrever um bom código em praticamente qualquer idioma (além de algumas línguas esotéricas, como brainfuck ou INTERCAL, ou seja).

Obviamente, o problema continua sendo que a biblioteca C padrão não contém nenhuma dessas classes de contêiner bacanas com as quais você está acostumado. Infelizmente, isso significa que você precisará usar o seu próprio produto ou solucionar essa falta usando matrizes alocadas dinamicamente. Mas eu aposto que você descobrirá em breve que tudo o que você realmente precisa são matrizes dinâmicas ( malloc()) e listas / árvores vinculadas que são implementadas por meio de membros de ponteiro em suas classes.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.