sábado, 20 de agosto de 2011

A maldição das macros

Para ser direto: em C, as macros são úteis para definir constantes, definir rotinas e para compilação condicional. Em C++, somente a terceira situação é válida.

Vamos ao exemplo de uma macro válida em C++:

#define CONSOLE_DEBUG
float x = sin(y);

#if defined (CONSOLE_DEBUG )
printf(“Valor do seno de y: %f\n”, x );
#endif

E é isso. Não utilize em C++ macros para definir constantes ou rotinas. Se for programar em C, utilize-as com cuidado.

Vamos a exemplos de problemas com macros.

Queremos uma macro que calcule o cubo de um número. Ela pode ser facilmente definida como:

#define cubo( A ) A*A*A

Quando, no código, você fizer:

int y = cubo(3);

Seu código será substituído por:

int y = 3*3*3;

Alá! y vale 27! Funcionou perfeitamente!

Aí em outra parte do código você faz:

int x = 2;
int y = cubo( x + 1 );

Infelizmente o resultado de y será 7. Isso acontece porque seu código será substituído por:

int y = x + 1 * x + 1 * x + 1;

Sabemos desde criança que a multiplicação tem precedência sobre a soma, e por esta razão o resultado não foi o esperado.

Uma versão melhorada desta macro seria:

#define cubo( A ) (A)*(A)*(A)

Ok, agora o exemplo do x + 1 funciona, mas e o seguinte caso?

int y = 27 / cubo( 3 )

Esperamos que o resultado seja 1, mas infelizmente, como dizem, a estrada do inferno é pavimentada de boas intenções. Seu y valerá 81!
É simples:

27 / (3) * (3) * (3)

27/3 será executado primeiro, e o resultado disso será multiplicado por 3 duas vezes, chegando ao incrível valor de 81. Precisamos de mais parêntesis. Vamos lá:

#define cubo( A ) ( (A) * (A) * (A) )

E finalmente chegamos a macro perfeita! Bom, quase. Alguém pode fazer (e com certeza fará!) alguma coisa do tipo:

int x = 2;
int y = cubo( ++x );

Esperamos que y valha 27 no fim das contas, mas você já deve ter percebido a bosta aqui:

int y = ( ( ++x ) * ( ++x ) * ( ++x ) );

No final x valerá 5 e y valerá alguma coisa que conhecemos como undefined behavior. (http://renangreinert.blogspot.com/2011/08/x-x.html).

Infelizmente, se você programa em C, terá que conviver com este risco. Se você programa em C++, evite todos estes problemas utilizando funções inline em vez de macros. O exemplo acima seria melhor escrito na seguinte forma:

inline int cubo( int num )
{
    return num * num * num;
}

Quer que cubo valha para outros valores, como float, double, char? Utilize template:

template < typename T >
inline T cubo( T num )
{
    return num * num * num;
}

E assim ficamos todos felizes! =D

Outro exemplo de problemas com rotinas em macros são as que têm mais de 1 linha.

#define imprime \
printf( "1\n" ); \
printf( "2\n" ); \
printf( "3\n" );

for ( int i = 0; i < 10; ++i )
    imprime;

Note que apenas a primeira linha da macro (isto é, a que imprime 1) será executada 10 vezes. 2 e 3 serão impressos apenas uma vez, no fim do for.
Bom, é uma boa prática sempre fechar os fors, ifs, whiles, etc com chaves, mesmo que tenham apenas 1 linha, porém como muita gente não segue isso, é melhor fechar as suas macros também. Assim:

#define imprime \
{ \
printf( "1\n" ); \
printf( "2\n" ); \
printf( "3\n" ); \
}

Aí está um exemplo de uma macro sadia de múltiplas linhas em C (nem preciso mencionar que em C++ você deve utilizar funções inline, né?).

Ok, e os #defines que definem valores, e não rotinas?

Vamos ao exemplo:

#define MAX_VALUE 10

Esta abordagem possui vários problemas. O primeiro é que você não sabe o que é “10”. Pode ser um int, um char, um short ou até mesmo um float mal definido (a definição correta seria 10.0F).

Este problema pode ser facilmente resolvido com um cast:

#define MAX_VALUE (int)10

Melhor, mas ainda assim tem seus problemas. Suponha que seu programa calcula a pressão, a temperatura e a velocidade. MAX_VALUE é o valor máximo do quê realmente?
A única forma de saber é olhando a definição e torcer para que tenha algum comentário ou coisa do tipo.
Você pode desenvolver um padrão para definição das macros, algo do tipo PRESSAO_MAX_VALUE, VELOC_MAX_VALUE, TEMPERATURA_MAX_VALUE, etc, mas quando você trabalha com projetos muito grandes isso começa a encher o saco.

Outro problema com #defines é que se você definir MAX_VALUE em uma parte do código com 10 e em outra parte do código como sendo 20, seu compilador dará um warning, mas só. Como (infelizmente) muitas pessoas tem o hábito de ignorar warnings, isso será um problema.

Se você utilizar variáveis do tipo const em C++, você estará livre destes problemas.
No exemplo citado, você poderia ter:

class Pressao
{
public:
    static int MAX_VALUE = 10;
    ...
};

class Temperatura
{
public:
    static int MAX_VALUE = 30;
    ...
};

class Velocidade
{
public:
    static int MAX_VALUE = 100;
    ...
};

No código, você poderia utilizar:

int x = Velocidade::MAX_VALUE;
int y = Temperatura::MAX_VALUE;

e por aí vai.

Melhor, né?

É triste, mas sempre tem um porém. A versão atual do C++ só permite que tipos com valores inteiros (int, char, short...) sejam definidos na declaração dentro de classes. Assim, floats, doubles e strings estão de fora. A nova versão do C++ (a C++0x, se você ainda não ouviu falar é bom se atualizar) promete resolver este problema, mas enquanto ela não vem, vamos as alternativas.

Uma é definir as variáveis dentro do namespace. Algo do tipo:

namespace nsPressao
{
    const float MAX_VALUE = 10.5F;

    class Pressao
    {
        ...
    };
}

Outra alternativa seria declarar na classe e definir fora:

//Pressao.h
class Pressao
{
public:
    static const float MAX_VALUE;
};


e no começo do Pressao.cpp definir a variável:

//Pressao.cpp
const float Pressao::MAX_VALUE = 10.5F;

Pressao::Pressao()
...

E é isso. Espero ter convencido a todos de que macros em C++ não são uma boa opção! Abraço e bom fim de semana!



quarta-feira, 17 de agosto de 2011

CppCheck

Tenho andado meio sem tempo para postar coisas novas aqui, mas só para constar, vou dar uma dica de uma ferramenta muito boa para quem desenvolve em C++: é o CppCheck.
O CppCheck é uma ferramenta que faz análise estática no código-fonte em busca de bugs, sintaxes mal definidas e também dá sugestões de alterações para melhorar o desempenho.
Esta ferramenta é free, open-source e pode ser baixada em:

http://sourceforge.net/projects/cppcheck/

Para quem não conhece, vale dar uma conferida!



 

quinta-feira, 11 de agosto de 2011

3 dicas simples para deixar o código mais eficiente


Aqui vão três dicas de C++ que, apesar de simples, muita gente não se liga quando está desenvolvendo.

  1. Declare variáveis no menor escopo possível

Muitos programadores, principalmente aqueles que vêm do C, tem o costume de declarar todas as variáveis no começo de cada função. Esta prática, apesar de muita gente defender que é interessante por questões de design, acaba prejudicando o desempenho de seu programa.
Observe o seguinte código:

void Funcao( int x )
{
    std::string minhaString(“teste”);
    if ( x > 10 )
    {
        //faz alguma coisa com “minhaString”;
        ...
    }
    else
    {
        printf(“Valor invalido de x”);
    }
}

Note que o objeto minhaString só será utilizado caso x seja maior que 10, porém ele é criado em todas as situações. Se x for menor que 10, minhaString será criado e depois destruído sem ser usado de fato.

Uma função mais eficiente poderia ser escrita da forma:

void Funcao( int x )
{
    if ( x > 10 )
    {
        std::string minhaString(“teste”);
        //faz alguma coisa com “minhaString”;
        ...
    }
    else
    {
        printf(“Valor invalido de x”);
    }
}

Assim, caso x seja menor que 10, minhaString nem ao menos será criada.

  1. Passe objetos como const &

Observe a seguinte função:

void ImprimeVector( std::vector< int > vetor )
{
    for ( int i = 0; i < vetor.size(); ++i )
    {
        printf( "%d ", vetor[i] );
    }
}

E a sua chamada:

std::vector< int > meusNumeros;
//adiciona um monte de elementos em meusNumeros
...
ImprimeVector( meusNumeros );

A função funciona corretamente, porém note que, ao se passar meusNumeros como parâmetro para ImprimeVector, um novo vetor idêntico é criado em memória. Dependendo da quantidade de elementos contidos em meusNumeros, isso pode consumir um tempo e memória razoáveis.

Não seria melhor se pudéssemos utilizar dentro da função o mesmo objeto que foi criado fora?

Isso é simples. Basta que o parâmetro receba a referência de um vector. Para garantir que o nosso objeto não será modificado dentro da função, marcamos o parâmetro como const também. Assim:

void ImprimeVector( const std::vector< int >& vetor )

Tanto a chamada da função quanto a utilização de vetor dentro dela permanecem idênticas.

  1. Evite chamar funções desnecessariamente

Parece óbvio, mas este é um erro bastante comum. Vamos ao exemplo anterior:


for ( int i = 0; i < vetor.size(); ++i )
{
    printf( "%d ", vetor[i] );
}

Note que a função size() do vetor é chamada para cada iteração do for. Isso é desnecessário, já que sabemos que o tamanho não irá variar dentro do loop.
O melhor seria implementar a função da seguinte forma:

int tamanho = vetor.size();
for ( int i = 0; i < tamanho; ++i )
{
    printf( "%d ", vetor[i] );
}

P.s.: Alguns compiladores podem até otimizar a chamada do size() nessas situações, mas isso não é garantido. Por isso, é melhor fazer direito.


É isso aí pessoal. Outro dia posto mais dicas sobre como deixar o código mais eficiente. Até lá!

extern "C"


Como muitos já sabem, para se chamar uma função de um código em C por outro código em C++, é necessário colocar um extern “C” antes de sua declaração.
Por exemplo, suponha que você tem os seguintes arquivos:

//hello_world.h
#if !defined (HELLO_WORLD_H_)
#define (HELLO_WORLD_H_)

void imprime();

#endif


//hello_world.c
#include <stdio.h>
#include “hello_world.h”

void imprime()
{
    printf(“Hello world!”);
}


//main.cpp
#include “hello_world.h”

int main()
{
    imprime();
    return 0;
}

Se tentar compilar assim, vai dar um erro do tipo “unresolved external symbol” ou coisa do tipo.

Para resolver o problema, é necessário alterar ou o hello_world.h para:

//hello_world.h
#if !defined (HELLO_WORLD_H_)
#define (HELLO_WORLD_H_)

#if defined (__cplusplus) //já explico o que é isso
extern “C”
{
#endif

void imprime();

#if defined (__cplusplus)
}
#endif

#endif

ou o main.cpp para


//main.cpp
extern “C”
{
#include “hello_world.h”
}

int main()
{
    imprime();
    return 0;
}

O que muita gente não entende é por que isso é necessário.

Bom, vamos lá.
Quando você compila um código em C++, o compilador substitui o nome de cada função do seu código por um nome único. Isso é necessário porque em C++ você pode ter mais de uma função com o mesmo nome. Isso é chamado de name mangling (não sei se existe alguma tradução aceita para isso).
Por exemplo, suponha o seguinte código:

void Funcao( char* p ){...};
void Funcao( char* p, int a ){...};

int main()
{
    Funcao(“teste”);
    Funcao(“teste”, 5 );
    return 0;
}

Seu compilador transformará a declaração das funções em:

void __xxxy( char* p ){...};
void __txyz( char* p, int a ){...};

E as chamadas em:
__xxxy(“teste”);
__txyz(“teste”, 5 );

Note que em C isso não é necessário, pois em C não é permitido que duas funções possuam o mesmo nome.
Neste caso, no exemplo acima, o compilador de C manterá o nome “imprime” da função quando estiver compilando o “hello_world.c”, porém o compilador de C++ irá substituí-lo por alguma coisa do tipo “_xxacd” quando estiver compilando o main.cpp. Assim, quando o linker estiver linkando os objetos criados, não encontrará uma definição de “_xxacd” dentro do objeto compilado em C, e dará o erro citado.
O comando extern “C” diz para seu compilador de C++ que aquelas funções não devem sofrer o processo de mangling.

Agora, se você não entendeu o motivo do #if defined (__cplusplus) no exemplo acima, eu explico:
A macro __cplusplus é definida pelos compiladores de C++, porém não existe nos compiladores de C. Por isso, quando fizemos:

#if defined (__cplusplus)
extern “C”
{
#endif

O compilador de C que está compilando o hello_world.c irá ignorar o extern “C”, pois a macro __cplusplus não está definida.
Se não colocássemos isso, o compilador de C acusaria um erro no código, pois ele não conhece o comando extern “C”.

quarta-feira, 10 de agosto de 2011

x = x++


Neste tópico vamos abordar um dos assuntos que assusta muita gente em C e C++, principalmente quem veio de outras linguagens. É o famoso undefined behavior!

O que é um undefined behavior?

Imagine que você está jogando um jogo de tabuleiro qualquer com seu filho (ou seu pai, sua namorada, sei lá) e o dado cai no chão. O que você faz? Usa o valor que caiu ou pega o dado e joga de novo? Como a regra do jogo não diz nada, você vai acabar querendo fazer o que mais valer a pena (se deu um valor bom, usa ele. Se não, joga de novo). Provavelmente o adversário vai reclamar, mas como você é o dono do jogo você faz o que você quiser.
O famoso undefined behavior é mais ou menos isso aí. Você escreve um código que cai em alguma regra não prevista pelo padrão C ou C++ e o compilador faz o que ele quiser (sim, ele é o dono do jogo).

Então C++ é uma bosta, que não prevê estas situações?”

Não. Isso é uma característica da linguagem. Tanto C quando C++ são conhecidos por sua flexibilidade e seu ótimo desempenho, e isto se deve à sua simplicidade. Algumas situações não são previstas por suas regras e outras são explicitamente definidas como undefined behavior.

Por exemplo:

x = 0;
x = x++;

O resultado disso pode ser x == 0, x == 1, x == 50 ou o seu HD sendo formatado.
Não se espera que nenhum programador em sã consciência faça esse tipo de coisa, mas existem situações em que você pode cair em um undefined behavior sem saber.

Vamos a mais exemplos?
(para os exemplos, vamos assumir que os tipos de dados tem os seguintes tamanhos:
char – 1 byte
short – 2 bytes
int – 4 bytes
Note que, dependendo do compilador e da plataforma utilizada, eles podem ter tamanhos diferentes. Vou falar mais sobre isso em um próximo post)

Soma do Capeta

Imagine o seguinte código:

unsigned char a = 250U;
unsigned char b = 253U;
unsigned short x = a + b;

Qual será o valor de “x”?
Parece simples. Sabemos que um “unsigned char” pode contar de ZERO a 255. Já um “unsigned short” pode contar de zero a 65535. Ora, como o resultado será 503, este valor cabe em “x” e não há nada de errado com a soma, correto?
Errado! Quem define o tipo do retorno e a base em que a operação será realizada são os operandos, e não o resultado. Portanto, não estranhe se compilar o código acima e o resultado de x for 247 (também não estranhe se retornar o valor correto, já que este é um comportamento indefinido).

E também não adianta fazer:

x = (unsigned short)( a + b );
 ou 
 x = static_cast< unsigned short >( a + b );

O jeito correto de realizar esta operação e colocar pelo menos um dos operandos na base 16. Ou seja:

x = (unsigned short)a + b;
Ou, em c++:

x = static_cast< unsigned short >( a ) + b;

Integer Overflow

Já viu algum código assim?

if ( x + 1 > x ) …?

Pode parecer absurdo à primeira vista, mas este código é bastante usado para detectar se ocorreu overflow na contagem do número ou não.

Sabemos que um signed char conta desde -128 até +127.

Então qual será o valor de “x” no final da seguinte operação?

signed char x = 127;
++x;

Você testa no Visual Studio e o resultado é -128. Testa com o gcc no Linux e o resultado é -128. Testa no Mac e o resultado é -128.
Bom, isso acontece porque a maioria dos compiladores simplesmente converte o incremento em código assembly sem verificar se houve overflow ou não, e a maioria dos processadores se comporta desta forma. Mas o overflow de inteiros é um undefined behavior e deve ser evitado. Uma nova versão do gcc pode optar por manter o resultado antigo ou causar um assert quando ocorrer esta situação.

Quer saber se o número chegou ao seu valor máximo? Não faça if (x + 1 > x ). Em vez disso, utilize a biblioteca limits.h e use suas macros:
CHAR_MAX, CHAR_MIN, INT_MAX, INT_MIN, LONG_MAX, LONG_MIN, etc.

Argumentos das Funções

int FuncA()
{
    printf(“Hello “);
    return 0;
}

int FuncB()
{
    printf(“World”);
    return 0;
}

void FuncC( int a, int b )
{
    printf(“!!!!”);
}

int main()
{
    FuncC( FuncA(), FuncB() );
    return 0;
}

Este código pode tando imprimir “Hello World!!!!” quanto “WorldHello !!!!”, porque a norma não diz nada quanto a ordem em que os argumentos de uma função são executados.


Alterando uma variável const

Recentemente alguém postou o seguinte código em um grupo do LinkedIn:



int main()
{
    const int num = 5;
    int &ref = (int &)num;
    ref = ref + 2;
    printf("num addr:%x ref addr:%x,\n",&num,&ref);
    printf("num value=%x ref value=%x\n",num,ref);
}

O resultado “surpreendente” foi:

num addr:bfb403b4 ref addr:bfb403b4,
num value=5 ref value=7

Mesmo endereço mas valores diferentes!! Uau!!

É o que dá tentar alterar o valor de uma variável const. O fato é que o compilador é livre para substituir no código os locais que utilizam uma variável const em tempo de compilação (igual como faz com os defines). Por isso, se uma variável foi declarada como const, não tente modificá-la.


Desalocando um ponteiro NULL com free()

Ao contrário do que muitos pensam, tentar desalocar um ponteiro que aponta para NULL com free() é, sim, um undefined behavior. O mesmo não acontece com a função delete.

Portanto:

delete NULL; //ok, nada vai acontecer.
free( NULL ); //Undefined behavior.

Dividir um número por ZERO

int x = 3/0;

Não, o resultado não será nenhum número tendendo a infinito ou coisa parecida. O resultado disso poderá ser qualquer coisa.
Portanto, para evitar problemas, o ideal é sempre conferir o valor do divisor antes de realizar uma divisão.

Retornar ponteiro ou referência para um objeto que não existe mais

char* Funcao()
{
    char dados[20];
    strcpy( dados, “ola mundo!” );
    return dados;
}


int main()
{
    char* olaMundo = Funcao();
    printf( olaMundo );
    return 0;
}

No exemplo acima, “Funcao()” cria um array temporário chamado dados e retorna uma referência para ele.
Se este array tivesse sido criado dinamicamente, isso não seria problema (apesar que quem chama a função precisaria lembrar de deletá-lo), mas no exemplo mostrado, a memória ocupada por dados é liberada assim que “Funcao” é finalizada. Por isso, olaMundo está apontando para uma área de memória que pode conter qualquer coisa.

Bom pessoal, é isso. Listei aqui alguns dos erros mais comuns que já vi o pessoal cometendo.
Se tiverem dúvidas ou tiverem interesse em mais exemplos de comportamento indefinido, só pedir por comentários ou por e-mail.

Até a próxima!

terça-feira, 9 de agosto de 2011

O princípio KISS e a Complexidade Ciclomática


Keep It Simple, Stupid!

Em outras palavras, se você quer um código de qualidade e com poucos bugs, mantenha-o simples. Isso é meio óbvio, mas a disciplina de qualidade de software (e por que não dizer toda a Engenharia de Software?) é, basicamente, a explicação do óbvio. O problema é que é um óbvio que poucos seguem.
Existem muitas formas de deixar o seu código complexo demais para ser entendido. Seja com funções com muitas linhas, poucos comentários, classes com muito acoplamento, variáveis com nomes que não fazem sentido, etc.
Neste post iremos tratar sobre uma métrica muito utilizada por grandes empresas, porém que muitas pessoas desconhecem: a complexidade ciclomática (ou Complexidade de McCabe).

Basicamente, a complexidade ciclomática é calculada para uma função de acordo com a quantidade de caminhos diferentes possíveis dentro dela.

Por exemplo, a seguinte função:

void func( int x )
{
    if ( x > 0 )
    {
        printf(“x eh maior que zero” );
    }
    printf(“tchau!”);
}

Possui complexidade ciclomática 2, pois só há dois comportamentos possíveis dela: se x for maior que zero e se x for igual ou menor. Parece simples, mas começa a complicar quando você loops, switch/cases, etc.
Não vou entrar em detalhes aqui de como se calcular esta complexidade. Existem muitas ferramentas (frees inclusive) que fazem este cálculo. A minha favorita é a CCCC (http://sourceforge.net/projects/cccc/), que funciona tanto para códigos escritos em C quanto em C++.
Ok, e no que isso vai mudar a minha vida?”
A SEI (Software Engineering Institute) apresenta a seguinte tabela, relacionando a complexidade ciclomática com o risco da função:


Complexidade Ciclomática
Risco
1 a 10
Função simples, fácil de testar, de baixo risco.
11 a 20
Complexidade e risco moderados.
21 a 50
Função complexa, de alto risco.
> 50
Risco muito alto, impossível de testar.


Existe também uma outra tabela que mostra a probabilidade de se inserir um bug no código ao se modificar uma função, de acordo com a sua complexidade:

Complexidade da Função
Chance de inserir bug ao modificá-la
1 a 10
5,00%
20 a 30
20,00%
> 50
> 40%


Portanto, da próxima vez que escrever um código, procure manter suas funções com um risco no máximo moderado.

Quer um exemplo? Segue a análise de complexidade ciclomática realizada em cima do kernel do linux, versão 2.6.23:

Número de Funções: 161 mil
Média da complexidade ciclomática: 5
Total de funções com complexidade menor ou igual a 10: 144 mil
Total de funções com complexidade entre 11 e 20: 12 mil
Total de funções com complexidade entre 21 e 50: 4800
Total de funções com complexidade maior que 50: 722.

Sim, nem o Linus Torvalds consegue fugir das funções de risco muito alto. Porém note que apenas 3% das funções do código possui complexidade maior que 10. Assim fica fácil saber onde é mais provável de encontrar os bugs neste código, não é? :)


sexta-feira, 5 de agosto de 2011

brainfuck!

Cansou de C++? Quer aprender Java, PHP, C#, ADA? Ou talvez algo mais baixo-nível, como Assembly ou VHDL?

Que nada! Macho mesmo programa em brainfuck!

Se nunca ouviu falar, aqui vai a definição da wikipedia:

"brainfuck (também conhecido como brainf*ck, ou BF) é uma linguagem de programação esotérica notada pelo seu extremo minimalismo, criada por Urban Müller, em 1993. Ela é uma linguagem Turing completa, desenhada para desafiar e confundir os programadores, e não é útil para uso prático. Pela sua simplicidade, o desenvolvimento de compiladores e interpretadores para essa linguagem é muito mais fácil do que para outras linguagens. O nome da linguagem é geralmente não-capitalizado, apesar de ser um substantivo próprio."

Para entender brainfuck, basta imaginar um array de 30 mil posições de signed chars, todos inicializados com zero. Algo como:

char array[30000];
memset( array, 0, sizeof( array ) );

Agora crie um ponteiro "p" para o início deste array:

char* p = array;

É isso que você tem para programar.
Os comandos de brainfuck são:



ComandoUtilidade
+Incrementa o valor do dado na posição apontada (equivalente a fazer (*p)++ em C)
-Decrementa o valor do dado na posição apontada (equivalente a fazer (*p)-- em C)
>Aponta para o próximo byte (equivalente a fazer ++p em C ).
Se estiver na última posição, volta para a primeira.
<Aponta para o byte anterior (equivalente a fazer --p em C ).
Se estiver na primeira posição, salta para a última.
.Imprime na tela o valor do dado na posição apontada (equivalente a putchar( *p ) )
,Lê um caractere do teclado e copia para a posição apontada (equivalente a *p =  getchar() )
[Abre um loop, que será executado até a posição apontada ficar zero.
(algo como um while( *p ){
]Fecha o loop anterior.


Qualquer outro comando é ignorado pelo compilador.

Um "Hello World" em brainfuck poderia ser escrito da seguinte forma:

++++++++++[>++++++++>+++++++++++>---------->+++>++++++++>+++++++
+++++>+++++++++++>++++++++++>+++++++++++>+++<<<<<<<<<<-]>-.>--.>
++++.>++.>---.>---.>.>.>+.>+++.

Agora você já pode colocar mais uma linguagem de programação no seu Currículo =]


Não viu utilidade?
Há pessoas que ainda defendem seu uso, dizendo que com esta linguagem é possível criar o menor compilador do mundo (um compilador de brainfuck tem menos de 200 bytes).
E você que pensava que sua torradeira jamais seria capaz de compilar um código...

Misra C e outros padrões de desenvolvimento

O Misra C (http://www.misra.org.uk/) é um padrão de desenvolvimento, criado pela indústria automobilística, com o objetivo de facilitar a portabilidade e a confiabilidade dos códigos escritos em C para sistemas embarcados.
Em 2008 foi lançada uma versão do Misra para C++ também.
O preço disso?
10 libras para o Misra C, 15 libras para o C++.

Não tem dinheiro para comprar?
Uma alternativa free é o padrão JSF++, criado pela Joint Strike Fighter. Ele basicamente é responsável por manter as areonaves da JSF no ar. Interessante, né?

O documento pode ser baixar aqui:
http://www.jsf.mil/downloads/documents/JSF_AV_C++_Coding_Standards_Rev_C.doc

Mesmo que você não desenvolva softwares embarcados, vale a pena dar uma lida.