Logo Passei Direto
Buscar
Material
páginas com resultados encontrados.
páginas com resultados encontrados.
left-side-bubbles-backgroundright-side-bubbles-background

Crie sua conta grátis para liberar esse material. 🤩

Já tem uma conta?

Ao continuar, você aceita os Termos de Uso e Política de Privacidade

left-side-bubbles-backgroundright-side-bubbles-background

Crie sua conta grátis para liberar esse material. 🤩

Já tem uma conta?

Ao continuar, você aceita os Termos de Uso e Política de Privacidade

left-side-bubbles-backgroundright-side-bubbles-background

Crie sua conta grátis para liberar esse material. 🤩

Já tem uma conta?

Ao continuar, você aceita os Termos de Uso e Política de Privacidade

left-side-bubbles-backgroundright-side-bubbles-background

Crie sua conta grátis para liberar esse material. 🤩

Já tem uma conta?

Ao continuar, você aceita os Termos de Uso e Política de Privacidade

left-side-bubbles-backgroundright-side-bubbles-background

Crie sua conta grátis para liberar esse material. 🤩

Já tem uma conta?

Ao continuar, você aceita os Termos de Uso e Política de Privacidade

left-side-bubbles-backgroundright-side-bubbles-background

Crie sua conta grátis para liberar esse material. 🤩

Já tem uma conta?

Ao continuar, você aceita os Termos de Uso e Política de Privacidade

left-side-bubbles-backgroundright-side-bubbles-background

Crie sua conta grátis para liberar esse material. 🤩

Já tem uma conta?

Ao continuar, você aceita os Termos de Uso e Política de Privacidade

left-side-bubbles-backgroundright-side-bubbles-background

Crie sua conta grátis para liberar esse material. 🤩

Já tem uma conta?

Ao continuar, você aceita os Termos de Uso e Política de Privacidade

left-side-bubbles-backgroundright-side-bubbles-background

Crie sua conta grátis para liberar esse material. 🤩

Já tem uma conta?

Ao continuar, você aceita os Termos de Uso e Política de Privacidade

left-side-bubbles-backgroundright-side-bubbles-background

Crie sua conta grátis para liberar esse material. 🤩

Já tem uma conta?

Ao continuar, você aceita os Termos de Uso e Política de Privacidade

Prévia do material em texto

Introdução à Lógica de Programação 
UNIDADE 1 
 
Etapas : Algoritmo é uma sequência lógica de instruções que podem ser 
executadas. É importante destacar que qualquer tarefa que siga um 
certo padrão. 
Etapas : 
1. Algoritmo é uma sequência lógica de instruções que podem ser 
executadas. É importante destacar que qualquer tarefa que siga um certo 
padrão pode ser representada por um algoritmo; entretanto, para montá-
lo é necessário dividir a tarefa em três fases fundamentais. 
 
2. Etapas de um algoritmo? 
Entrada: São as informações que iniciam o algoritmo. Processamento: São 
os passos necessários para atingir a meta. 
Saída: São os resultados do processamento. de ser julgada.” 
 
3. Exemplo de Algoritmo? 
Problema: Calcular a média final dos alunos da 8ª Série. Os alunos 
realizarão quatro provas: P1, P2, P3 e P4. 
 
4. Construção de um Algoritmo 
Para montar o algoritmo proposto, fazem-se três perguntas: 
1) Quais são os dados de entrada? 
Resp.: P1, P2, P3 e P4. 
2) Qual será o processamento a ser utilizado? 
Resp.: Somar todos os dados de entrada e dividi-los por 4 
3) Qual será o dado de saída? 
Resp.: A média final 
 
5. Algoritmo: Recebe a nota da prova P1 Recebe a nota de prova P2 
Some todas as notas e divida o resultado por 4 
Mostre o resultado da divisão 
 
6. Verificação do Algoritmo: 
Ao desenvolver um algoritmo, em seguida ele deverá sempre ser testado 
para verificar o seu bom funcionamento. Esta verificação chama-se: Teste 
de Mesa, onde é simulada a execução das instruções do algoritmo para 
provar se os passos utilizados levarão ao resultado esperado ou não. 
 
7. Do exemplo anterior: Nota da Prova P1 Nota da Prova P2 
Dá-se valores à tabela abaixo: 
 
8. O que é um diagrama de bloco? 
Uma forma eficiente de representar os passos lógicos de uma 
determinada tarefa é a utilização de um diagrama de blocos porque ele 
segue um padrão, fazendo com que o seu entendimento ( mesmo não 
estando a par do problema em questão) torne-se bastante facilitado. 
 
9. Diagrama de Blocos 
Através do uso do diagrama pode-se definir uma sequência de símbolos, 
com significado bem definido, assim a sua principal função é a de facilitar 
a visualização dos passos de execução de uma tarefa. 
 
10. Simbologia Padrão 
Em um diagrama de blocos existem diferentes símbolos e no quadro a 
seguir, são mostrados alguns dos principais símbolos utilizados: 
No interior do símbolo é escrito uma expressão matemática ou lógica, 
uma ação, um índice e etc., o que for relevante mostrar, pois somente os 
símbolos vazios não significarão nada. Veja o exemplo na Tabela. 
 
11. Simbologia ? 
Cada uma destas formas se aplica a uma determinada ação como está 
indicado. Existem outras formas que podem ser aplicadas. 
 
12. Simbologia: 
 
13. Exemplo de Diagrama de Blocos 
Percebe-se que no primeiro exemplo (da bala) uma sequência lógica foi 
seguida, utilizando somente as informações diretas, porém no segundo 
exemplo (da média) foi utilizado um cálculo e a seguir, foi exibido o seu 
resultado final. 
 
14. 
 
15. Há diversas formas de representação de algoritmos que diferem entre 
si pela quantidade de detalhes de implementação que fornecem ou, 
inversamente, pelo grau de abstração que possibilitam com relação à 
implementação do algoritmo em termos de uma linguagem de 
programação específica. 
Dentre as principais formas de representação de algoritmos destacam-se: 
a descrição narrativa, o fluxograma convencional e o pseudocódigo (ou 
linguagem estruturada). 
 
16. ATIVIDADES DE APRENDIZAGEM 
1) Construa um diagrama de blocos que: 
• Leia a cotação do dólar 
• Leia um valor em dólares 
• Converta esse valor para Real 
• Mostre o resultado 
2) Desenvolva um diagrama que: 
• Leia 4 (quatro) números 
• Calcule o quadrado para cada um 
• Somem todos e 
• Mostre o resultado 
 
17. ATIVIDADES DE APRENDIZAGEM 
3) Construa um algoritmo para pagamento de comissão de vendedores de 
peças, levando-se em consideração que sua comissão será de 5% do total 
da venda e que você tem os seguintes dados: 
• Identificação do vendedor 
• Código da peça 
• Preço unitário da peça 
• Quantidade vendida 
A seguir, construa o diagrama de blocos do algoritmo desenvolvido, e ao 
final, faça um teste de mesa. 
 
18. ATIVIDADES DE APRENDIZAGEM 
4) Identifique os dados de entrada, processamento e saída no algoritmo 
abaixo: 
• Receba código da peça 
• Receba valor da peça 
• Receba Quantidade de peças 
• Calcule o valor total da peça (Quantidade * Valor da peça) 
Mostre o código da peça e seu valor total 
 
 
TESTE DE SOFTWARE: INTRODUÇÃO, CONCEITOS BÁSICOS E TIPOS DE 
TESTES 
Você sabe o que é um teste de software e quais são os principais tipos de 
teste de software? Neste artigo você vai tirar todas as suas dúvidas. 
Por mais que se planeje a construção de um software, erros são passíveis 
de ocorrer. Pode ser um bug num game, uma falha que feche um 
programa ou um erro que impossibilite você salvar um arquivo. 
Quem já passou por esse tipo de situação sabe como é chato quando 
ficamos na mão por culpa de um programa com falhas. O teste de 
software serve justamente para tentar encontrar possíveis erros que um 
programa recém-desenvolvido possa apresentar, de modo a conseguir 
corrigi-lo antes que seja lançado no mercado, ficando disponível para uso 
do público. 
O teste de software geralmente é a última etapa na construção de um 
programa, visando checar o seu nível de qualidade. Os defeitos que um 
teste busca identificar incluem erro de compatibilidade, de algum 
algoritmo, de requisitos que não podem ser complementados, limitação 
de hardware etc. A lista é grande e aumenta com o tamanho do 
programa. 
QUAIS OS TIPOS DE TESTE DE SOFTWARE? 
Existem diferentes tipos de testes que podem ser aplicados num software 
para identificar suas falhas, sendo as principais: 
– Teste da caixa branca – utiliza o aspecto interno do programa/sistema, 
o código fonte, para avaliar seus componentes. Ele também é conhecido 
como teste orientado à lógica ou estrutural. Podem ser analisados itens 
como: fluxo dos dados, condição, ciclos etc. Na hora de implementá-lo é 
preciso verificar a criticidade, a complexidade, a estrutura e o nível de 
qualidade que se pretende obter do programa, envolvendo confiança e 
segurança; 
– Teste da caixa preta – diferente do teste anterior, que prioriza os 
aspectos internos, o teste da caixa preta verifica aspectos externos. Os 
requisitos funcionais do sistema são avaliados. Não se observa o modo de 
funcionamento, sua operação, tendo como foco as funções que deverão 
ser desempenhadas pelo programa. Desse modo, avalia-se se um grupo 
de entrada de dados resultou nas saídas pretendidas, levando-se em 
consideração a especificação do programa. Ou seja, o que se esperava 
que o software deveria fazer. É conhecido também como técnica 
funcional; 
– Teste da caixa cinza – esse tipo de teste une os dois anteriores, por isso 
o termo “cinza”. Avalia tanto os aspectos internos quanto os externos, de 
entrada e saída. Pode utilizar-se de engenharia reversa; 
– Teste de regressão – esse consiste em realizar testes a cada versão de 
um software, onde se modificam-se funcionalidades. Desse modo, evita-
se que erros que foram corrigidos antes no software antes voltem a 
aparecer na hora de se incrementar algo novo a ele. 
– Teste de unidade – testa-se unidades menores de um software, de 
modo isolado, para ver se todas funcionam adequadamente; 
– Teste de integração – depois das unidades testadas, realiza-se uma 
verificação se elas funcionam juntas, integradas. Pode ocorrer delas 
apresentarem incompatibilidades ao funcionarem em conjunto, mesmo 
após terem sido aprovadas no teste de unidade; 
– Teste de carga – esse teste é feito para avaliar os limites de uso do 
software, o quanto ele suporta em volume de informações, tráfego etc. 
sem que apresente erros; 
– Teste de usabilidade – esse teste é feito por um pequenogrupo de 
usuários para ver se o software satisfaz as suas necessidades. Nesse teste 
analisa-se como o usuário usa o sistema, verificando onde ele tem mais 
dificuldade. Ouve-se também suas impressões, porém é preciso 
confrontá-las com as observações do avaliador; 
– Teste de stress – aqui leva-se o software ao seu limite de potência e 
funcionamento, para mais ou para menos, de modo a avaliar em qual 
ponto ele deixa de funcionar adequadamente. Isso é feito para verificar se 
suas especificações máximas ou mínimas de uso estão corretas. 
O QUE É UM PLANO DE TESTE DE SOFTWARE? 
Um plano de teste é feito para colaborar com o desenvolvimento de um 
software. É por meio desse plano que os componentes técnicos, 
funcionais, estruturais etc. serão verificados e validados, de modo a 
garantir o bom funcionamento do programa junto ao usuário final. Sendo 
assim, um plano de teste de software tem como foco garantir a 
confiabilidade e segurança de um software, identificando possíveis erros 
e falhas durante a sua confecção, ou mesmo depois. 
Ele deve ser planejado em conjunto com a proposta do software, sendo 
aplicado em cada etapa do projeto e não somente no final. 
FERRAMENTAS DE TESTE DE SOFTWARE 
Para garantir a qualidade de um programa, as desenvolvedoras realizam 
testes nele. Isso é necessário para que falhas sejam detectadas antes que 
o software seja colocado no mercado. Sabe aquele programa que vive 
travando, não roda direito ou que faz o PC ficar lento? Esse, 
provavelmente, deve ter passado pelo processo de desenvolvimento com 
essas imperfeições. Então, para evitar que isso aconteça, as empresas 
contratam profissionais (os testadores de software ou analistas de testes) 
para identificarem esses problemas e relatarem para que os 
desenvolvedores os corrijam. Mas, para fazer isso eles precisam realizar 
uma bateria de testes diferentes, que envolvem desde análise da 
estrutura interna do software até a avaliação da interface. 
O QUE SÃO FERRAMENTAS DE TESTE DE SOFTWARE? 
Para que esses testes possam ser realizados de modo mais rápido e com 
maior abrangência, existem ferramentas que automatizam alguns deles 
ou auxiliam na execução de outros. Essas são as ferramentas de teste de 
software. 
O QUE É E O QUE FAZ UM TESTADOR DE SOFTWARE? 
Com a expansão do mercado de tecnologia da informação (TI), uma nova 
profissão surgiu e tem crescido nos últimos anos: o testador de software 
ou analista de testes. Esse profissional é contratado para encontrar erros, 
falhas, bugs e outros tipos de problemas que não foram detectados 
durante a confecção de um software. 
O mercado para esse tipo de profissão é amplo. Abrange desde a 
prestação de serviços de testes de softwares para programas gerenciais 
até aplicativos de smartphones voltados para o público. E a expectativa é 
de que ele fique cada vez maior, à medida em que clientes de 
desenvolvedoras de softwares passam a solicitar a avaliação desse 
profissional nos programas encomendados. 
AUTOMAÇÃO DE TESTE DE SOFTWARE 
Num mundo cada vez mais interligado pela tecnologia, os planos de testes 
de softwares têm um peso importante, pois muitos negócios dependem 
de que esses estejam funcionando corretamente. 
Qualquer falha num programa de gerenciamento financeiro pode 
acarretar prejuízos grandes em termos monetários. Um erro num 
software de um equipamento médico pode custar a vida uma pessoa ou 
dificultar o atendimento a alguém que precisa. 
E não é só os casos mais extremos que precisam de testes de software de 
qualidade, pois uma simples falha de um programa de uso comum que 
ocorra com frequência pode fazer com que usuário dele abra queixa ou 
não o recomende para outros usuários, migrando para a concorrência. 
Quem perde é quem desenvolveu o software. 
PROCESSO DE TESTE DE SOFTWARE 
O Processo de Testes de Software representa uma estruturação de 
etapas, atividades, artefatos, papéis e responsabilidades que buscam a 
padronização dos trabalhos e ampliar a organização e controle dos 
projetos de testes. 
O Processo de Teste, como qualquer outro processo deve ser revisto 
continuamente, de forma a ampliar sua atuação e possibilitar aos 
profissionais uma maior visibilidade e organização dos seus trabalhos, o 
que resulta numa maior agilidade e controle operacional dos projetos de 
testes. 
 
ETAPA 1: PLANEJAMENTO DOS TESTES 
Esta etapa caracteriza-se pela definição de uma proposta de testes 
baseada nas expectativas do Cliente em relação à prazos, custos e 
qualidade esperada, possibilitando dimensionar a equipe e estabelecer 
um esforço de acordo com as necessidades apontadas pelo Cliente. 
 
DINÂMICA DAS MACRO-ATIVIDADES 
Este diagrama representa a seqüência das “macro-atividades” a serem 
executadas na etapa de “Planejamento dos Testes”. 
 
DETALHAMENTO DAS MACRO-ATIVIDADES 
Esta lista representa o conjunto de atividades que deverão ser executadas 
para que cada macro-atividade seja considerada finalizada, funcionando 
como um “check-list” de execução da etapa de “Planejamento dos 
Testes”. 
 
ESTUDO DO PROJETO: 
• Estudar as modificações solicitadas pelo Cliente (novos requisitos); 
• Estudar as modificações de arquiteturas dos aplicativos; 
• Estudar as lições aprendidas dos Projetos Anteriores; 
• Avaliar expectativas de custos, prazos e qualidade exigidas pelo 
Cliente; 
• Avaliar os riscos envolvidos nos Projetos e seus impactos neste 
processo; 
AVALIAÇÃO DE IMPACTO: 
• Avaliar se o projeto exige a criação de casos de testes 
“progressivos”; 
• Avaliar se o projeto exige modificações em casos de testes 
“regressivos” 
• Avaliar se o projeto exige adequações na automação dos testes; 
• Avaliar se o projeto exige adequação nas atuais ferramentas 
empregadas; 
• Avaliar se o projeto exige a aquisição/construção de novas 
ferramentas; 
• Avaliar se o projeto exige modificações na estruturação do 
ambiente; 
ANÁLISE INTERNA DE ESFORÇO 
• Levantar métricas históricas para auxiliar na elaboração das 
estimativas de esforço; 
• Estimar esforço interno para absorção dos impactos da Arquitetura 
dos Testes; 
• Demonstrar esforço externo para absorção dos impactos da 
Arquitetura dos Testes; 
ANÁLISE EXTERNA DE ESFORÇO: 
• Avaliar disponibilidade de espaço físico e infra-estrutura para os 
Terceiros; 
• Especificar as necessidades de adequações que serão repassadas a 
Terceiros; 
• Especificar métricas de qualidade e produtividades esperadas; 
• Especificar SLA’s de serviço e multas contratuais; 
• Estabelecer concorrência e obter a melhor proposta (opcional); 
• Receber Proposta de Trabalho (Cronograma, Prazos e Custos da 
Terceirização); 
• Definição de Cenários Possíveis (Duração, Esforço, Custo e 
Qualidade): 
• Levantar Lista de Projetos em Andamento e a serem Iniciados; 
• Avaliar a disponibilidade de recursos internos para alocação no 
Projeto; 
• Identificar Cenários Diversos (Terceirização, Redução de Escopo, 
Repriorização de Projetos); 
• Definir Cronograma-Macro para cada cenário identificado; 
• Definir Riscos para cada cenário identificado e Planos de Ação 
Esperados; 
• Estabelecer Propostas e Aguardar aprovação da Diretoria; 
• Aprovação do Planejamento: 
• Obter o Aceite das Propostas de Cenários Aprovados pela Diretoria; 
• Obter o Aceite de uma das Propostas pelo Cliente; 
• Divulgar do Cenário Aprovado do Projeto aos colaboradores e 
terceiros; 
• Obter a Assinatura do CONTRATO-MESTE e elaborar os ANEXOS; (no 
caso de terceirização) 
• Alocar Espaço Físico dos Terceiros; (no caso de terceirização) 
• Comunicar a Finalização da Etapa de Planejamento dos Testes; 
(externo) 
• Definição das Responsabilidades 
• Neste diagrama, está a representação dos papéis e 
responsabilidades para cada grupo de atividades envolvido na etapa 
de “Planejamento dos Testes”. 
DEFINIÇÃO DE CENÁRIOS POSSÍVEIS (DURAÇÃO, ESFORÇO, CUSTO E 
QUALIDADE): 
• Levantar Lista de Projetos em Andamento e a serem Iniciados; 
• Avaliar a disponibilidade de recursos internos para alocação no 
Projeto;• Identificar Cenários Diversos (Terceirização, Redução de Escopo, 
Repriorização de Projetos); 
• Definir Cronograma-Macro para cada cenário identificado; 
• Definir Riscos para cada cenário identificado e Planos de Ação 
Esperados; 
• Estabelecer Propostas e Aguardar aprovação da Diretoria; 
APROVAÇÃO DO PLANEJAMENTO: 
• Obter o Aceite das Propostas de Cenários Aprovados pela Diretoria; 
• Obter o Aceite de uma das Propostas pelo Cliente; 
• Divulgar do Cenário Aprovado do Projeto aos colaboradores e 
terceiros; 
• Obter a Assinatura do CONTRATO-MESTE e elaborar os ANEXOS; (no 
caso de terceirização) 
• Alocar Espaço Físico dos Terceiros; (no caso de terceirização) 
• Comunicar a Finalização da Etapa de Planejamento dos Testes; 
(externo) 
DEFINIÇÃO DAS RESPONSABILIDADES 
Neste diagrama, está a representação dos papéis e responsabilidades 
para cada grupo de atividades envolvido na etapa de “Planejamento dos 
Testes”. 
 
 
MAPEAMENTO DOS ARTEFATOS 
Nesta representação gráfica, estão destacados os “artefatos de entrada” 
exigidos como premissa para que cada macro-atividade possa ser 
realizada. Também são destacados os “artefatos de saída” produzidos 
como resultado da atividade. 
ETAPA 2: ESPECIFICAÇÃO DOS TESTES 
Esta etapa é caracterizada pela identificação dos casos de testes que 
deverão ser construídos e modificados em função das mudanças 
solicitadas pelo Cliente, bem como pelo próprio aperfeiçoamento do 
processo de testes (ampliação da cobertura). 
DINÂMICA DAS MACRO-ATIVIDADES 
Este diagrama representa a seqüência das “macro-atividades” a serem 
executadas na etapa de “Especificação dos Testes”. 
DETALHAMENTO DAS MACRO-ATIVIDADES 
Esta lista representa o conjunto de atividades que deverão ser executadas 
para que cada macro-atividade seja considerada finalizada, funcionando 
como um “check-list” de execução da etapa de “Especificação dos Testes”. 
ESTUDO DOS REQUISITOS: 
• Estudar os requisitos funcionais e não funcionais solicitadas pelo 
Cliente (novos requisitos); 
• Estudar as modificações de requisitos solicitados pelo Cliente 
(mudanças de requisitos); 
• Revisar os artefatos e identificar “inconsistências” dos requisitos; 
• Estabelecer o Aceite dos Documentos fornecidos e “feedback” da 
qualidade dos mesmos; 
• Estudar as lições aprendidas da Etapa “Especificação de Testes”; 
ESPECIFICAR AS ADAPTAÇÕES DA ARQUITETURA DOS TESTES: 
• Especificar as adequações nas atuais ferramentas empregadas; 
• Especificar as novas ferramentas exigidas pelo projeto; 
• Especificar as modificações estruturais na organização do ambiente; 
• Especificar as adequações na automação da preparação do 
ambiente (script de teste); 
• Especificar as adequações na automação da execução dos testes 
(script de teste); 
• Especificar as adequações na automação da análise dos resultados 
(script de teste); 
IDENTIFICAÇÃO DOS CASOS DE TESTES 
• Identificar cada solicitação de mudança requisitada pelo Cliente; 
• Identificar todos os Casos de Uso envolvidos em cada solicitação; 
• Identificar Casos de Uso não cobertos adequadamente por Casos de 
Testes; (legado) 
• Identificar todos o Fluxos do Caso de Uso (Básico, Alternativo e 
Exceção); 
• Identificar os casos de testes que garantam cada Fluxo do Caso de 
Uso; 
REFINAMENTO DOS CASOS DE TESTES: 
• Estabelecer dinâmica com os Analistas de Testes que possuem 
conhecimento horizontal; 
• Apresentação de um quadro-geral do impacto das mudanças nos 
respectivos aplicativos; 
• Cada Analista de Testes apresenta seus casos de testes por 
aplicativo; 
• O grupo de Analistas de Testes criticam e sugerem melhorias nos 
casos de testes; 
• O grupo de Analista de Testes avalia o nível de cobertura alcançado; 
• Novas reuniões serão realizadas até que seja alcançado o patamar 
ideal de casos de testes; 
ACEITE DOS CASOS DE TESTES: 
• Identificar Áreas-Chaves para apresentação dos casos de testes 
(Clientes Internos e Externos) 
• Apresentar os casos de testes “progressivos” que serão aplicados 
nos testes; 
• Apresentar os casos de testes “regressivos” que serão aplicados nos 
testes; 
• Realizar refinamento dos casos de testes apresentados (“regressivos 
e progressivos”); 
• Estabelecer o acordo Mútuo de Responsabilidade sobre o Nível de 
Qualidade do Software; 
REFINAMENTO DO PROJETO DE TESTES: 
• Reavaliar as estimativas de esforço e duração do Processo de Teste; 
(se necessário) 
• Estabelecer um Cronograma-Detalhado, baseado no Cronograma-
Macro já elaborado; 
• Reavaliar riscos do Projeto em função de um maior detalhamento 
sobre os requisitos; 
• Negociar eventuais modificações em relação à duração, prazo e 
custo do projeto de testes; 
• Comunicar a Finalização da Etapa de “Especificação dos Testes”; 
(externo) 
DEFINIÇÃO DAS RESPONSABILIDADES 
Neste diagrama, está a representação dos papéis e responsabilidades para 
cada grupo de atividades envolvido na etapa de “Especificação dos 
Testes”. 
MAPEAMENTO DOS ARTEFATOS 
Nesta representação, estão destacados os “artefatos de entrada” exigidos 
como premissa para que cada macro-atividade possa ser realizada. 
Também são destacados os “artefatos de saída” produzidos como 
resultado da atividade. 
CALCULANDO O ROI (RETORNO DE INVESTIMENTOS) EM TESTE DE 
SOFTWARE 
Convido você agora a se aprofundar e calcular junto comigo os custos dos 
defeitos para um software e o retorno do investimento na realização de 
teste de software. 
Primeiramente devemos identificar custos, incidências e percentuais de 
correção de defeitos nas fases do ciclo de desenvolvimento do software, 
assim conseguiremos realizar um cálculo de valores baseado na realidade. 
Em primeiro lugar, é preciso observar que o custo de correção de defeitos 
encontrados em qualquer pacote de software cresce exponencialmente a 
cada fase da criação ou existência deste software, o que pode ser 
representado, segundo o autor Ron Patton, da seguinte forma [8] como 
mostra a figura abaixo. 
 
Aumento exponencial dos custos com defeitos segundo Patton [8] 
O custo de encontrar defeitos e removê-los na fase de especificação é 
baixíssimo, na ordem de grandeza de dezenas de centavos (de uma 
moeda qualquer). Na fase de design estes custos já crescem para a ordem 
de grandeza de unidades de moeda, e assim por diante, até chegarem a 
custos na casa das centenas quando o software já está em produção. O 
custo baixo de se encontrar defeitos nas fases de especificação e design 
se justifica pelo baixo — ou, em alguns casos, desprezível — retrabalho 
resultante da descoberta e correção destes defeitos nestas fases iniciais. 
Seria o equivalente a amassar o guardanapo e começar a rabiscar 
novamente. Defeitos encontrados na fase de codificação já são mais caros 
porque muitas vezes exigem testes realizados pelos desenvolvedores (o 
que toma tempo), correção ou mesmo descarte de código desenvolvido e, 
nos piores casos, um retrocesso às fases de especificação e design. 
Já os defeitos encontrados na fase de produção, além de todos os custos 
anteriores, implicam em custos de atendimento ao cliente, replicação dos 
defeitos em laboratório, chegando até mesmo aos temidos recalls. Em 
suma, o raciocínio de Patton é claro: quanto mais cedo forem 
encontrados os defeitos, menos custarão, tanto para os desenvolvedores 
quanto para os clientes. 
https://blog.onedaytesting.com.br/wp-content/uploads/2016/02/bug-momentos.png
Quanto à eficiência dos testes na descoberta de defeitos de software, 
dados obtidos a partir de projetos da própria Sofist mostram que quando 
o cliente atribui tarefas de testes para parte de seu time de 
desenvolvimento, este consegue descobrir no máximo 40% dos defeitos 
do software em questão. 
CALCULANDO O RETORNO SOBRE O INVESTIMENTO (ROI) 
 
Tomando por base empresas que seguem o modelo CMMI, temos que 
aquelas que se enquadram no nível 1 tipicamente entregam software com 
7,5 defeitos a cada 1.000 linhas de código, enquanto as empresas com 
certificação CMMI nível 5 produzem softwarecom melhor qualidade, 
reduzindo os defeitos para uma média de 1 a cada 1.000 linhas de código 
[9]. 
Nesta situação, tomemos por hipótese um pacote de software com 1 
milhão de linhas de código — nada incomum em projetos atuais — que 
tenha sido codificado com 1.000 (mil) defeitos inadmissíveis, isto é, que 
terão que ser corrigidos a qualquer custo, seja em que fase da vida do 
software forem encontrados. Assumindo, de acordo com o raciocínio de 
Patton, que defeitos encontrados durante a fase de desenvolvimento 
tenham custo de R$ 1,00, durante a fase de testes tenham um custo de 
R$ 10,00 e que os mesmos defeitos encontrados com o software em 
produção tenham um custo de R$ 100,00, podemos construir um cenário 
interessante. 
https://www.sofist.com.br/
ANÁLISE DE 3 SITUAÇÕES: 
1. O software vai para o mercado sem ter sido formalmente testado; 
2. O software passa por um processo de testes realizados por pessoal 
interno, interinamente designado pela empresa para realizá-los; 
3. O software passa por um processo testes realizados por equipe 
especializada terceirizada; 
PREMISSAS DA ANÁLISE 
• 10% dos defeitos serão encontrados na fase de codificação, 0%, 40% 
ou 85% dos defeitos serão encontrados na fase de testes 
(respectivamente nos cenários 1, 2 e 3) e o restante será encontrado 
na fase de produção; 
• O salário considerado no caso dos testes internos é de R$3.000,00 
mensais, com custo para a empresa (acrescido dos benefícios) de 
R$5.000,00 mensais; 
• Ainda no caso de testes internos, considera-se a atuação parcial de 
um gerente de testes com salário de R$5.000,00 (R$10.000,00, com 
benefícios), com 50% de seu tempo designado para o processo de 
testes; 
• Os testes terceirizados são conduzidos por uma equipe que custa 
R$10.000,00 por mês, durante três meses; 
• Considera-se ROI como sendo a razão entre os valores 
economizados (em relação à abordagem sem testes) e o valor dos 
investimentos em cada uma das opções de testes avaliadas neste 
caso; 
RESULTADOS – TABELA ROI 
 
De acordo com este raciocínio, podemos compor a tabela apresentada 
acima. A tabela mostra com clareza que os investimentos em testes de 
software, sejam estes realizados por pessoal interno ou por equipe 
especializada terceirizada, trazem vantagens financeiras, constituindo-se 
em investimentos que geram reduções relevantes dos custos de 
desenvolvimento do produto. 
 
Os resultados, como pode ser claramente visto na imagem acima, 
apresentam uma justificativa sólida para a realização dos testes. 
ESTILO DE CÓDIGO – BOAS PRÁTICAS DE PROGRAMAÇÃO EM 
LINGUAGEM C 
No mundo dinâmico do desenvolvimento de software para sistemas 
embarcados, um código ou algoritmo não pode simplesmente ser 
considerado como correto por realizar aquilo que se deseja. Outros 
aspectos relacionados à legibilidade, manutenção e segurança devem ser 
considerados durante sua implementação. 
Um desenvolvedor de software em geral, inclusive embarcado, 
idealmente deve prezar por manter seu código dentro de padrões 
definidos ou pelo senso comum, pelo projeto em que se está contribuindo 
na comunidade, ou por regras da própria empresa em que se trabalha. 
Este artigo considera as boas práticas de programação para qualquer 
nível, desde desenvolvedores que estão entrando no mundo embarcado 
até sêniors que procuram melhorar sua performance no desenvolvimento 
de código. 
Mas o que seriam estes estilos de código, para que servem e o que 
podemos conseguir de melhorias ao adotar boas práticas de 
programação? 
Bem, de forma geral, o estilo de código pode ser considerado uma 
questão filosófica diretamente ligada aos gostos do desenvolvedor. 
Porém, tanto para desenvolvedores embarcados quanto para propósitos 
gerais, existem alguns estilos adotados (e comprovados de alguma forma) 
que podem beneficiar tanto o próprio desenvolvedor quanto outros que 
porventura poderão vir a utilizar o código ou algoritmo desenvolvido. 
Iremos neste artigo exemplificar como não programar na linguagem C, ou 
seja, uma pequena paródia ao que se deveria ser feito em comparação a 
erros comuns da linguagem, citando pensamentos que o programador faz 
durante o desenvolvimento do código. 
“ISSO NUNCA VAI ACONTECER” 
Desenvolvedor se depara em situações que “nunca irão acontecer”, e, por 
conta disso, algumas verificações no código deixam de existir. Um 
exemplo famoso para este tipo de suposição aconteceu em 2014 com o 
tão conhecido YouTube, onde o vídeo Gangnam Style causou o overflow 
da variável que conta o número de visualizações de vídeo (int32_t até 
então). Este caso não foi tão grave, mas em uma situação crítica, 
suposições deste tipo devem garantir que o que não pode acontecer 
realmente não irá acontecer. Uma boa forma de garantir que tais 
suposições realmente assegurem que não possam acontecer é 
utilizar asserts, exemplo: 
C 
1 
2 
3 
4 
void write_string(char *str) { 
 assert(str != NULL); 
 /* codigo aqui */ 
} 
 
É um exemplo bem simples, mas vamos imaginar que esta função seja 
uma função interna de uma lib, e portanto, quem a desenvolveu está 
garantindo que nunca vai acontecer um caso em que uma string nula será 
passada. Porém, aqui é o caso perfeito de se prevenir contra aquilo que 
“nunca irá acontecer”, portanto, o uso do assert é bem recomendado. 
P.S.: Claro, se for uma lib que será utilizada por outros clientes, é natural 
existir verificações dos argumentos da função, mas não iremos entrar 
neste caso, pois é o caso normal de verificação de possíveis erros durante 
o desenvolvimento de código. 
Pensando no próprio caso do YouTube, a suposição de que nenhum vídeo 
seria visualizado mais vezes do que um signed int de 32 bits pudesse 
armazenar, também pode ser verificado: 
C 
1 
2 
3 
4 
int increment_counter() { 
 assert(count < LONG_MAX); 
 ++count; 
} 
Outro caso simples, mas pensando em uma aplicação de um sistema 
crítico, onde o desenvolvedor assumiu que o nível do tanque nunca irá 
ultrapassar um valor limite. Esta suposição pode não ser sempre verdade, 
e em caso de overflow, a variável irá começar a contar do zero, e sua 
planta estará transbordando, mas seu sistema de controle irá dizer que o 
nível está baixo, ou normal. 
Assertions são bons candidatos para se garantir que coisas que “nunca 
irão” acontecer realmente nunca irão acontecer. 
BOAS PRÁTICAS DE PROGRAMAÇÃO – FORÇAR EVITAR FALSOS 
POSITIVOS 
Existem alguns casos de desenvolvimento em que estamos realizando 
testes unitários em nosso código e, ao forçar algum erro, temos falsos 
positivos. Ou seja, ao testar o retorno de uma função para algum erro, 
devemos garantir que estamos testando contra o erro que foi forçado e 
não testando apenas contra algum erro genérico. 
Suponha que você esteja validando a funcionalidade seu método que 
valida uma senha e você quer ver se forçando um erro de ponteiro 
inválido o método estará retornando erro. Os possíveis retornos de erro 
são: 
C 
1 
2 
3 
#define ERR_TAMANHO_INVALIDO (-1) 
#define ERR_PONTEIRO_INVALIDO (-2) 
#define ERR_SENHA_INVALIDA (-3) 
E no seu teste unitário você está testando, sem se dar conta, de que está 
acontecendo um possível falso positivo. Ou seja, você conseguiu forçar o 
erro, porém, você não está testando contra o erro específico que você 
está procurando, mas sim contra qualquer erro. Abaixo é exibido um 
exemplo simples: 
C 
1 assert(valida_senha(ponteiro_do_buffer, tamanho_buffer) < 0); 
Se sua função possuir algum bug, e se você testar contra o erro que de 
fato deveria ser testado, pode ser que o erro não seja o mesmo que você 
espera por algum problema de implementação. Para evitar estes falsos 
positivos, é ideal sempre testar contra exatamente aquilo que se está 
esperando. Abaixo segue o exemplo correto, onde o método é testado 
contra todos seus possíveis códigos de erro. 
C 
1 
2 
3 
assert(valida_senha(ponteiro_do_buffer, tamanho_buffer) == ERR_TAMANHO_INVALIDO); 
assert(valida_senha(ponteiro_do_buffer,tamanho_buffer) == ERR_PONTEIRO_INVALIDO); 
assert(valida_senha(ponteiro_do_buffer, tamanho_buffer) == ERR_SENHA_INVALIDA); 
FUNÇÕES QUE INICIALIZAM DEVEM SEGUIR O CONCEITO DE 
ATOMICIDADE 
Quando criamos alguma função do tipo inicia_recurso() que inicializa 
algum recurso ou módulo, devemos nos preocupar nos aspectos de 
atomicidade. Isto é, a função inicia_recurso() deve possuir apenas dois 
estados: (1) executado com sucesso; (2) não executado. Ou seja, se ela 
falhar em sua execução, ela deve retornar ao estado em que se ela nunca 
tivesse sido executada/invocada. É necessário que a função internamente 
garanta que em caso de falha, ela desaloque qualquer recurso que tenha 
sido alocado durante sua execução. Assim ela retorna em um estado 
seguro, mesmo em caso de falha. 
C 
1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
24 
25 
26 
27 
28 
29 
30 
#include <stdint.h> 
#include <stdbool.h> 
#include <stdio.h> 
#include <string.h> 
#include <stdlib.h> 
 
typedef struct ctx { 
 char *nome; 
 int log; 
} ctx_t; 
 
int inicia_log(char *nome) { 
 int index_log; 
 Index_log = cria_arquivo_log(nome); 
 If (index_log <= 0) { 
 syslog (LOG_INFO, "Arquivo de log nao iniciado"); 
 } else { 
 syslog (LOG_INFO, "Sucesso ao criar arquivo de log"); 
 } 
 return index_log; 
} 
 
bool inicia_daemon(ctx_t *ctx) { 
 static const char *string = "daemon_app"; 
 ctx->nome = malloc(strlen(string)); 
 if (ctx->nome == NULL) { 
 return false; 
 } 
 strncpy(ctx->nome, string, strlen(string)); 
 ctx->log = inicia_log(ctx->nome); 
31 
32 
33 
34 
35 
36 
37 
38 
39 
40 
41 
42 
43 
44 
45 
46 
 if(ctx->log) { 
 free(ctx->nome); 
 ctx->nome = NULL; 
 return false; 
 } 
 return true; 
} 
 
int main() 
{ 
 ctx_t ctx; 
 bool resp; 
 
 resp = inicia_daemon(&ctx); 
 return resp ? 0 : -1; 
} 
No exemplo acima, percebemos que, em caso de falhas intermediárias da 
função inicia_daemon(), ela mesmo se encarregará de desalocar todos os 
recursos que foram alocados até então, inclusive limpando o ponteiro que 
havia sido atribuído em ctx->nome. Desta forma é possível garantir que 
em qualquer falha de métodos inicializadores, eles serão executados 
atomicamente pelo seu código. 
PONTO ÚNICO DE RETORNO 
Este estilo de código requer bastante conhecimento do código para que 
seja seguro utilizar este estilo de programação pois, para ser efetivo, o 
código deve permitir o erro em cascata, isto é, a propagação de erros 
dentro da função não irá quebrar o código. No exemplo anterior, não 
poderíamos implementar este método pois a alocação de memória para 
armazenar o nome do daemon irá determinar se a próxima instrução 
pode ou não ser executada. Desta forma, aplicando o ponto único de 
retorno neste código iria nos gerar o tão temível segmentation fault por 
acesso indevido à região de memória. Em algumas situações ele é 
bastante útil para que possamos entender o que aconteceu de errado no 
fluxo de código. Abaixo segue um exemplo simples de como pode-se 
realizar erro em cascata tirando vantagem do ponto único de retorno: 
C 
1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
24 
25 
26 
27 
28 
29 
30 
31 
32 
33 
34 
35 
#include <stdint.h> 
#include <stdbool.h> 
#include <stdio.h> 
#include <string.h> 
#include <stdlib.h> 
 
#include “valida_campos.h” /* header das funcoes valida_(*) */ 
 
#define NO_ERROR (0) 
#define ERR_VAL_MAC (1 << 1) 
#define ERR_VAL_SERIAL (1 << 2) 
#define ERR_VAL_IP (1 << 3) 
 
char valida_conexao(char **buff) { 
 char ret_val = NO_ERROR; 
 
 if(valida_mac(buff[0]) != NO_ERROR) 
 ret_val |= ERR_VAL_MAC; 
 if(valida_serial(buff[1]) != NO_ERROR) 
 ret_val |= ERR_VAL_SERIAL; 
 if(valida_ip(buff[2]) != NO_ERROR) 
 ret_val |= ERR_VAL_IP; 
 return ret_val; 
} 
 
int main() { 
 
 char mascara_erro = 0; 
 char **valores = { "AA:BB:CC:DD:EE:FF", "123456", "127.0.0.1"}; 
 
 mascara_erro = valida_conexao(valores); 
 
 verifica_erro(mascara_erro); 
 
 return 0; 
36 } 
BOAS PRÁTICAS DE PROGRAMAÇÃO – UTILIZAÇÃO DE GOTO NEM 
SEMPRE É TEMÍVEL 
Muitos são totalmente contra a utilização de GOTOs nos código, porém 
vários artigos propõem sua utilização (inclusive muitos goto são vistos no 
Linux kernel) de forma correta. No próprio exemplo anterior, quando não 
é possível realizar o cascateamento de erro por conta de alocação de 
memória por exemplo, a utilização de goto é bem empregada. Vamos 
supor que os métodos valida_* realizam alocações de memória, e que 
estas alocações são dependentes entre métodos, isto é, a alocação que é 
realizada internamente por validate_mac irá determinar que 
a validate_serial possa ser executada ou não. 
C 
1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
char valida_conexao(char **buff) { 
 
 char ret_val = NO_ERROR; 
 
 if(valida_mac(buff[0]) != NO_ERROR) { 
 ret_val = ERR_VAL_MAC; 
 goto invalid_mac; 
 } 
 
 if(valida_serial(buff[1]) != NO_ERROR) { 
 ret_val = ERR_VAL_SERIAL; 
 goto invalid_serial; 
 } 
 
 if(valida_ip(buff[2]) != NO_ERROR) { 
 ret_val = ERR_VAL_IP; 
 goto invalid_ip; 
 } 
 
invalid_ip: 
 free_ip(buff[2]); 
22 
23 
24 
25 
26 
27 
28 
invalid_serial: 
 free_serial(buff[1]); 
invalid_mac: 
 free_mac(buff[0]); 
 
 return ret_val; 
} 
Neste exemplo, vimos que os gotos seguem a ordem inversa das 
execuções das funções que validam os respectivos valores. Essa inversão 
ocorre pois, se a verificação falha no seu último passo, todos os passos 
anteriores devem ser desfeitos. Assim, a desalocação de memória 
realizada pelos passos intermediários também é desfeita, evitando 
vazamento de memória. 
OS PERIGOS DE NÃO SE UTILIZAR CHAVE 
Muitos desenvolvedores não utilizam chaves em declarações simples pois 
não há necessidade de proteger com chaves uma única linha de código. 
No Linux kernel e em muitos outros códigos open-source utilizam este 
paradigma de declarações com blocos de código de única linha não são 
protegidos com chaves. Porém, este tipo de regra é bastante perigosa 
para o desenvolvimento de sistemas embarcados, e aqui vão alguns 
exemplos que pode custar ao desenvolvedor horas de depuração, ou 
custar até mesmo uma nova planta. 
C 
1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
int loop_principal() { 
 int ret = NO_ERROR; 
 
 if (verifica_nivel(tanque) >= VALOR_MAXIMO) // verifica nivel maximo 
 dispara_alarme(); 
 
 ret |= loop_medicao(); 
 ret |= motor_ligado(); 
 
 return ret; 
} 
Supondo então que o desenvolvedor apagou alguma parte do 
comentário, e sem perceber, a linha de código dispara_alarme() acaba 
subindo de linha, e fazendo parte do comentário. 
C 
1 
2 
3 
4 
5 
6 
7 
8 
9 
int loop_principal() { 
 int ret = NO_ERROR; 
 
 if (verifica_nivel(tanque) >= VALOR_MAXIMO) // verifica nivel dispara_alarme(); 
 ret |= loop_medicao(); 
 ret |= motor_ligado(); 
 
 return ret; 
} 
Este é um dos erros mais inocentes que pode acontecer, mas como visto 
no exemplo, pode-se nunca mais disparar o alarme e além disso, 
o loop_medicao() pode vir a ser executado somente quando o nível está 
igual ou maior que valor limite. Este é um pequeno erro, onde o 
desenvolvedor resolveu apagar uma pequena parte do comentário e, por 
causa de um descuido, a linha abaixo do comentário subiu e fez parte do 
próprio comentário. 
Por mais insignificante que este descuido possa parecer, suas 
consequências são graves e sim, este tipo de erro pode acontecer com 
mais frequência do que imaginamos.Para evitar isso, procure comentar 
código sempre com blocos de comentários /* comentario aqui */, desta 
forma você está garantindo que apenas o que está dentro do bloco será 
seu comentário. 
PROGRAMAÇÃO EM ÚNICA LINHA 
Quando aprendemos a utilizar os operadores ternários, acabamos 
querendo realizar inúmeras operações em uma única linha. Parece mais 
eficiente visualmente falando, porém, isso afeta diretamente a 
legibilidade do código e, por consequência, afeta o desempenho durante 
alguma depuração de possível bug. 
C 
1 
2 
3 
int adicionar_atualizar_usuario(char *nome, char flag_adiciona) { 
 return (cria_usuario(nome, grupo(flag_adiciona == 'Y')) ? move_usuario() : remove_usuario()); 
} 
É de se admitir que em uma única linha de código, conseguimos escrever 
o que se precisaria de talvez 4 ou 5 linhas. Talvez este tipo de código 
segue a filosofia de que quanto menos linhas de código, menos bugs, mas 
vamos comparar com o código abaixo, onde o mesmo código acima foi 
escrito, porém em linhas separadas prezando pela legibilidade. 
C 
1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
int adicionar_atualizar_usuario(char *nome, char flag_adiciona) { 
 int ret = 0; 
 bool adiciona = (flag_adiciona == 'Y'); 
 
 ret = cria_usuario(nome, grupo(adiciona)); 
 
 adiciona ? move_usuario : remove_usuario(); 
 
 return ret; 
} 
Legibilidade é fator fundamental para o desenvolvimento, e por mais 
claro que as instruções dessa linha possam ser, qualquer outro 
desenvolvedor terá que gastar alguns minutos tentando entender o fluxo 
de execução desta linha. 
COMENTAR O ÓBVIO 
Os famosos fall-through de switch cases são geradores de dor de cabeça 
para quem está lendo ou revisando algum código. Erros em switch cases 
são bastante comuns pelo esquecimento de breaks que acabam fazendo 
com que um case passe por dentro de outro, porém, algumas vezes o 
desenvolvedor realmente deseja isso. Contudo, se nos depararmos com 
esta situação, e nenhum comentário é vinculado a isso, certamente 
iremos achar que o desenvolvedor esqueceu de colocar o break e 
podemos sem querer querendo alterar toda a lógica do código. Por isso, é 
muito importante comentar inclusive o óbvio, para evitar problemas 
simples, mas que geram resultados catastróficos. 
C 
1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
switch(selecao) { 
 case A: 
 rotaciona_90x(); 
 break; 
 case B: 
 rotaciona_90y(); 
 break; 
 case C: 
 rotaciona_90y(); 
 default: 
 rotaciona_90z(); 
} 
Olhando o código acima, podemos pensar que o desenvolvedor esqueceu 
de colocar o break no case C. Então, decidimos colocar o break que está 
faltando. Isso irá evitar que o rotaciona_90z() seja executado, e o braço 
do robô irá agir de maneira errada. Ou seja, por interpretação errada de 
outro desenvolvedor, agora o sistema está com bug. Para evitar isso, um 
simples comentário bastaria para evidenciar a necessidade do fall-
through. 
C 
1 
2 
3 
4 
5 
case C: 
 rotaciona_90y(); 
 /* Fall-through: precisamos rotacionar o eixo Z */ 
 default: 
 rotaciona_90z(); 
BOAS PRÁTICAS DE PROGRAMAÇÃO – NÃO ECONOMIZE TEMPO AO 
DOCUMENTAR CÓDIGO 
Quando se desenvolve código em empresa ou para a comunidade, é 
importante zelar pela documentação do código para que outros possam 
entender o que ele realiza sem precisar de grande conhecimento de suas 
entranhas. Ser claro ao documentar é tão importante quanto se 
preocupar em desenvolver um código limpo e eficiente, portanto seja 
explícito quanto aos parâmetros de entrada e saída de sua função/rotina, 
etc. Se sua função requer ponteiros, comente também se os ponteiros são 
desalocados em caso de falha, entre outros recursos que a função possa 
alocar. 
C 
1 
2 
3 
4 
5 
6 
7 
8 
9 
/** 
 * \brief Valida se o nivel esta de acordo 
 * \param endereco_tanque Ponteiro para o tanque desejado 
 * \param parametros Parametros do tanque solicitado 
 * \param nivel Nivel maximo permitido 
 * \return Retorna TRUE em caso de nivel OK. 
 * Retorna FALSE caso o nivel esteja acima 
 */ 
bool validar_nivel(char *endereco_tanque, char **parametros, int nivel); 
Neste exemplo acima, a documentação não diz qualquer informação 
sobre como os ponteiros são tratados. Fica assim a cargo do 
desenvolvedor de analisar o código (se possível), ou ir na tentativa e erro 
para descobrir se os ponteiros devem ser desalocados externamente ou 
não. 
Agora, um exemplo com uma boa documentação é mostrada: 
1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
/** 
 * \brief Valida se o nivel esta de acordo 
 * \param endereco_tanque Ponteiro para o tanque desejado [IN] 
 * \param parametros Parametros do tanque solicitado [IN][OUT] 
 * \param nivel Nivel maximo permitido 
 * \return Retorna TRUE em caso de nivel OK, e as informacoes do tanque sao 
 * armazenadas no ponteiro "parametros". 
 * Retorna FALSE caso o nivel esteja acima do especificado. 
 * 
 * \info A memoria alocada "por parametros" deve ser desalocada pelo usuario. 
 */ 
12 bool validar_nivel(char *endereco_tanque, char **parametros, int nivel); 
Assim, um código bem documentado possui muito mais valor. Embora 
seja preciso dedicar tempo para documentação, o próprio desenvolvedor 
se beneficia de seus comentários, não precisando ele mesmo retornar ao 
código para lembrar seu funcionamento interno. 
(EXTRA) PRECAUÇÕES EM CÓDIGO SEGURO 
Uma questão importante relacionado ao estilo de código é buscar por 
código seguro. Ao utilizarmos funções da stdlib por exemplo, podemos 
verificar as maneiras corretas e seguras de utilizá-las. Existem 
comunidades especializadas em investigar e definir padrões de 
programação que garantem a segurança do código. 
Um website para consulta de exemplos de código seguro é encontrado 
em aqui. O CERT Secure Coding Standards é formado por grupos de 
desenvolvedores que tentam explorar vulnerabilidades da linguagem C e 
C++, dando exemplos reais e soluções “ideais” para serem utilizadas. 
Portanto, sempre que formos utilizar alguma função conhecida, como 
atoi(), é extremamente recomendável procurar no CERT sobre ela e ver o 
que eles têm a dizer, quais são suas vulnerabilidades e o que pode se 
utilizar em sua substituição. 
O padrão MISRA C define um conjunto de regras para a linguagem de 
programação em C que visa garantir padrões seguros de 
desenvolvimento. Este padrão foi desenvolvido para o desenvolvimento 
de sistemas embarcados para automóveis, porém ganhou popularidade e 
atualmente é adotado nas mais diversas áreas de desenvolvimento por 
conta de seus benefícios. 
Existem inúmeros outros lugares e comunidades que procuram 
exemplificar e apresentar soluções de situações reais em que códigos 
podem ser melhorados tanto em questão de performance quanto em 
questão de segurança. Estar atento a estes exemplos torna-se bastante 
útil para que o desenvolvedor desenvolva melhor sua capacidade de 
desenvolvimento de código nestes aspectos. 
CONCLUSÃO – BOAS PRÁTICAS DE PROGRAMAÇÃO EM LINGUAGEM C 
https://www.securecoding.cert.org/
Tentamos abordar aqui situações simples, mas que exemplificam a 
realidade e os cuidados necessários quando estamos desenvolvendo 
código. É uma boa iniciativa pensarmos de maneira não tão pragmática, 
mas um pouco filosófica em se desenvolver sistemas embarcados. O estilo 
de código não está relacionado a apenas a forma com que você escreve o 
código, mas também a forma em que ele é desenvolvido e as precauções 
utilizadas para torná-lo mais seguro. Portanto, o desenvolvimento de 
código também envolve questões filosóficas que determinam a maneira 
em que adotamos para desenvolver código e, consequentemente isso 
atinge nossa produtividade de maneira positiva ou negativa dependendo 
do que utilizamos como desenvolvimento. Se basear por estilos utilizados 
pela comunidade é um bom exercício, inclusive para quem está iniciandono desenvolvimento de software e gostaria de estar mais familiarizado 
com o “mundo real” de desenvolvimento.

Mais conteúdos dessa disciplina