Linguagem C: História e Padrões
- por Sergio Prado
Em qualquer livro de programação, normalmente as primeiras páginas são voltadas à história da linguagem (e são também as páginas que não lemos ou damos uma "passada bem rápida"). Mas conhecer a origem da linguagem pode nos ajudar a entender um pouco suas características.
C é uma linguagem desenvolvida por engenheiros e para engenheiros. Foi criada em 1972 por Dennis Ritchie para ser usada no desenvolvimento sistema operacional Unix no Bell Labs, que na época pertencia à AT&T (hoje parte da Alcatel-Lucent). C foi derivada da linguagem B, desenvolvida por Ken Thompson, que por sua vez se baseou-se na linguagem BCPL.
Ritchie e Thompson também participaram da equipe de desenvolvimento do UNIX, cujas primeiras versões foram totalmente desenvolvidas em Assembly. Em um certo momento, eles sentiram a necessidade de uma linguagem mais portável para implementar o Unix no PDP-11.
Esta linguagem precisava prover acesso de baixo nível ao hardware (PCU, I/Os e periféricos) e se entender bem com o Assembly, precisava ser portável para que o SO pudesse rodar em diferentes plataformas de hardware, além de ter um bom desempenho e otimizar o uso de memória, principalmente numa época em que estavam começando a surgir as primeiras CPUs, e o preço por byte de memória era caríssimo. Foi portanto destas necessidades que surgiu a linguagem C.
Deste então, 99,99999% das arquiteturas de CPU possuem a implementação de um compilador C. De mainframes, supercomputadores e minicomputadores, a celulares, relógios e torradeiras. É difícil encontrar uma plataforma de hardware sem um compilador C compatível.
O PRIMEIRO PADRÃO
O livro "The C Programming Language", publicado em 1978 por Brian Kernighan e Dennis Ritchie, foi considerado o padrão de facto da linguagem C durante muitos anos, até sair o padrão ANSI C. Este padrão informal também ficou conhecido como "K&R C".
É provavelmente o livro de C que mais se vendeu até hoje, por vários motivos. Dentre eles, foi o primeiro livro de C disponível de forma mais abrangente, um dos autores do livro é o próprio criador da linguagem, é escrito de forma clara e possui poucas páginas, comparado à outras "bíblias" que vemos por aí. Todo desenvolvedor C deveria ter sua cópia.
A segunda edição do livro foi lançada em 1988 e atualizada para ficar de acordo com o padrão ANSI C.
OS PADRÕES ANSI C E ISO C
Durante a década de 80 "choveram" implementações de compiladores C, já que naquela época a semântica da linguagem estava aberta à interpretações. Surgiu então a necessidade de um padrão formal da linguagem.
Este padrão foi desenvolvido pelo ANSI (American National Standards Institute), que formou um comitê e ratificou o padrão em 1989 como ANSI X3.159-1989 "Programming Language C", também conhecido com ANSI C ou C89.
Um ano depois o padrão foi adotado pela ISO (International Organization for Standardization) como ISO/IEC 9899:1990, também chamado de C90. Na prática, C89 (ANSI C) e C90 (ISO C) referem-se ao mesmo padrão.
A partir de 1990, a ISO ficou responsável pela evolução do padrão. O endereço oficial do grupo é este aqui, e um draft do padrão pode ser visto aqui.
O que eles fizeram foi acrescentar algumas novas características ao padrão anterior K&R C.
As principais mudanças foram a inclusão de protótipos de função, a padronização das bibliotecas e a adição de algumas palavras-chave como enum, const, volatile, signed, void (isso mesmo, estas palavras-chave não existiam formalmente antes do padrão ANSI C).
A maioria do código compilado em K&R C, compila em ANSI C, com algumas exceções. Para tratar estas exceções, foi criado o #define __STDC__, que deveria ser implementado por padrão em todos os compiladores ANSI C. Veja no exemplo abaixo o protótipo da função soma() nos padrões ANSI C e K&R C.
1 2 3 4 5 |
#if __STDC__ extern int soma(int a, int b); #else extern int soma(); #endif |
Todos os compiladores modernos suportam ANSI C. E ter um programa compatível com ANSI C significa que este será provavelmente portável para qualquer arquitetura de hardware que possua um compilador ANSI C disponível.
O PADRÃO C99
Na década de 90 a ISO corrigiu alguns detalhes e publicou em 1999 o ISO/IEC 9899:1999, que ficou conhecido como C99. Um draft do padrão pode ser visto aqui.
Dentre as alterações, podemos destacar a inclusão de funções inline, variáveis "long long", suporte para comentários de uma linha, além de não assumir int quando não é especificado o retorno da função.
Por exemplo, o código abaixo esta de acordo com o padrão C89, mas não compila de acordo com o padrão C99:
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 |
#include "stdio.h" /* Retorna indice do maior elemento do array */ maior(long buf[], int qtd) { long m = 0; int i, ind = -1; for (i = 0; i < qtd; i++) { if (buf[i] > m) { m = buf[i]; ind = i; } } return(ind); } main(void) { int m = -1; long buf[] = { 1234, 3456, 7345, 5897 }; if ((m = maior(buf, 4)) >= 0) printf("Maior elemento buf[%d]=%ld\n", m, buf[m]); else printf("Maior elemento nao encontrado\n", buf[m]); return 0; } |
Para compilar de acordo com o padrão C99, algumas linhas precisam ser alteradas:
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 |
#include "stdio.h" int maior(long long buf[], int qtd); // Retorna indice do maior elemento do array inline int maior(long long buf[], int qtd) { long long m = 0; int i, ind = -1; for (i = 0; i < qtd; i++) { if (buf[i] > m) { m = buf[i]; ind = i; } } return(ind); } int main(void) { int m = -1; long long buf[] = { 1234, 3456, 7345, 5897 }; if ((m = maior(buf, 4)) >= 0) printf("Maior elemento buf[%d]=%lld\n", m, buf[m]); else printf("Maior elemento nao encontrado\n", buf[m]); return 0; } |
Para compilar, foi necessário alterar os retornos de função, declarando explicitamente "int".
Além disso, fizemos algumas alterações permitidas no padrão C99. Alteramos todas as variáveis "long" para "long long", usamos comentários de uma linha com "//" e declaramos a função maior() como "inline".
Com estas alterações o código ficou compatível com o padrão C99, mas perdeu a compatibilidade como padrão ANSI C (C89).
O FUTURO PADRÃO C1X
C1X é um nome não-oficial para um trabalho que começou em 2007, com o objetivo de revisar e definir um novo padrão para a linguagem C. Algumas das alterações propostas podem ser encontradas no site da Wikipedia.
Algumas adições me pareceram bem interessantes: uso das palavras-chave _Generic para declarar uma função que pode receber diferentes tipos de dados e _Atomic para definir que o acesso à determinada variável deve ser atômico; a substituição da função gets() pela gets_s(), cuja implementação é bem mais segura; implementação de estruturas e unions anônimas; etc.
Claro que o padrão atual (C99) é bem maduro, e você deveria usá-lo em todos os seus projetos. O novo padrão pretende adicionar novas funcionalidades para melhorar a vida do desenvolvedor. Eu particularmente gostaria de ver um mecanismo de tratamento de erros ao estilo try-catch-throw do C++. De qualquer forma, deve levar ainda um tempo para o padrão sair, e mais um tempo para os compiladores implementarem.
Nenhum dos documentos oficiais dos padrões esta disponível gratuitamente na internet para download. Mas podemos baixar versões iniciais da especificação, ATAs de reunião e adendos. Esta wiki tem algumas informações interessantes com links para documentos relacionados aos padrões.
Um abraço,
Sergio Prado