Artigo do tipo Exemplos Práticos
Recursos especiais neste artigo:
Contém nota Quickupdate.
Por: Gabrielle Karine Canalle e Everton Cimbra de Araújo
C/C++ e Java

Neste artigo serão abordadas as linguagens de programação C/C++ e Java, apresentando um histórico e suas principais características. Será realizada também uma análise sobre as semelhanças e diferenças entre essas linguagens, e elaborada uma comparação entre as principais estruturas de código, como estruturas de repetição, estruturas de seleção, definição de classes e utilização de bibliotecas.


Em que situação o tema é útil

Este tema é útil para instruir o leitor em relação às principais semelhanças e diferenças existentes nas sintaxes das linguagens C/C++ e Java, e onde cada uma dessas linguagens tem seu uso mais indicado.

Linguagens de programação são responsáveis por transmitir instruções para o computador, para que certas tarefas possam ser realizadas. Os computadores entendem apenas sequências de números binários, e para um ser humano seria difícil decorar inúmeras combinações desses números como representações de instruções. Para que a transmissão de instruções para o computador ocorra de maneira mais fácil, foram criadas as linguagens de programação.

Desde o surgimento da primeira linguagem de programação (Assembly), na década de 50, várias outras surgiram. A partir de então, uma grande evolução, no que diz respeito a estas linguagens, vem ocorrendo. Estas evoluções têm permitido que mais problemas sejam resolvidos, alcançando maior precisão, agilidade e qualidade.

Quando uma nova linguagem surge, é muito comum que seus criadores tenham como base linguagens já conhecidas e que tiveram sucesso entre programadores. Com o C++ foi assim. Seu criador utilizou como base a linguagem C, implementando algumas melhorias. Isso também aconteceu com o criador do Java, que fez uso de várias linguagens como base, sendo uma das principais o C++.

Devido a isso, C/C++ e Java possuem muitas semelhanças entre si, sendo fácil para um programador que está adaptado com uma dessas linguagens, aprender facilmente a outra. Por outro lado, mesmo com essa derivação na criação das linguagens, devido aos aprimoramentos realizados sobre elas, existem muitas diferenças, e algumas muito significantes, sendo até o fator que impulsiona o desenvolvedor a escolher uma linguagem ao invés da outra.

Com base nisso, este artigo aborda as principais semelhanças e diferenças entre as linguagens C/C++ e Java.

Histórico das Linguagens C/C++ e Java

A linguagem C foi criada em 1972 por Dennis Ritchie, no AT&T Bell Labs, com o intuito de implementar o sistema operacional Unix, escrito originalmente em Assembly. Em 1973, após algumas melhorias na linguagem, o sistema operacional Unix foi reescrito na linguagem C.

A linguagem C++ teve seu início como uma extensão do C, visando suportar o paradigma da programação orientada a objetos. Esta linguagem foi desenvolvida em 1983, por Bjarne Stroustrup, também no Bell Labs. O C++ é uma linguagem de nível médio, ou seja, possui comandos simples das linguagens de alto nível e comandos complexos das linguagens de baixo nível. É, também, multi-paradigma, pois suporta mais de um estilo de programação, e é considerada uma linguagem de uso geral.

Outras linguagens além de C também serviram de inspiração para a criação do C++, como ALGOL 68, Ada, CLU e ML, portanto, não se pode considerar que C++ teve como base apenas o C.

Com o passar dos anos estas duas linguagens foram evoluindo de maneira independente, e as diferenças entre elas aumentaram. Mesmo assim, a maioria dos códigos escritos em C pode ser compilada em C++. Contudo, existem algumas diferenças sintáticas e semânticas que tornam trechos de códigos válidos em C, inválidos em C++. Ou ainda, trechos de códigos iguais com comportamentos diferentes em cada linguagem. Por exemplo, para alocar e desalocar memória em C, são utilizadas as funções malloc() e free(). Estas funções são inválidas em C++, pois foram substituídas pelas funções new() e delete().

Para o desenvolvimento nestas linguagens, em C, podem-se utilizar ambientes (IDEs) como o Code::Blocks, GNU Compiler Collection, Make, dentre outros. Já para o desenvolvimento em C++, tem-se: G++, Ultimate++, Microsoft Visual C++, Eclipse, dentre outros. O Bloodshed Dev C++ suporta desenvolvimento tanto em C quanto em C++.

Devido aos pontos negativos existentes em C, como, por exemplo, não ser orientada a objetos, não possuir sobrecarga de operadores e funções e não possuir tipos complexos de variáveis, dentre outros, várias linguagens surgiram com o intuito de ser sua sucessora, trazendo diversas melhorias. No entanto, dentre elas, a que obteve maior sucesso foi a linguagem C++, que serviu, inclusive, de inspiração para outras linguagens, como a IDL de CORBA, C# e Java.

A plataforma Java foi lançada em maio de 1995, e tudo aconteceu muito rápido. Seu número de usuários cresceu rapidamente, devido principalmente à popularização da web. Desta maneira, grandes fornecedores de tecnologia, como a IBM, anunciaram suporte ao Java. Em 2003, Java atingiu a marca de 4 milhões de desenvolvedores em todo o mundo. Em 13 de Novembro de 2006, a Sun disponibilizou a maior parte do Java como software livre sob os termos da GNU (General Public License). Em 8 de Maio de 2007, a Sun disponibilizou o restante do código Java como software de código aberto.

Ainda em relação ao Java, existem recursos para o desenvolvimento de aplicativos para uma grande variedade de ambientes. Dentre os recursos, há também a JVM (Máquina virtual Java – Nota do DevMan 1), que é responsável por executar os programas Java. A plataforma é composta ainda por ferramentas de desenvolvimento (como compiladores e depuradores), bibliotecas de desenvolvimento para desktop, dispositivos móveis, web, ambientes distribuídos, dentre outros. Além destas ferramentas, a plataforma conta com a linguagem de programação Java, orientada a objetos e que foi desenvolvida na década de 90, por uma equipe de programadores chefiada por James Gosling, na Sun Microsystems.

Essa linguagem tornou-se popular principalmente por ser uma plataforma livre, que foi e é muito utilizada para implementar aplicativos para a web e ambientes coorporativos.

Dentre os vários IDEs disponíveis para o desenvolvimento em Java, destacam-se três: Eclipse, NetBeans e JDeveloper.

Nota do DevMan 1

JVM: A JVM, ou Máquina Virtual Java, é responsável por carregar e executar aplicativos Java, convertendo os bytecodes do programa em código nativo, que é o código que o Sistema Operacional entende. Devido à JVM, os programas escritos em Java podem funcionar em qualquer plataforma que possua uma versão da mesma, sendo assim, independentes de plataforma.

Principais diferenças entre C/C++ e Java

Como já informado, entre as linguagens C/C++ e Java existem muitas semelhanças, mas também existem algumas diferenças, que serão apresentadas a seguir, como, por exemplo, o tratamento de exceções, o uso de memória, a compilação do código e a portabilidade.

Tratamento de exceções

A confiabilidade é um requisito muito importante para qualquer linguagem de programação, pois uma linguagem confiável possibilita criar programas mais seguros e bem aceitos pelos usuários. A confiabilidade nas linguagens é influenciada por vários fatores como, por exemplo, a verificação de tipos, o tratamento de exceções, a legibilidade e a facilidade de escrita. Quando se trata de confiabilidade, C e C++ possuem várias características que acabam propiciando erros de programação, como a manipulação direta de endereços de memória utilizando ponteiros. Em Java, por outro lado, não se tem acesso direto ao endereço de memória, por isso, não é permitida a manipulação desse endereço. Sendo assim, a linguagem Java se torna mais confiável.

Quando uma linguagem possui recursos para tratar exceções, evitando que o programa seja finalizado inesperadamente, sem saber ao certo o que ocorreu, essa linguagem se torna mais confiável para o usuário também. Esta confiança se dá pelo fato da linguagem oferecer ao programador recursos que podem ser utilizados para prever a possibilidade de ocorrência de determinadas exceções. Com esta possibilidade, o programador já implementa um código que possa tratar estas exceções quando ocorrerem.

A linguagem C não possui recursos para o tratamento de exceções, ficando a cargo de o programador desenvolver uma maneira ou técnica para tratá-las. O C++, por sua vez, possui embutido um mecanismo de tratamento de exceções, composto pelo try() e pelo catch(). Em Java, toda exceção é um objeto. A superclasse de todas as exceções é a java.lang.Throwable. Todos os objetos dessa classe ou de suas subclasses podem ser gerados ou capturados por meio do tratamento de exceções. As duas subclasses de java.lang.Throwable são: java.lang.Exception e java.lang.Error (ver Figura 1). A primeira é utilizada em situações nas quais é possível realizar um tratamento que permita prosseguir com o processamento como, por exemplo, quando um número é informado pelo usuário e este não pode ser negativo, ou, em outro caso, quando o usuário não pode digitar letras, pois é um argumento inválido. Já a segunda classe é utilizada em situações nas quais a aplicação não deve tentar tratar; situações que não deveriam ocorrer, que acontecem geralmente por problemas na JVM como, por exemplo, falta de memória.

As classes já definidas em Java e mais utilizadas para o tratamento de exceções são:

· java.lang.NullPointerException- trata erros que acontecem durante a execução do programa como, por exemplo, chamar um método de instância de um objeto null;

· java.lang.IllegalArgumentException – trata de erros referentes a um argumento inválido fornecido para uma chamada de método;

· java.lang.NumberFormatException - indica que tentou-se a conversão de uma string para um formato numérico, mas seu conteúdo não representava adequadamente um número para aquele formato. É uma subclasse de java.lang.IllegalArgumentException;

· java.lang.ClassNotFoundException - indica que a aplicação tentou carregar uma classe mas não foi possível encontrá-la.


image

Figura 1. Hierarquia de exceções.

A detecção e controle de exceções em Java também é feito por meio da estrutura try()/catch(). A Listagem 1 mostra um exemplo de uso deste recurso.

Listagem 1. Exemplo de tratamento de exceção utilizando try()/catch().

try{
   string exemplo = null;
}catch(NullPointerException e){
    System.out.println("A string está nula");
}

Quando um método pode lançar uma exceção, é utilizado o throws para informar aos outros métodos que isso poderá acontecer. A exceção que será lançada é a que está descrita logo após o throws como no código a seguir:

public void teste() throws NumberFormatException{}

Na assinatura do método, está se informando com o throws que ele pode lançar a exceção java.lang.NumberFormatException.

Uso de memória

Em relação ao uso de memória, os programas escritos em Java liberam automaticamente a memória alocada e não utilizada, pois fazem uso do Garbage Collector (GC – Nota do DevMan 2), que de tempos em tempos percorre a memória a procura de objetos que não possuem mais uma referência válida. Esses objetos são considerados como “lixo”, e são coletados para liberação da memória ocupada. Para que o GC faça este trabalho de gerenciamento de memória, não é necessário nenhum código, pois esse processo é automático. No entanto, existe o método System.gc(), que o “força” a trabalhar.

O uso desse método é como se fosse um aviso da aplicação de que, naquele momento, não teria nenhum problema para ela que o GC varresse a memória em busca de objetos que não estejam mais sendo utilizados.

Em C, essa atividade depende exclusivamente do programador. Para a alocação de memória é utilizada a função malloc(), e para desalocar, a função free(). Um exemplo dessas funções está representado no código a seguir:

char *str;

str = malloc(100);

free (str);

Em C++ o processo também é manual, e essas duas funções foram substituídas por new() e delete(). O código a seguir mostra um exemplo de uso delas:

int *ex;

ex = new int(100);

delete ex;


Nota do DevMan 2

Garbage Collector: O GC é um mecanismo de gerenciamento automático de memória. Ele libera blocos de memória que não estão sendo utilizados pela aplicação. Esta limpeza acontece de tempos em tempos e libera os blocos que não possuem mais nenhuma referência válida.


Ponteiros em Java existem ou não? Existem muitos autores que defendem a ideia de que ponteiros em Java existem sim, e são os objetos, uma vez que um ponteiro nada mais é do que uma referência para um endereço de memória. Para outros autores, o fato de não se ter acesso direto ao endereço de memória, sendo impossível manipulá-lo diretamente, caracteriza que a linguagem Java não possui ponteiros, e sim referências.

Compilação

Sobre o processo de compilação, todas as linguagens necessitam de um compilador. Porém, algumas delas, além do compilador, precisam também de um pré-processador (Nota do DevMan 3), como é o caso das linguagens C/C++.

Nota do DevMan 3

Pré-processador: O pré-processador examina um programa fonte e executa algumas modificações sobre ele, como, por exemplo, a inclusão de arquivos, a substituição de macros, dentre outras possibilidades. Estas modificações são realizadas com base nas diretivas de compilação que, nas linguagens C e C++, são iniciadas pelo caractere #.

Compilador:De maneira sucinta, um compilador traduz um programa escrito em determinada linguagem de programação, para a linguagem da máquina em que este programa será executado. Para isso, existem vários passos onde cada compilador possui suas particularidades.

O pré-processador examina o programa fonte e executa certas modificações sobre ele de acordo com as diretivas de compilação. As diretivas de compilação são comandos que não são compilados, sendo enviados ao pré-processador.

Nas linguagens C/C++ existem várias diretivas de compilação, cada uma com sua função, mas todas se iniciam com o caractere #. As principais diretivas são: #include, #define, #undef, #ifdef, #ifndef, #if, #else, #elif, #endif. Dentre elas, a mais utilizada é a #include, que é o responsável por informar ao compilador que ele deve incluir um arquivo externo no processo de compilação do arquivo onde esta diretiva se encontra. Essas diretivas são utilizadas nos cabeçalhos dos programas escritos em C/C++.

Programas Java não necessitam de um pré-processador. Portanto, não existem diretivas nesta linguagem. Em vez de cabeçalhos, os arquivos em Java possuem as declarações dos pacotes e imports. Além disso, Java provê um mapeamento entre os nomes dos pacotes/classes e os diretórios onde se localizam, e quando o compilador precisa ler um determinado arquivo de classe, ele sabe exatamente onde encontrá-lo. Por exemplo, em uma aplicação de teste, ao nomear um pacote como br.edu.utfpr.md.teste, no disco ele se divide em várias subpastas, como mostra a Figura 2.

image

Figura 2. Mapeamento de pacotes em Java.

Portabilidade

Para que programas feitos em uma determinada linguagem se comportem da mesma maneira independentemente da plataforma onde serão executados, é necessário que a linguagem de programação utilizada para a implementação desse programa tenha portabilidade. Desse modo, o programa pode ser utilizado em vários ambientes diferentes.

Quando se fala de portabilidade, pode-se dizer que Java é uma linguagem portável, já que após o processo de compilação de uma classe, ela pode ser invocada em qualquer plataforma que dê suporte à linguagem. Isto se deve ao fato de o código-fonte Java ser compilado para bytecode, que é um código interpretado pela Máquina Virtual Java (JVM). Isto faz com que os programas Java sejam independentes de plataforma, ao contrário de C/C++, que são compilados diretamente para código nativo. Esta característica de C/C++ faz com que essas linguagens sejam dependentes de plataforma. Deste modo, para que programas implementados nelas possam ser executados em outras plataformas, é preciso realizar uma nova compilação para cada uma destas plataformas.

Semelhanças entre C/C++ e Java

A linguagem Java, em sua criação, teve como base a linguagem C++. Nesta nova linguagem, buscou-se o aperfeiçoamento dos pontos que continham desvantagens em sua precursora, criando assim uma linguagem mais simples e voltada para atender as necessidades do desenvolvimento de aplicações em ambientes distribuídos e heterogêneos. Devido ao Java ter se baseado em C++, elas acabaram se tornando muito parecidas, sendo que as principais semelhanças são verificadas em suas sintaxes.

Um exemplo de semelhança na sintaxe das linguagens está na escrita dos comentários. Para comentar um código, em C/C++ ou em Java, utiliza-se // (para uma linha) ou /**/ (para um bloco de código). Outro exemplo está na definição de classes, que tanto em C++ quanto em Java é realizado por class NomeDaClasse.

Como um exemplo de aprimoramento do C++ para o Java, pode-se citar os dados do tipo boolean. Em C++, define-se um campo booleano com o tipo bool, e este recebe os valores 0 ou 1. Em Java, este tipo de dado passou a se chamar boolean, e pode receber os valores true ou false. Esta mudança tornou o entendimento da sintaxe mais simples, já que o desenvolvedor tem o retorno em forma de verdadeiro ou falso, ao invés de números.

Comparação entre as sintaxes

As linguagens de programação são compostas por um conjunto de regras relacionadas à sua semântica e sintaxe. A semântica é a descrição do significado das instruções de uma linguagem, e a sintaxe é a forma como são escritas as instruções de uma linguagem. Devido às principais semelhanças e diferenças entre as linguagens C/C++ e Java se encontrarem em suas sintaxes, serão comparados os principais recursos das linguagens, tais como: estruturas de seleção e repetição, uso de bibliotecas da API, entrada e saída de dados, e escrita e leitura de arquivos.

Estruturas de seleção

As estruturas de seleção possibilitam ao programa uma escolha, ou seja, que um caminho seja selecionado de acordo com a satisfação ou não de determinadas condições. Para isso, existem algumas diferentes estruturas de seleção, dentre elas o if...else e o switch. A sintaxe das estruturas de seleção if e switch são idênticas em Java e em C/C++. A estrutura básica do if, na linguagem C/C++, que é a estrutura de seleção mais comum, é representada pelo código:

if (<condição>){

<Instruções>;

}

Nesta estrutura, a condição é analisada a cada passagem por ela. Se a condição for satisfeita, o bloco de código referente ao if será executado. Se não for satisfeita, as instruções que estão logo após o fechamento do bloco serão executadas. Note que estas instruções, após o bloco, serão executadas também caso a condição tenha sido satisfeita. Um bloco de código refere-se a instruções que se localizam entre chaves.

Além da estrutura de seleção básica do if, existem algumas variações. Uma delas, if...else, está representada na Listagem 2. Neste exemplo, caso a condição avaliada seja verdadeira, as instruções pertencentes ao bloco if serão executadas. Se a condição avaliada for falsa, serão executadas as instruções pertencentes ao bloco else.

Listagem 2. Variação da estrutura de seleção if – if...else.

if(<condição>){
  <Instruções>;
}else{
  <Instruções>;
}

Outra variação do if é o if...else if, que está representada na Listagem 3. Nesta estrutura, caso a condição avaliada no if for verdadeira, serão executadas as instruções referentes ao bloco if. Caso a condição seja falsa, será verificada automaticamente a condição do else if. Se a condição do else if for verdadeira, as instruções pertencentes ao bloco else if serão executadas, caso contrário, as instruções referentes ao bloco else que serão. Não há limite para a quantidade de else if a ser utilizada, porém, é permitida a existência de um único else.

Listagem 3. Variação da estrutura de seleção if – if...else if...else.

if(<condição>) {
  <Instruções>;
} else if(<condição>) {
  <Instruções>;
} else {
  <Instruções>;
}

Existe também a estrutura de seleção múltipla switch. Esta estrutura é utilizada para o caso de várias condições, praticamente da mesma maneira que várias condições podem ser avaliadas pela estrutura if else if. O switch testa o valor de uma variável contra uma lista de constantes inteiras ou caracteres. Quando o valor é igual, as instruções associadas àquela constante são executadas. Sua estrutura está representada na Listagem 4. O break realiza a saída imediata do switch e, caso ele não seja utilizado, todos os cases que estão abaixo do case que foi executado são assumidos como verdadeiros, e serão executados. As instruções que estão dentro de default serão executadas caso nenhuma das outras opções seja escolhida, de modo semelhante ao else.

Listagem 4. Estrutura de seleção múltipla switch.

switch(<expressão>) {
  case <opção1>:
    <comandos>;
    break;
  case <opção2>:
    <comandos>;
    break;
  default:
    <comandos>;
}

Tanto o switch quanto o if (e suas variações) são estruturas de seleção, porém, existem algumas diferenças entre eles. Quando se utiliza o if, uma expressão booleana é comparada, tendo duas alternativas: executar um bloco de código se a expressão for verdadeira, ou outro bloco de código se a expressão for falsa. Existe também o aninhamento de if, que permite a utilização de várias estruturas if, uma dentro da outra. Há também a possibilidade de se utilizar o if em conjunto com o else if, com condições estando no mesmo nível, e não um if dentro de outro. Para essa situação, o switch é muito mais vantajoso, pois torna o código mais claro e legível para o desenvolvedor. Como o switch é mais utilizado quando se tem muitas alternativas, é conhecido como estrutura de seleção de múltipla escolha. Outra diferença é que o switch não aceita expressões, apenas constantes.

Existe ainda o operador ternário, que é muito semelhante ao if...else. A única mudança significativa será na economia de código. O código a seguir mostra um exemplo de uso do operador ternário:

cont1 <= cont2 ? cont1++ : cont2--;

//sintaxe: <condição> ? <operação1> : <operação2>;

Se a condição for verdadeira, a operação 1 será executada, se for falsa, a operação 2 será executada. O operador ternário tem sua sintaxe idêntica tanto em C/C++ quanto em Java.

Estruturas de repetição

As estruturas de repetição são utilizadas para executar um bloco de instruções por várias vezes, até que a condição de parada seja atingida. Em C/C++ existem três tipos de estruturas de repetição:

· while – utilizado quando não se sabe quantas vezes um bloco de instruções deve ser executado;

· do..while – utilizado quando não se sabe quantas vezes um bloco de instruções deve ser executado. Nesta estrutura o bloco é executado, obrigatoriamente, pelo menos uma vez;

· for – utilizado quando se sabe o numero de vezes que um bloco de instruções deve ser executado. Faz uso de uma variável para controlar os loops.

No while, se testa uma condição, e caso ela seja verdadeira, as instruções que estão no bloco da estrutura serão executadas, voltando ao teste da condição após isso. Caso a condição for falsa, as instruções que estão fora do laço de repetição, logo abaixo dele, serão executadas. Estas instruções, fora do bloco, serão executadas também quando a condição de parada da repetição ocorrer. O código a seguir apresenta a estrutura de repetição do comando while:

while(<condição>) {

<Instruções>;

}

O do while se assemelha muito ao while. A diferença é que a condição será testada apenas no final do laço de repetição. Isto faz com que as instruções pertencentes ao bloco da estrutura sejam executadas ao menos uma vez. O código a seguir mostra a estrutura do comando do while:

do {

<Instruções>;

} while(<condição>);

A estrutura de repetição for é geralmente empregada quando se sabe o número de vezes que o conjunto de instruções, pertencentes ao bloco, deve ser repetido. Deste modo, é executado o bloco de instruções até que a condição de parada determinada no início do for seja satisfeita. O código a seguir mostra a estrutura do for:

for(<variável de inicialização>; <condição>; <incremento>)

O Java também implementa essas três estruturas de repetição, e as sintaxes delas são bastante parecidas. Porém, com o Java, surgiram novas maneiras de se trabalhar com as estruturas de repetição. Por exemplo, existe um padrão de projeto (Nota do DevMan 4) chamado Iterator que é utilizado para percorrer coleções. Ele provê uma maneira de acessar os elementos de uma coleção sequencialmente, sem expor sua representação interna. O Iterator() é utilizado para obter um objeto Iterator, para que essa varredura em uma coleção possa ocorrer. Os métodos da classe Iterator mais utilizados são: hasNext(), que indica se existem mais elementos na coleção; next(), que obtém o próximo elemento da coleção; e remove(), que retira um elemento da coleção.


Nota do DevMan 4

Padrões de Projeto: Descrevem soluções reutilizáveis para problemas existentes em desenvolvimento de sistemas. São modelos para resolução de problemas. Um padrão possui quatro elementos essenciais: o nome do padrão, o problema (que descreve em que situação se deve aplicar o padrão), a solução (que descreve os elementos que compõem o padrão), e as consequências, que são as análises e resultados finais das vantagens e desvantagens da aplicação do padrão.

O padrão Iterator provê uma forma de sequencialmente acessar objetos de uma coleção, sem expor sua implementação ou estrutura interna. Para isso, propõe a centralização de todas as regras de navegação da coleção em uma classe, de forma que tais regras possam variar sem que seja necessário expor os detalhes de nossas coleções.

Java também oferece suporte à estrutura de repetição foreach, utilizada para percorrer arrays ou coleções. Ela foi adicionada ao Java para tornar o processo de varredura em uma coleção mais simples, já que não utiliza um índice contador e nem necessita de uma expressão de condição para ser executado. O foreach funciona para todos os tipos de listas, mas não é totalmente flexível. É utilizado apenas para exibir os elementos sequencialmente, do início ao fim da lista, sem adicionar ou remover elementos. Devido ao foreach permitir apenas recuperar os elementos de maneira sequencial, quando houver a necessidade de obter o valor do índice contador para realizar alguma operação, é indicado o uso do for. O código a seguir mostra um exemplo da estrutura do foreach:

for(<tipo> <variável> : <array/coleção>){

<Instruções>;

}

Operadores relacionais e lógicos

Para formar a expressão condicional que é avaliada nas estruturas de seleção e repetição, utilizam-se operadores relacionais. Os operadores relacionais permitem a criação de comparações entre os elementos, como a comparação de igualdade/desigualdade, e a comparação de maior/menor, devolvendo sempre um valor booleano (true/false). Os operadores relacionais mais utilizados são:

· > : Maior que;

· < : Menor que;

· == : Iguais;

· != : Diferentes;

· >= : Maior ou igual que;

· <= : Menor ou igual que;

· = : Atribuição.

Em algumas situações, uma expressão pode depender de mais que uma simples condição, e para isso existem os operadores lógicos. Estes operadores são utilizados para descrever situações lógicas nas quais os operadores relacionais ou aritméticos não conseguem representar. Os mais utilizados são:

· && (E): retorna true se ambas as expressões forem verdadeiras;

· || (OU): retorna false se ambas as expressões forem falsas.

Uso de bibliotecas da API

Quando se está implementando um software, existe a possibilidade de se utilizar um conjunto de classes, métodos e funções que resolvam determinados problemas sem a necessidade de criar alguma solução que já tenha sido implementada. A esse conjunto de códigos prontos dá-se o nome de biblioteca.

O uso de bibliotecas em um programa é muito importante, pois economiza tempo e obtém-se ganho em produtividade. Nestas bibliotecas existem diversas funções prontas e já testadas, e que podem ser utilizadas por qualquer programador. Para isto, basta importar a biblioteca correspondente ao recurso desejado, sendo desnecessário criar essa função novamente.

Em C, para utilizar uma função ou estrutura de uma biblioteca, basta solicitar ao pré-processador que a inclua no código. Para que isso aconteça, o pré-processador necessita de um “mapa”, para que possa localizar a função desejada. Esse mapa são os arquivos de cabeçalho com extensão .h, que vem logo após a diretiva #include, no início do programa (código fonte). O h da extensão vem da palavra inglesa HEADER, que significa cabeçalho.

Quando o arquivo de cabeçalho vem cercado pelos sinais de maior e menor (<>), o pré-processador procura o arquivo não apenas nos diretórios do projeto em que ele se encontra, mas também em diretórios adicionais, normalmente definidos por uma variável de ambiente chamada INCLUDE. Porém, quando o arquivo de cabeçalho é cercado por aspas duplas (“”), o pré-processador entende que esse cabeçalho faz parte do projeto, e o procura dentro do diretório em que o projeto está sendo compilado, não utilizando os caminhos dos diretórios adicionais da variável de ambiente INCLUDE. O código a seguir mostra as duas formas de incluir uma biblioteca em C:

#include <stdio.h>

#include <stdlib.h>

#include “teste.h”

Em C++, o processo para incluir bibliotecas é diferente. Continua-se utilizando o #include, mas o h não é mais adotado como extensão para os nomes das mesmas. Depois de importá-las é necessário especificar qual será o namespace utilizado. As bibliotecas de C são diferentes das de C++, mas a maioria dos compiladores C++ suportam as de C também. O código a seguir mostra como fica a inclusão de bibliotecas em C++:

#include <iostream>

using namespace std;

Neste exemplo, a instrução using namespace std foi utilizada para mostrar ao compilador que se quer fazer uso do namespace std para ter acesso às funções localizadas nele. Todas as funções que fazem parte da biblioteca std (de standard, que em inglês significa padrão), estão localizadas dentro de um namespace. Um namespace é um espaço de nomes que armazena funções, classes, estruturas e constantes, e serve para evitar a duplicação de nomes. Por exemplo, podem existir duas funções com o mesmo nome, desde que estejam em namespaces diferentes. Esta instrução diz que essa biblioteca será utilizada durante todo o programa.

Em Java, não é necessário o uso de arquivos header, e a diretiva #include não existe. Porém, existe a instrução import, que se assemelha ao #include. O import serve para importar um pacote ou classe de uma biblioteca em tempo de compilação, e os termos que seguem o import são os pacotes ou classes que serão utilizados. Existem duas maneiras de realizar um import, de forma explícita e de forma implícita.

Na forma implícita, como em java.util.*, o asterisco quer dizer que todos os recursos daquele pacote estarão disponíveis na classe. Na forma explícita, como em java.util.ArrayList, indica-se que a classe só utilizará um recurso específico do pacote java.util, que no caso é a classe ArrayList. O código a seguir traz as duas formas de import em Java:

import java.util.ArrayList;

import java.util.*;

Entrada e saída de dados

As operações de entrada e saída de dados são de extrema importância para qualquer aplicação. A função de saída induz o usuário a executar uma ação ou fornece a ele alguma informação. Já a função de entrada armazena um dado, normalmente informado pelo usuário.

Esses recursos se diferem em C e C++. Em C, as principais funções de entrada e saída se encontram na biblioteca STDIO.H. A entrada de dados geralmente é controlada pela função scanf(), e a saída de dados é controlada pela função printf(). Existem ainda outras funções de entrada e saída, como puts() e gets(), putchar() e getchar(). A Listagem 5 mostra um exemplo de uso das funções scanf() e printf().

Listagem 5. Exemplo de uso das funções printf() e scanf().

#include <stdio.h>

int main(void){
  int a, b, r;
  printf("Digite dois numeros para serem somados:");
  scanf("$d %d", &a &b);
  r = a + b;
  printf("A soma de %d é igual a %d", a, b, r);
  return 0;
}

Em C++ as funções de entrada e saída de dados são encontradas na biblioteca IOSTREAM. A entrada de dados é controlada geralmente pela função cin(), e a saída pela função cout(). A Listagem 6 mostra um exemplo de uso das funções cin() e cout().

Listagem 6. Exemplo de uso das funções cin() e cout().

#include <iostream>
using namespace std;

int main(void){
  int a, b, r;
  cout <<"Digite o primeiro numero que sera somado:";
  cin >> a;
  cin.ignore ();
  cout <<"Digite o segundo numero que sera somado:";
  cin >> b;
  cin.ignore ();
  r = a + b;
  cout <<"A soma de "<<a<<" mais "<<b<<" e igual a"<<r;
  return 0;
}

Em Java, para a entrada de dados, podem-se utilizar os objetos da classe BufferedReader. Esses objetos possuem um método readLine() que permite a leitura de uma linha de texto e a retorna como uma String. A Listagem 7 mostra um exemplo de uso da classe BufferedReader.

Listagem 7. Exemplo de uso do BufferedReader para entrada de dados.

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class Teste {

  public static void main(String[] args) {
    BufferedReader bf = new BufferedReader (new
      InputStreamReader(System.in));
    try {
      System.out.print("Digite uma frase:");
      String frase = bf.readLine();
      System.out.println(frase);
    } catch(IOException e) {
      System.out.println("Erro")
    }
  }

}

Ainda para entrada de dados, pode-se utilizar também a classe java.util.Scanner. A Listagem 8 mostra um exemplo de uso da classe Scanner.

Listagem 8. Exemplo de uso da classe Scanner.

import java.util.Scanner;

public class Teste{

  public static void main(String[] args){
    Scanner in = new Scanner(System.in);
    System.out.print("Digite seu nome: ");
    String nome = in.nextLine();
    System.out.print("Digite sua idade: ");
    int idade = in.nextInt();
    System.out.println("Seu nome é" +nome+)
      ", e sua idade e" +idade+ "anos");
  }

}

Além das classes para manipulação de entrada e saída de dados, a linguagem Java oferece classes para a construção de interfaces gráficas que possibilitam ao usuário uma interface mais amigável, permitindo uma melhor interação entre o usuário e o programa. Essas classes disponibilizam controles visuais como menus, janelas, botões e painéis que podem ser utilizados em qualquer aplicação. A classe javax.swing.JOptionPane, por exemplo, pode ser utilizada para entrada e saída de dados. A Listagem 9 mostra um exemplo de uso desta classe.

Listagem 9. Exemplo de uso da classe JOptionPane.

import java.swing.JOptionPane;

public class Teste{
  public static void main(String[] args){
    String nome = JOptionPane.showInputDialog
    ("Nome:");
    JOptionPane.showMessageDialog(null, nome);
  }
}

Apesar dessa opção, quando se fala de saída de dados em Java, o método padrão é o System.out.println(), que exibe a saída na console e não em uma tela. A Listagem 10 mostra um exemplo de uso do método System.out.println().

Listagem 10. Exemplo utilizando o método System.out.println().

public class Teste{
  public static void main(String[] args){
    System.out.println("");
  }
}

Escrita e leitura de arquivos

Um arquivo é uma sequência de bytes armazenada em um dispositivo, sendo esse dispositivo um disco rígido, um pen drive, um CD, entre outros. Em linguagens de programação, existem diversos meios para conectar um programa a um arquivo, para que esse programa possa ler e escrever dados no mesmo, além dos recursos que permitem criar e salvar novos arquivos. Em C/C++, as funções para manipulação de arquivos se encontram na biblioteca stdio.h. Para se obter acesso aos dados de um arquivo, é necessária a criação de um ponteiro do tipo FILE. Este ponteiro é declarado por:

FILE *arq;

Após a declaração do ponteiro, antes de se executar qualquer operação em um arquivo, é necessário abri-lo. Para isso, existem vários modos como, por exemplo, modo de leitura, de escrita, de leitura/escrita ou modo de adição de texto. A função responsável pela abertura de um arquivo é a fopen(), e para fechá-lo, fclose(). Para escrever caracteres, as funções mais utilizadas são putc() e fputc(). Para leitura de um arquivo, são utilizadas as funções getc() e fgetc().

Existem ainda várias outras funções para manipulação de arquivos como, por exemplo, a função rewind(), que reposiciona o ponteiro para o início do arquivo, ou as funções fread() e fwrite(), que realizam a leitura e escrita em um arquivo, respectivamente.

Em Java, existem diversas classes para manipulação de arquivos. Para criar um arquivo em Java, por exemplo, é utilizada a classe java.io.File. O código para criar um arquivo com a classe File pode ser visto a seguir:

File arquivo = new File(<caminho/arquivo>);

Para escrita e leitura em arquivos, as classes mais utilizadas são as do pacote java.io:

· PrintStream - possui um construtor que já recebe o nome de um arquivo como argumento;

· FileWriter e FileReader - atalho para escrita e leitura de arquivos;

· PrintWriter – classe que tem acesso aos métodos printf, println e print;

· BufferedWriter e BufferedReader - é um Writer/Readerque recebe outroWriter/Reader pelo construtor e concatena os diversos chars para formar umaStringatravés do métodowriteLine/readLine.

A Listagem 11 mostra um trecho de código com a classe PrintWriter para escrever em um arquivo.

Listagem 11. Trecho de código para escrita em arquivo utilizando a classe PrintWriter.

try {
  PrintWriter escrita = new PrintWriter(
    new FileWriter
    ("arquivo.txt"));

  escrita.println
  ("texto que vai ser gravado no arquivo");
  escrita.flush();
  escrita.close();

} catch (FileNotFoundException ex) {
  System.out.println("Erro" + ex);
}

A Listagem 12 mostra um trecho de código que emprega a classe BufferedReader para fazer a leitura de um arquivo.

Listagem 12. Exemplo utilizando a classe BufferedReader para leitura de arquivo.

try {
  BufferedReader leitura = new BufferedReader(
    new FileReader("arquivo.txt"));
  String linha = leitura.readLine();
 
  while (linha != null) {
    System.out.println("linha="+linha);
    linha = leitura.readLine();
   }
  leitura.close();
} catch (FileNotFoundException ex) {
  System.out.println("Erro " + ex);
} catch (IOException ex) {
  System.out.println("Erro " + ex);
}

Conclusão

Nota-se que as semelhanças entre as linguagens C/C++ e Java são inúmeras. Isto se deve ao fato de C++ ser derivada de C e Java de C++. Estas derivações trouxeram aperfeiçoamentos. No primeiro caso (de C para C++), o propósito era implementar o paradigma da programação orientada a objetos. No segundo caso (de C++ para Java), buscava-se aprimorar a linguagem e torná-la mais segura, sendo um dos fatores que mais contribuíram para isso, o fato de Java não permitir a manipulação direta de endereços de memória.

As principais semelhanças entre essas três linguagens se encontram em suas sintaxes. As estruturas de seleção e repetição, por exemplo, possuem sintaxes muito parecidas, se não iguais. Apesar de mais discretas, as linguagens C/C++ continuam em evolução. Já foram lançados vários padrões para elas. Em dezembro de 2011, por exemplo, foi lançado o padrão C11, que substitui o C99, sendo ainda muito utilizada para o desenvolvimento total ou parcial de compiladores, interpretadores, navegadores e sistemas operacionais.

Já a linguagem Java possui várias plataformas que estão em constante evolução. As inovações são especificadas por uma comunidade de desenvolvedores por meio de um processo chamado JCP (Java Community Process), que define quais serão as próximas melhorias incluídas. Depois que essas melhorias estão implementadas e testadas, é disponibilizada uma nova versão do Java.

Como visto no decorrer do artigo, as linguagens C/C++ e Java são confiáveis e muito utilizadas. Cada uma delas, como era de se esperar, possui suas diferenças, embora também contenham muitas semelhanças entre si. Algumas destas diferenças, como o modelo estruturado e o orientado a objetos, e a portabilidade, podem ser balizadores na escolha de qual destas linguagens adotar.

Esta escolha é uma decisão que precisa estar diretamente ligada ao projeto a ser desenvolvido. Pontos como desempenho, produtividade, portabilidade e limitação de recursos para execução da aplicação são fundamentais nesta decisão.