___  ___           _         ______ _                  _       
|  \/  |          | |        | ___ (_)         _      (_)      
| .  . | ___ _ __ | |_  ___  | |_/ /_ _ __   _//_ _ __ _  __ _ 
| |\/| |/ _ \ '_ \| __|/ _ \ | ___ \ | '_ \ / _` | '__| |/ _` |
| |  | |  __/ | | | |_|  __/ | |_/ / | | | | (_| | |  | | (_| |
\_|  |_/\___|_| |_|\__|\___| \____/|_|_| |_|\__,_|_|  |_|\__,_|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~www.mentebinaria.com.br

0x1C - Especificações da linguagem C

por Fernando Mercês <fernando *em* mentebinaria.com.br>
** Licenciado sob a Creative Commons 3.0 **

Há algum tempo tinha vontade de escrever algo sobre os padrões da linguagem C e recentemente, motivado por uma discussão na lista exploits-brasil [1] e aproveitando um domingo nublado no Rio de Janeiro, decidi começar. :)

Este artigo vai ajudá-lo a entender (e quando utilizar) os padrões de programação da linguagem C. Não ache que a utilidade é teórica, pois não é. Seguir um padrão pode ajudar na segurança de código, otimização e até aumentar a produtividade, como veremos mais a diante (sabia que C tem o tipo boolean, por exemplo?).

  1. Introdução
  2. Linha do tempo do C
  3. Recursos da C99
  4. Recursos da C11
  5. Qual especificação usar?
  6. Conclusão

1. Introdução

A maioria das linguagens de programação seguem padrões abertos, que os criadores de compiladores (ou interpretadores) precisam seguir para suportar a linguagem completamente. A linguagem C foi criada por Denis Ritchie (falecido em 2011) entre 1969 e 1973 com o objetivo de substituir o Assembly (o que explica a existência comandos como goto na linguagem). Desde então, várias versões de compiladores foram criadas, por várias pessoas e empresas, sem seguir uma especificação oficial. Por isso, em 1983, o American National Standards Institute, mais conhecido pela siga ANSI, formou um comitê para estabelecer um padrão, que só foi concluído em 1989.

2. Linha do tempo da linguagem C

1969-1973
Denis Ritchie cria a linguagem C

1983
O ANSI cria um comitê para especificar a linguagem

1989
A especificação ANSI X3.159-1989 é concluída. O nome informal dela é C89 e também é conhecida por ANSI C.

1990
O ISO (International Organization for Standardization) assume a especificação de linguagem e cria um grupo de trabalho (working group) chamado WG14 [2] para cuidar da especificação do C, que é renomeada para ISO/IEC 9899:1990, informalmente conhecida como C90. Não houve mudanças na linguagem. Esta especificação se mantém por vários anos e define a linguagem C que "todos conhecem".

1999
O WG14 publica a especificação ISO/IEC 9899:1999, informalmente chamada de C99. Esta já traz várias novidades que serão tratadas mais à frente neste artigo.

2011
O WG14 publica a ISO/IEC 9899:2011, conhecida como C11. Este é o padrão atual da linguagem.

Vários recursos cobertos nas especificações mais recentes já eram implementados pelos desenvolvedores de compiladores por sua conta. No caso do GCC, por exemplo, o Projeto GNU criou os padrões GNU89, GNU99 e GNU11 com suas próprias extensões. O Visual C++ da Microsoft também tem várias extensões da linguagem (lembre-se que em geral qualquer compilador C++ compila C, pois a linguagem é a mesma, mas a recíproca não é verdadeira).

Vou mostrar alguns recursos das especificações atuais, utilizando o gcc 4.6 para os exemplos.

A opção -std do gcc define qual especificação usar. Já a -pedantic faz o gcc alertar sobre qualquer construção que seja proibida pela especificação, de forma bem rigorosa.

3. Recursos da C99

- Tipo booleano
Incluindo a stdbool.h, você pode declarar variáveis do tipo bool e usar os estados verdadeiro e falso, assim:

#include <stdbool.h>

void main()
{
   bool var;

   if (var)
      var = false;
}

É fato que isso já era feito usando-se 1 e 0 e na verdade os estados true e false não passam de outros nomes (#define) para estes números, respectivamente. No entanto, a legibilidade do código melhora com o tipo bool.

- Comentários com //

A C89 só permite comentários entre /* e */, o que dá um trabalho extra para comentários de uma linha. Usando a C99, você pode iniciar a linha com //, como no C++, PHP etc. e esta linha será um comentário perfeitamente válido.

- Funções em linha
Quando você chama uma função no código, o compilador normalmente gera uma instrução CALL (do Assembly) para desviar o fluxo de código para o endereço da função. Isso envolve passagem de parâmetros pela pilha de memória (stack), retorno etc. Ou seja, isto gera um ônus de processamento e consumo de memória considerável, principalmente associado ao número e tamanho dos argumentos. Um código que não use funções é mais otimizado, no entanto, é mais difícil de manter. Para resolver isso, você pode dizer que uma função é em linha (inline). Dessa forma, toda vez que a função for chamada, o código dela será copiado para o trecho onde a chamada aconteceu. É como se você copiasse e colasse a função em vários trechos, mas sem precisar fazer isso. ;)

Imagine que temos a função:

inline int soma(int num1, num2)
{
   return num1 + num2;
}

Como ela foi declarada como inline, ao chamá-la por exemplo como:

var = soma(3, 5);

O compilador vai substiuir por:

var = 3 + 5;

- Vetores de tamanho variável

Na C99 você pode declarar um vetor assim:

void funcao(int numero)
{
   char vet[numero];
}

O tamanho do array de char vet depende do parâmetro recebido. Isso era resolvido antes com funções para alocação dinâmica.

Há adições também do tipo complex, para números complexos (e imaginários) e muito mais novidades. No site do WG14 [2] há toda a especificação para download.

Para utilizar a C99 com o gcc, use:

$ gcc -std=c99 fonte.c

4. Recursos da C11

- Novas funções seguras (com bound checking)
As funções tidas como inseguras como strcpy, strcat etc ganharam novas versões sufixadas com "_s", de safe. Basicamente elas recebem um argumento a mais que é o número de caracteres para trabalhar. São elas:

- Multithreading (thread.h)
Apesar de os SOs modernos fornecerem funções de API para multithreading, ao ser inserida oficialmente no C possivelmente vai ajudar na portabilidade de aplicativos que utilizem este recurso. É algo muito bom para a linguagem.

- Estruturas e uniões anônimas
Útil quando estão aninhados, este foi um recurso que senti falta quando programei a primeira versao do pev, que usa muitas estruturas. Se queremos que um campo de uma estrutura seja outra estrutura, antes da C11, é preciso fazer:

struct ponto {
   int x;
   int y;
}

struct reta {
   struct ponto p;
}

Aí para acessar:

struct reta r;

r.p.x = 10;

Com a C11, a estrutura reta pode ser:

struct reta {
   struct ponto;
}

E o acesso:

r.x = 10;

Dizemos então que a estrutura reta possui uma estrutura anônima (sem nome) como campo.

5. Qual especificação usar?

Na hora de usar uma especificação e seguir suas regras, você deve pesar como é o seu projeto, em quais sistemas ele vai rodar, como será distribuído etc. Por exemplo, se é um projeto interno e o compilador que você utiliza suporta a C99, não vejo por que não utilizá-la. Agora, se você vai distribuir seu código, aí já tem que pensar nas versões dos compiladores de quem for baixar. Será que o gcc do usuário do seu software é a última versão? Será que suporta a C11?

No hdump, um dumper hexa/ASCII que fiz para usar no Windows e no Solaris, eu optei por trabalhar com C89 por dois motivos: o código é pequeno e eu precisava levar o fonte para compilar em versões antigas do Solaris, com compiladores que não suportavam a C99, por exemplo.

Já para o pev, um analisador de binários de Windows, eu comecei em ANSI mas já estou atualizando para C99, para deixar o código mais legível e utilizar os novos recursos. Eu distribuo o fonte e binários já compilados, inclusive para Windows, então não tem problema para os usuários.

O importante é ler as especificações e perceber se tem algo que te ajuda/atrapalha no projeto, pensando no escopo, usuários e sistemas alvo. Só a C11 que acho que ainda não dá para distribuir um código em produção, visto que nenhum compilador à suporta completamente. Recomendo usá-la internamente e esperar um pouco antes de liberar código com ela, pois está muito recente.

6. Conclusão

Existem outros recursos interessantes na C11 [3] mas ainda não conheço um compilador que a suporte 100%. Na verdade o gcc também não suporta a C99 por inteiro ainda. Existe até uma página onde você pode acompanhar as mudanças no suporte à C99 [4]. Na versão 4.6, o gcc já estreiou o suporte inicial à C11, sob a opção -std=c1x. A versão 4.7 já vai vir com suporte a mais recursos desta especificação, sob a opção -std=c11. Inslusive cabe um comentário: é muito bom poder contar com um conjunto de compiladores constantemente atualizado e bem feitos como são os do GCC. Essa maravilhosa suíte inclui compiladores para C, C++, Objective-C, Fortran, Java, Ada, e Go. Todos livres. É um espetáculo mesmo. :)

O último rascunho ISO/IEC 9899:201x, divulgado em abril do ano passado está disponível em PDF [5] mas a especificação mesmo, publicada em dezembro, está à venda por cerca de R$ 450,00 na ISO Store [6].

[1] https://groups.google.com/forum/?fromgroups#!forum/exploits-brasil
[2] http://www.open-std.org/JTC1/SC22/WG14/
[3] http://en.wikipedia.org/wiki/C11_%28C_standard_revision%29
[4] http://gcc.gnu.org/gcc-4.6/c99status.html
[5] http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf
[6] http://www.iso.org/iso/iso_catalogue/catalogue_tc/catalogue_detail.htm?csnumber=57853