Posts Tagged "linguagens"

Exercícios de Linguagem C

Alunos da turma de Técnico em Eletrônica do sexto período. Cliquem na imagem para baixar os exercícios da primeira lista:

download

Usando o Eclipse + PTP + MPI

Este mês tenho participado do curso da MPI, Message Passing Interface, ofertado pela UFRGS. Esta é uma iniciativa interessante para as pessoas que estão trabalhando com sistemas multiprocessador e que necessitam adaptar suas aplicações a tal ambiente. Além do ambiente que é disponibilizado pelos organizadores do curso, tinha a necessidade de ter um lugar que pudesse testar sem a necessidade de estar constantemente conectado na Internet. Por esse motivo pesquisei algumas coisas e acabei utilizando o MPICH, com integração com o Eclipse, no qual já possui um plug-in para tal sistema. O MPICH é uma implementação portátil, de alto desempenho e amplamente conforme com o padrão MPI (MPI-1, MPI-2 e MPI-3).

Os objetivos do MPICH são:

  1. Fornecer uma implementação eficiente da MPI que suporte diferentes plataformas de computação e comunicação (sistemas de desktop, sistemas de memória compartilhada e arquiteturas multicore), redes de alta velocidade (Gigabit Ethernet, InfiniBand, Myrinet , Quadrics) e sistemas de computação high-end (Blue Gene, Cray);
  2. Permitir o desenvolvimento de ponta em MPI através de uma fácil estrutura modular.

Passos

1 – Você deve baixar o software disponível no site da MPICH. Como estou usando o ambiente Linux, os links abaixo são para a versão 3.0.4.

http://www.mpich.org/static/downloads/3.0.4/mpich-3.0.4.tar.gz

Possui versões para outros sistemas tais para Windows e também pacotes pré-determinados de diferentes distribuições. Lembrando que os passos abaixo o símbolo “$” indica que se trata de um terminal de usuário e tudo será instalado no diretório /opt. A maioria das distribuições não permite que usuários tenham acesso a escrita nesse diretório. Nesse caso aconselho que mude as permissões de acesso para que tenha acesso. Caso você tenho problemas com o Linux, e como mudar as permissões de acesso, aconselho ler este material e este daqui.

$ cd /opt

$ wget -c http://www.mpich.org/static/downloads/3.0.4/mpich-3.0.4.tar.gz

2 – Ao fim do download, execute os seguintes comandos:

$ mkdir /opt/mpich

$ tar zxpvf mpich-3.0.4.tar.gz

$ cd mpich-3.0.4

$ ./configure --prefix=/opt/mpich

$ make 2>&1 | tee m.txt

$ sudo make install |& tee mi.txt

Observe que após a instalação os executáveis estarão disponíveis no diretório /opt/mpich/bin , logo as variáveis de ambiente de usuário devem ser modificadas.

3 – Em um ambiente baseado em Ubuntu e com algum editor de texto, nesse caso específico foi utilizado o vim, digite:

$ cp -vax ~/.profile ~/.profile.bak

$ cp -vax ~/.bashrc ~/.bashrc.bak

$ echo "export PATH=/opt/mpich/bin:$PATH" >> ~/.profile

$ echo "export PATH=/opt/mpich/bin:$PATH" >> ~/.bashrc

Os dois primeiros comandos acima estão criando cópias dos arquivos, caso algo de errado aconteça e você poderá reverter. Depois saia do terminal e abra o mesmo novamente.

4 – Baixe o ambiente de desenvolvimento Eclipse, nesse caso estou usando o Kepler para Linux x86_64, disponível aqui nesse link. No entanto no mesmo site encontra-se uma IDE já preparada para trabalhar com o ambiente de multiprocessamento, que está disponível aqui. Porém no nosso exemplo vamos instalar esses pacotes manualmente, caso queira utilizar o ambiente para outra linguagem de programação, tais quais, java ou C/C++.

Como o arquivo já se encontra em uma versão pré compilada, a única ação para realizar a instalação é apenas descompactar o arquivo baixado para algum lugar no disco rígido. No caso aqui do tutorial, colocamos na pasta /opt do linux. Ficando nesse caso /opt/eclipse

Para executar o eclipse no linux, basta apenas executar o arquivo eclipse que se encontra na pasta citada anteriormente, ou criar um atalho na área de trabalho para este arquivo.

5 – Executando o eclipse, este irá executar o processo de descoberta do ambiente de trabalho, esta área é responsável para trabalhar com os arquivos fontes de seus programas. Neste caso colocamos em uma pasta nomeada como workspace na pasta do usuário, conforme figura abaixo:

eclipse1

Ao clicar em OK, o ambiente de desenvolvimento irá aparecer conforme imagem abaixo, então feche esta mensagem conforme círculo vermelho destacado a seguir:

eclipse2

Após essa parte, clique no menu HELP e escolha INSTALL NEW SOFTWARE… A seguinte tela irá aparecer:

 

eclipse3

Após este processo, na lista “drop down” ao lado da escrita “Work with” escolha o site de configuração do Kepler:

eclipse4

Espere alguns segundos, dependo da sua conexão de Internet, e a seguinte tela com os complementos do Eclipse irá aparecer:

eclipse5

Em seguida onde tem a inscrição “type filter text” digite “PTP“, aguarde alguns instantes – dependendo da conexão da Internet deve durar alguns minutos – que irá aparecer uma imagem como a da figura a seguir. Marque todas as opções do “check box” como na figura e aperte o botão “next

eclipse5

Aceite e confirme todas as outras opções que aparecerem. Ao final o sistema de instalação pedirá ao usuário que reinicie. Confirme a opção e na próxima vez que o ambiente Eclipse for inicializado, o mesmo estará com o PTP instalado.

6 – Para configurar o MPI, inicialize o Eclipse, escolha as opções de menu Windows -> Preferences… na tela que abrirá clique em cima do menu “Parallel Tools“, uma mensagem de erro aparecerá, nesse caso é porque a instalação não encontrou um diretório temporário para o compilador. Como sugestão coloque conforme a figura a seguir, que indica o diretório “/tmp“:

eclipse6

Clique no botão “Apply” e em seguida clique em cima do sub menu “Parallel Language” e escolha o sub menu “MPI“. Esta opção é para indicar ao ambiente onde se encontra o MPICH. No exemplo do tutorial, instalamos no diretório “/opt/mpich“. Clique no botão NEW e abra o diretório “/opt/mpich/include“, além de indicar os executáveis do mpicc e mpi++.

eclipse7

Confirma as alterações clicando no botão OK, e seu ambiente estará configurado.

7 – Para testar o ambiente se está configurado corretamente, na tela principal escolha a opção de menu: File -> New -> C Project..

Na tela que irá aparecer escolha a opção Executable -> MPI Empty C Project. Observe que o “Toolchain” esteja na opção Linux GCC. Em “Project Name” coloque a descrição “teste“:

eclipse8

Clique em Next, Next e depois Finish. Um novo projeto será criado. Crie um novo arquivo fonte, na opção File -> New -> Source File.

eclipse9

Coloque o nome de main.c. A versão do arquivo já com o conteúdo, pode ser baixada aqui. Senão copie e cole o conteúdo do arquivo abaixo para o recém criado main.c:

/*
============================================================================
Name        : main.c
Author      : Anderson Moreira
Version     :
Copyright   : IFPE
Description : Program 01 - teste
============================================================================
*/
#include <mpi.h>
#include <stdio.h>
#include <string.h>

int main (int argc, char *argv[ ])
{
   int processId; /* rank dos processos */
   int noProcesses; /* Numero de processos */
   int nameSize; /* Tamanho do nome */
   char computerName[MPI_MAX_PROCESSOR_NAME];
   MPI_Init(&argc, &argv);
   MPI_Comm_size(MPI_COMM_WORLD, &noProcesses);
   MPI_Comm_rank(MPI_COMM_WORLD, &processId);
   MPI_Get_processor_name(computerName, &nameSize);
   fprintf(stderr,"Hello gerado pelo processo %d na máquina %s\n", processId, computerName);
   MPI_Finalize( );
   return 0;
}

Pode ser que clicando duas vezes em cima do arquivo main.c, aparece uma mensagem de erro:

eclipse10

Para resolver este problema, clique em cima com o botão direito do mouse sobre o arquivo fonte e escolha, Open with -> C/C++ Editor. Após, este passo, salve o arquivo. Clique com o botão direito do projeto “teste” e escolha “Properties“. Outra forma é clicar em cima do projeto e apertar a combinação de teclas <ALT> + <ENTER>.

Na tela que aparece escolha a opção C/C++ Build -> Settings -> GCC C Linker -> Libraries, conforme figura:

eclipse11

Apague o caminho para biblioteca “mpi” clicando no botão “Delete” que é o que possui um “X” vermelho.

Saia da tela clicando no botão “OK” e pronto. Ao compilar o programa – combinação de teclas <CTRL> + <B> – você verá que o mesmo estará sendo verificado pelo ambiente do mpi.

Para executar o programa. Clique no menu Run -> Run configurations… Na tela que segue, escolha “C/C++ Remote Application” e depois clique em cima de New Launch Configuration (new). Na linha onde está “Remote Absolute File Path” digite o caminho do executável do programa com o prefixo do mpirun (mpirun -np 3 /home/alsm/workspace/teste/Debug/teste) conforme figura abaixo:

new_configuration

Clique em “Run” e pronto! Na tela de console irá aparecer a seguinte mensagem:

saida

Lembrando que a máquina “alsm-dadt” é a máquina identificada no arquivo “machinefile” localizado na pasta “home” do usuário e que tenha o seguinte conteúdo:

alsm-dadt:4

Agradecer a Professora Carla Osthoff, pela ajuda no curso rápido de MPI.

Fontes: http://www.top500.org, http://mpi.deino.net/manual.htm, http://www.lam-mpi.org/tutorials/, http://www.cesup.ufrgs.br/informacoes-ao-usuario/manuais, http://www.mcs.anl.gov/research/projects/mpi/, http://www.mpich.org/, http://www.mpi-forum.org/

Curso GRATUITO de Lógica de Programação on-line e com certificado

Colaboração: Carlos Tosin

Data de Publicação: 06 de setembro de 2011

A Softblue está lançando esta semana o curso de Lógica de Programação. E o melhor: totalmente gratuito! Este curso é voltado para quem não tem nenhum conhecimento na área de programação, mas tem interesse em ingressar na carreira de programador!

Veja abaixo a ementa do curso:

Introdução à lógica de programação

  • Lógica e lógica de programação
  • Algoritmos
  • Representação de algoritmos

Tipos de dados, constantes e variáveis

  • Tipos de dados primitivos
  • Constantes
  • Declaração de variáveis e atribuição de valores

Operadores

  • Operadores aritméticos
  • Operadores relacionais
  • Operadores lógicos

Estruturas de controle

  • Estruturas de seleção
  • Estruturas de repetição

Tipos de dados avançados

  • Vetores
  • Matrizes
  • Tipos de dados customizados

Arquivos

  • Dados de um arquivo
  • Criação de arquivos
  • Manipulação de dados

Funções

  • Declaração
  • Chamada
  • Variáveis locais e globais
  • Passagem de parâmetros
  • Retorno de valores
  • Recursividade

Linguagens de programação

  • Funcionamento
  • Exemplos de linguagens
  • Diferentes classificações

Após realizar todo o curso o aluno ainda obtém o certificado oficial da Softblue pela conclusão do curso em seu nome, com a informação da carga horária do curso.

Variáveis e valores em C (Parte 2)

Dando continuidade aos postsobre variáveis e valores em C. Vamos ver hoje misturas de variáveis e alguns exemplos de aprimoramento na definição.

Mistura de tipos em expressões

 Podem-se misturar tipos numa expressão aritmética.

Se numa operação existe a mistura de tipos, antes da operação os operandos são convertidos para o tipo dominante.

Na atribuição o tipo resultante da expressão calculada (lado direito) é convertido para o tipo da variável que recebe o valor (lado esquerdo) antes da atribuição.

Tipos dominantes – vale a seguinte prioridade:

 bool < char < int < long < float < double

Mudança de tipos

Pode-se mudar o tipo de uma expressão colocando-se o operador (tipo) à frente da expressão.

Exemplos:

int n;
double x, z;
z = x / (double) n;
n = (int) x;

Além disso, quando valores reais são atribuidos a variáveis inteiras, os mesmos são truncados.

Veja o programa abaixo e o que será impresso:

#include <stdio.h>
#include <stdlib.h>

int main () {
int i, j, k;
float a, b, c;
i = 13;
j = 5;
k = i / j;
printf("\n1 - valor de k = %3d", k);
c = i / j;
printf("\n2 - valor de c = %5.2f", c);
k = i / (float) j;
printf("\n3 - valor de k = %3d", k);
c = i / (float) j;
printf("\n4 - valor de c = %5.2f", c);

a = 13;
b = 5;
k = a / b;
printf("\n5 - valor de k = %3d", k);
c = a / b;
printf("\n6 - valor de c = %5.2f", c);
c = (int) a / (int) b;
printf("\n7 - valor de k = %5.2f", c);
c = (int) a / b;
printf("\n8 - valor de c = %5.2f", c);
system(“pause”); return 0;
}

Vai ser impresso:

1 – valor de k =   2
2 – valor de c =  2.00
3 – valor de k =   2
4 – valor de c =  2.60
5 – valor de k =   2
6 – valor de c =  2.60
7 – valor de k =  2.00
8 – valor de c =  2.60

Exercício) Dados a, b e c reais, calcular as raízes da equação do segundo grau ax2 + bx + c = 0

Resolução:

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

// Exercício) dados a,b,c reais, calcular as raizes da equacao ax2 + bx + c =0
int main() {
double a,b,c,   // coeficientes dados
delta,   // discriminante da equação
x1, x2;  // raízes calculadas
// entre com a,b e c
printf("digite o valor de a:");
scanf("%lf", &a);
printf("digite o valor de b:");
scanf("%lf", &b);
printf("digite o valor de c:");
scanf("%lf", &c);

// verifica se a = 0. Neste caso não é equação do 2. grau
if (a == 0.0) printf("\n*** nao e equacao do 2. grau");
else {// calcule o valor do discriminante
delta = b*b - 4*a*c;

// verifica se delta < 0 e neste caso não há raizes reais
if (delta < 0) printf ("\n*** nao ha raizes reais");
else {// calcule x1 e x2
x1 = (-b + sqrt(delta))/(2*a);
x2 = (-b - sqrt(delta))/(2*a);
printf("\n***x1 = %10.4lf",x1);
printf("\n***x2 = %10.4lf",x2);
}
}
system(“pause”); return 0;
}

Exercício) Calcular os valores da função senx*cosx para x = 0, 0.001, 0.002, …, 2pi

#include <stdio.h>
#include <stdlib.h>
#define pi 3.1416
#define pi2 2*pi
#include <math.h>
// Exercício) valores da função senx*cosx para x de 0 a 2pi com passo 0.001
int main() {
double x;
// imprime a tabela
for (x = 0.0; x < pi2; x = x + 0.0001)
printf("\nx=%10.5lf     senx*cosx=%10.5lf", x, sin(x)*cos(x));
system(“pause”); return 0;
}

No problema acima há 2 novidades:

Definição de constantes:

O comando #define é usado para definir um texto como sinônimo de um outro texto. No exemplo acima se define 3.1416 como sinônimo de pi e 2*pi como sinônimo de pi2.

For com passo 0.001

A variável do comando for é double e o passo de incremento é 0.001. Até agora, usávamos o comando for, incrementando ou decrementando o contador de 1 em 1 , 2 em 2, etc.,  mas sempre inteiros. O incremento pode ser qualquer valor.

Especialmente com relação ao #define, pode-se usá-lo para criar sinônimos dos elementos de programação e tornar mais clara a programação. Ao gosto do freguês, por exemplo:

#include <stdio.h>
#define abra {
#define feche }
#define enquanto while
#define se if
#define entao
#define senao else
#define inteiro int
#define leia scanf
#define imprima printf
// programa exemplo em linguagem algorítica

int main()
abra
inteiro n,    // numero dado
i;    // contador
// ler o n
imprima("digite o valor de n:");
leia("%d", &n);

// verifique quais são e quais não são os divisores de n
i = 2;
enquanto (i <= n/2)
abra
se (n%i == 0)

entao imprima ("\n%d - e divisor de %d", i, n);
senao imprima ("\n%d - nao e divisor de %d", i, n);

i++;
feche

system(“pause”); return 0;
feche

Exercício) Dado n inteiro e x real, calcular o valor da soma 1 + x + x2 + x3 + … + xn

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
// Exercício) Dado n>=0 inteiro e x real, calcular a soma 1+x+x2+x3+ . . .+ xn
int main() {
double x,   // dado
soma;// resultado calculado
int i,  // contador
n;  // dado

// ler x e n
printf("\nentre com o valor de x:"); scanf("%lf", &x);
printf("\nentre com o valor de n:"); scanf("%d", &n);

// calcular a soma
soma = 1;
for (i = 1; i <= n; i++) soma = soma + pow(x,i);
printf("\n*** valor da soma - %15.7lf", soma);
system(“pause”); return 0;
}

Exercício) Idem ao anterior sem usar pow

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
// Exercício) Dado n>=0 inteiro e x real, calcular a soma 1+x+x2+x3+ . . .+ xn

int main() {
double x,      // dado
termo,  // termo da soma
soma;   // resultado calculado
int i,  // contador
n;  // dado

// ler x e n
printf("\nentre com o valor de x:"); scanf("%lf", &x);
printf("\nentre com o valor de n:"); scanf("%d", &n);

// calcular a soma
soma = 1.0;
termo = 1.0;
for (i = 1; i <= n; i++) {
{termo = termo * x;
soma = soma + termo;
}

// imprime o resultado
printf("\n*** valor da soma - %15.7lf", soma);
system(“pause”); return 0;
}

É isso aí alunado! Por enquanto vai ficar nesse conteúdo até algum aluno me venha dizer que não sabe o básico de C! abraços e bons estudos

Programação 2 – Listas Duplamente Encadeadas

Ao trabalharmos anteriormente com as listas de encadeamento simples, tivemos a oportunidade de observar neste tipo de estrutura a falta de um mecanismo eficiente para percorrer os elementos da lista em ordem inversa.

Este não é o único problema identificado. A remoção de um elemento da lista, pelo fato de não termos a referência de seu antecessor, torna-se também complicada.

De forma a sanar todas estas dificuldades encontradas no uso das listas de encadeamento simples, temos as chamadas listas duplamente encadeadas, onde cada elemento tem um ponteiro para o próximo dentro da estrutura e outro para seu antecessor, além é claro dos campos responsáveis pelo armazenamento das informações.

struct noh {
int mat;
struct noh *prox;
struct noh *ant;
};

typedef struct noh lista;

Da mesma forma que fizemos nas listas de encadeamento simples, usaremos de um facilitador para marcarmos o início de nossa lista. Nosso marcador de início da lista não receberá dados, servindo apenas como uma referência para o início de nossa lista, que caso encontre-se vazia terá em nosso inicio->prox o valor NULL.

Utilizaremos também um outro marcador o qual atribuiremos o nome de atual, o qual marcará sempre a posição do último elemento de nossa lista. Inicialmente como não possuímos nenhum elemento em nossa lista, o valor de inicio será igual ao de atual. Entretanto, a medida em que formos inserindo novos elementos, o marcador de atual irá sempre se deslocando e apontando para o último elemento da lista. Com isso, ao realizarmos uma pesquisa seqüencial na lista, temos que nos ater ao fato de que nosso marcador deve terminar a operação apontando sempre ao último elemento.

É importante estar atento também ao fato de que neste exemplo, ao realizarmos a tarefa de inserção na lista, esta irá processar a entrada de um novo nó ao final da lista e não mais em seu início.

Enfim, em regras gerais, salvo as modificações acima citadas, usaremos dos mesmos princípios estudados anteriormente, com a facilidade de possuirmos agora sempre o endereço de memória do elemento anterior ao atual da lista, o que nos facilita e muito na navegação pela mesma.

Vejamos então a seguir como fica nosso exemplo de listas duplamente encadeadas.

struct noh {
int mat;
char *nome;
char sexo[1];
struct noh *prox;
struct noh *ant;
};

typedef struct noh lista;

char onome[256];
lista *inicio=NULL;
lista *atual=NULL;

void inicializa(){
inicio =(lista*) malloc (sizeof (lista));
if (!inicio){
printf("\nNao existe espaco na memoria!");
exit(1);
}

inicio->ant = NULL;
inicio->prox = NULL;
atual = inicio;
}

void insere()
{
lista *novo=NULL;
novo = (lista*) malloc (sizeof (lista));
if (!novo){
printf("\nNao existe espaco na memoria!");
exit(1);
}

printf("\nDigite a matricula: ");
scanf("%d",&novo->mat);
printf("\nDigite o nome: ");
scanf("%s",onome);
novo->nome = (char*) malloc (strlen(onome)+1);
strcpy(novo->nome,onome);
fflush(stdin);
printf("\nDigite o sexo: ");
scanf("%s",novo->sexo);
fflush(stdin);
novo->ant = atual;
novo->prox = NULL;
atual->prox = novo;
atual = novo;
}

void imprime(){
if (inicio->prox==NULL)
printf("\nFila vazia!\n");
else{
atual=inicio;
do{
atual=atual->prox;
printf("\n\nMatricula: %d",atual->mat);
printf("\nNome: %s",atual->nome);
printf("\nSexo: %s",atual->sexo);
}

while (atual->prox!=NULL);
}
}

void busca(){
int matbusca,encontrou=0;
printf("\n\nDigite a matricula para busca: ");
scanf("%d",&matbusca);
if (inicio->prox==NULL)
printf("\nFila vazia! Busca nao pode ser processada!\n");
else{
for (atual=inicio->prox; atual!=NULL; atual=atual->prox){
if (matbusca==atual->mat){
printf("\n\nMatricula: %d",atual->mat);
printf("\nNome: %s",atual->nome);
printf("\nSexo: %s",atual->sexo);
encontrou=1;
}

if (atual->prox==NULL && encontrou==0){
printf("\nRegistro nao encontrado!\n");
break;
}

if (atual->prox==NULL)
break;
}
}
}

void exclui(){
int matbusca,encontrou=0;
printf("\n\nDigite a matricula para exclusao: ");
scanf("%d",&matbusca);
if (inicio->prox==NULL)
printf("\nFila vazia! Busca para exclusao nao pode ser processada!\n");
else{
for (atual=inicio->prox; atual!=NULL; atual=atual->prox){
if (matbusca==atual->mat){
encontrou=1;
if (atual->prox==NULL){
atual->ant->prox = NULL;
free(atual);
atual = inicio;
}else{
atual->ant->prox = atual->prox;
atual->prox->ant = atual->ant;
free(atual);
atual=inicio;
}
}

if (atual->prox==NULL && encontrou==0){
printf("\nRegistro para exclusao nao encontrado!\n");
break;
}

if (atual->prox==NULL)
break;
}
}
}

void menu(){
clrscr();
printf("\n\t\t\t Controle de Lista");
printf("\n\n1 - Inserir\t2 - Excluir\t3 - Imprimir\t4 - Busca\t5 - Sair\n\n");
printf("Sua opcao: ");
}

void finaliza()
{
atual=inicio;
while (inicio->prox!=NULL){
if(inicio->prox->prox!=NULL){
inicio->prox = inicio->prox->prox;
free(inicio->prox->ant);
inicio->prox->ant = inicio;
}else{
free(inicio->prox);
inicio->prox = NULL;
}
}

free(inicio);
}

void main(){
char opcao='0';
inicializa();
while (opcao !='5'){
menu();
opcao=getchar();
fflush(stdin);
switch(opcao){
case '1':insere();
break;
case '2':exclui();
break;
case '3':imprime();
getch();
break;
case '4':busca();
getch();
break;
case '5':printf("\nFIM DE EXECUCAO!");
finaliza();
getch();
exit(1);
default:break;
}
}
}

Observações finais sobre listas duplamente encadeadas
Tivemos neste estudo a oportunidade de verificar como o duplo encadeamento nos permite trabalhar de maneira bem mais fácil pela lista.

É importante frisar que em nosso exemplo optamos por trabalhar com apenas duas variáveis para a navegação pela lista: inicio e atual, e com a condição de que após cada chamada de função, esta deve terminar posicionando nosso atual para o endereço do último nó da lista. Esta regra foi aplicada apenas para forçar uma atenção maior ao caminhar pela lista, entretanto não é a mais eficiente, principalmente quando tratamos da função de exclusão. Se nesta optarmos por introduzir mais uma variável, não necessitaríamos realizar o re-trabalho de voltarmos ao início da lista após uma exclusão, poderíamos guardar o endereço no próximo nó e continuar normalmente nossa busca a partir deste.

Com certeza, após a implementação do exemplo anterior, você já deve ter montado em sua cabeça as alterações necessárias a este para que trabalhemos com o conceito de filas e pilhas. Pois bem, este será nosso próximo estudo.

Programação 1 – Lista encadeada simples

Pessoal de ADS. Por esses dias estarei colocando no blog algumas noções de programação, como lista encadeada, alocação dinâmica, verificação de código com GDB, entre outras… Isso porque estou notando uma queda de rendimento de vocês em sala principalmente quando envolve programação. Vamos lá!

Normalmente para realizarmos a representação de um grupo de dados, utilizamos a forma mais simples para tal que é a dada através do uso de um vetor. Ao declararmos um vetor estamos reservando um espaço de memória unido e em seqüência, para armazenarmos seus elementos. Em um vetor, acessamos seus elementos indexando o ponteiro para seu primeiro elemento.

#define TAMANHO 10
int vetor[TAMANHO];

Ao realizarmos a declaração acima, estamos a partir do endereço de memória FF04, inclusive com este, alocando um total de 10 espaços em memória para que dados do tipo inteiro possam ser gravados nestes.
Desta forma, vetor é o endereço da memória onde o primeiro elemento do vetor está armazenado. De forma análoga poderíamos escrever a mesma situação como &vetor[0].
Apesar de extremamente útil em várias situações, o uso de vetores apresenta como grande desvantagem o fato deste não ser uma estrutura flexível, haja vista que necessitamos dimensioná-lo com um número máximo de elementos. Seu redimensionamento em tempo de execução não é simples e pode requer um recurso computacional mais elevado. Com isso, ao utilizarmos um vetor, corremos o risco da necessidade de crescê-lo além do previsto durante a execução do programa ou então termos super dimensionado o problema e ficarmos com espaço alocado para o mesmo sem que estes sejam utilizados.
Ao fazermos uso portanto de estruturas dinâmicas, como estas alocam seus elementos usando alocação dinâmica de memória, temos esta situação resolvida.

Listas Encadeadas.

Listas encadeadas são estruturas flexíveis onde a cada novo elemento inserido na estrutura é alocado um espaço de memória para que seja processado seu armazenamento. Listas encadeadas, devido ao uso do mecanismo de alocação dinâmica, terão sempre o espaço de memória utilizado variando de acordo com seu número de elementos.
Uma lista encadeada é uma coleção de nós, que armazenam dados, e de ligações com outros nós. Os nós podem estar localizados agora em qualquer lugar na memória, e a passagem de um nó para o outro se dará através do armazenamento dos endereços de outros nós.
Na lista encadeada cada nó conterá um ou mais campos responsáveis pelo armazenamento das informações, além de um campo que é um ponteiro para uma próxima estrutura do mesmo tipo.

struct noh {
int mat;
struct noh *prox;
};
typedef struct noh lista;

De forma a fornecer um facilitador na compreensão das listas, trabalharemos com o conceito de lista com cabeça, ou seja, teremos sempre o conteúdo do primeiro nó irrelevante. Este servirá apenas para marcar o início da lista, não se alterará, e quando estiver apontando para null, indicará que a lista está vazia.



struct noh {
int mat;
struct noh *prox;
};
typedef struct noh lista;
lista *inicio;
inicio =(lista*) malloc (sizeof (lista));
inicio->prox = NULL;

Função para inicialização.

A função para inicialização de uma lista deve criar o nó inicial da lista mas também, no momento de sua criação verificar se há espaço livre na memória para a efetivação do processo.
Iniciaremos neste momento através dos exemplos dados a construção de um primeiro programa modelo, para tratamento de listas, o qual fará uso de variáveis globais de forma a eliminar a necessidade no momento de passagem de parâmetros às funções. Um outro exemplo será dado no decorrer de nossas aulas utilizando estes recursos.


struct noh {
int mat;
char *nome;
char sexo[1];
struct noh *prox;
};
typedef struct noh lista;
char onome[256];
lista *inicio=NULL;
lista *atual=NULL;
lista *anterior=NULL;

void inicializa(){
inicio =(lista*) malloc (sizeof (lista));
if (!inicio){
printf(“\nNao existe espaco na memoria!”);
exit(1);
}
inicio->prox = NULL;
}

Função para inserção

A partir do momento que temos nossa lista inicializada, podemos proceder com a inserção de novos elementos nesta. Lembrando que para cada novo elemento adicionado estaremos alocando um novo espaço de memória para este.
Podemos realizar a inserção em qualquer lugar da lista, entretanto, a maneira mais simples de execução desta tarefa é dada com a inserção do novo elemento no início da lista. Sendo assim o novo elemento entrará logo após nosso marcador de início da lista.
O mais importante aqui é realizar sempre sua programação de maneira defensiva e com o máximo de cuidado nos detalhes de modo a não cometer falhas de direcionamento de próximos nós.
Vejamos como ficaria nossa função de inserção.

void insere()
{
lista *novo=NULL;
novo = (lista*) malloc (sizeof (lista));
if (!novo){
printf("\nNao existe espaco na memoria!");
exit(1);
}
printf("\nDigite a matricula: ");
scanf("%d",&novo->mat);
printf("\nDigite o nome: ");
scanf("%s",onome);
novo->nome = (char*) malloc (strlen(onome)+1);
strcpy(novo->nome,onome);
fflush(stdin);
printf("\nDigite o sexo: ");
scanf("%s",novo->sexo);
fflush(stdin);
if (inicio->prox == NULL){
inicio->prox = novo;
novo->prox = NULL;
}else{
novo->prox = inicio->prox;
inicio->prox = novo;
}
}

Função para impressão

Nossa função de impressão terá uma tarefa bem simples a ser realizada. Seu papel será de a partir do marcador de início da lista, percorrer toda esta, e imprimir as informações gravadas em seus campos. Antes de ler a solução proposta abaixo para nosso estudo, tente criar a função de impressão como um primeiro exercício de nossa disciplina.
Dica: Não se esqueça de verificar antes de tudo se alista não está vazia. Ao realizar a impressão a mesma deve ocorrer, pela lógica proposta acima, na ordem inversa a da entrada dos dados.

void imprime(){
if (inicio->prox==NULL)
printf("\nFila vazia!\n");
else{
atual=inicio;
do{
atual=atual->prox;
printf("\n\nMatricula: %d",atual->mat);
printf("\nNome: %s",atual->nome);
printf("\nSexo: %s",atual->sexo);
}
while (atual->prox!=NULL);
}
}

Função para busca seqüencial

Para verificarmos se um determinado elemento encontra-se presente em nossa lista, utilizaremos aqui de uma busca seqüencial. Em nosso exemplo como citado anteriormente estamos trabalhando neste primeiro momento com variáveis globais e com as funções sem passagem de parâmetros. Entretanto, adiante estaremos reescrevendo a mesma recebendo como parâmetros um ponteiro para lista e uma informação que necessitamos pesquisar. Vamos ao nosso exemplo.

void busca(){
int matbusca,encontrou=0;
printf("\n\nDigite a matricula para busca: ");
scanf("%d",&matbusca);
if (inicio->prox==NULL)
printf("\nFila vazia! Busca nao pode ser processada!\n");
else{
for (atual=inicio->prox; atual!=NULL; atual=atual->prox){
if (matbusca==atual->mat){
printf("\n\nMatricula: %d",atual->mat);
printf("\nNome: %s",atual->nome);
printf("\nSexo: %s",atual->sexo);
encontrou=1;
}
if (atual->prox==NULL && encontrou==0)
printf("\nRegistro nao encontrado!\n");
}
}
}

Função para excluir um elemento da lista

Implementaremos agora a função responsável por localizar e excluir um elemento da lista. Novamente é importante estar atento a detalhes como:
– lista está vazia?
– o elemento a ser excluído não é o primeiro da lista?
A função para remover um elemento da lista é um pouco mais complexa já que devemos guardar o elemento anterior ao procurado e fazer com que este aponte após a exclusão do elemento identificado para o posterior a ele, de forma a manter de maneira correta o encadeamento de nossa lista.
Vamos ao nosso código!

void exclui(){
int matbusca,encontrou=0;
printf("\n\nDigite a matricula para exclusao: ");
scanf("%d",&matbusca);
if (inicio->prox==NULL)
printf("\nFila vazia! Busca para exclusao nao pode ser processada!\n");
else{
anterior = inicio;
for (atual=inicio->prox; atual!=NULL; atual=atual->prox){
if (matbusca==atual->mat){
anterior->prox = atual->prox;
free(atual);
encontrou=1;
}
if (encontrou == 1)
break;
anterior=atual;
if (atual->prox==NULL && encontrou==0)
printf("\nRegistro para exclusao nao encontrado!\n");
}
}
}

Observações finais sobre listas encadeadas

Tivemos a oportunidade de iniciar nosso estudo sobre listas através das listas de encadeamento simples. Notamos que pelo fato de não possuirmos o apontador para elementos anteriores, nosso caminhar pela lista fica prejudicado e necessitamos de soluções nada elegantes para a resolução de problemas como uma exclusão de um elemento no meio da lista.
Partiremos no próximo estudo para o tratamento das listas duplamente encadeadas, o que nos permitirá controlar de maneira eficiente este caminhar, nos inserindo nos tópicos de filas e pilhas.
Quando fazemos uso de linguagens de programação que possuem coletores de lixo de memória, não nos é necessário desalocar o espaço de memória utilizado. Entretanto a linguagem C não possui este mecanismo e liberar a memória utilizada é algo essencial. Fazemos isso através do comando free().

O que fazer com C que aprendeu na faculdade?

Escrito em 6/1/2011 por Fernando Mercês <fernando [a t] mentebinaria.com.br>
Licenciado sob a Creative Commons 3.0

Em muitas faculdades brasileiras a linguagem C é ensinada aos alunos de cursos de tecnologia. Mesmo assustando os novatos, a maioria resiste e vence a matéria. O problema é entender por qual motivo o C foi escolhido para iniciar o curso de programação. Seria uma linguagem didática para se aprender a programar? Ou é um teste para ver quem tem ou não o “jeito pra coisa”? Alguns diriam que o correto seria começar com Pascal, mas há quem defenda linguagens mais modernas como Python, Perl, Ruby ou PHP. E aí, pra que serve o C no primeiro período? Neste artigo farei uma análise sobre o que se aprende da linguagem, o motivo pelo qual ela surge no início do curso, seu valor de mercado e o que é possível fazer com esse start que a faculdade nos dá.

A linguagem C

A importância histórica da linguagem C é inegável e despensa maiores comentários. Sabemos que até hoje a maioria dos softwares mais poderosos são feitos em C e/ou C++ (seu primo orientado à objetos). Boa parte do kernel Linux, Windows e outros núcleos e SOs são feitas em C. Então o C deve servir para algo. 🙂 Ensino da linguagem C
Você acabou de entrar na faculdade, está tendo aulas desta linguagem e não está entendendo nada? Não se preocupe, você não está sozinho. Algumas instituições de ensino acham que o C é uma liguagem didática, quando não é. Para se aprender a programar, usa-se pseudo-linguagem, PORTUGOL ou ferramentas do gênero. Nem mesmo o Pascal, considerado mais fácil de se aprender que o C, é atraente e interessante à primeira vista. O grande monstro que aterroriza o aluno é a pergunta: “Pra quê que eu vou fazer isso?”. Pois é, para quê escrever um programa em C que calcule a média de três alunos e imprima na tela? Qual a lição tirada disso? A resposta é: nenhuma. A maneira como a linguagem é lecionada tenta empurrar o C guela abaixo em estudantes que viram, por exemplo, Visual Basic e Delphi no segundo grau. Isto é, se é que estudaram tais linguagens ou lembram-se delas. Não poderia dar certo mesmo.Antes de criar um programa, o aluno tem que saber o que está fazendo. O que é criar um programa, para que serve isso, o que é um arquivo executável, um binário, bits, bytes, o processador, dentre outros conceitos importantíssimos antes de se escrever o primeiro Hello World.

O resultado desse ensino forçado é o alto íncide de reprovação, abandono, mudança de curso e desistência. É possível encontrar alunos que estão no fim do curso de programação mas ainda não passaram nas matérias básicas de C. É o terror da faculdade. Definitvamente, a linguagem C vira uma vilã e a frase mais ouvida nos corredores é que “C é chato”.

Por que “C é chato”?

Porque ela não te mima. Numa escala onde o nível mais alto é o mais próximo da linguagem usada pelo ser humano e o mais baixo, da linguagem usada pelos microprocessadores, a linguagem C é considerada de nível médio. Assembly, por exemplo, é de baixo nível, enquanto Object Pascal (usada no Delphi), de alto nível. Isso significa que para programar em C é preciso conhecer conceitos mais próximos do hardware, que as linguagens de alto nível abstraem para o programador, tornando o trabalho mais fácil. Por isso temos a impressão de que C é chato, difícil, sem sentido. Realmente, sem os conceitos básicos de computação bem sólidos, um código em C pode tornar-se incompreensível. Vejamos um exemplo.Um código em PHP (alto nível) para se declarar uma variável e armazenar uma frase nela:

----------------------------------CODE----------------------------------
$str = "Essa é minha string";
----------------------------------EDOC----------------------------------

Um código equivalente em C, seria:

----------------------------------CODE----------------------------------
void main(void)
{    char str[] = "Essa é minha string";
}
----------------------------------EDOC----------------------------------

No código em C, uma função teve de ser escrita (a main, que é a função principal de um programa), inclusive com seu tipo de retorno e parâmetros, onde usei void para não retornar nem receber nada. Além disso, foi criado um vetor de caracteres (char) para armazenar a frase. Em C, entende-se como string um vetor de caracteres onde o último caracter é o NULL, código 0x00 na tabela ASCII. Tá vendo por que se precisa dos conceitos de computação até pra começar uma frase em C? Agora perceba a jogada:

----------------------------------CODE----------------------------------
#include   void main(void)
{    char str[20];
strcpy(str, "Essa é minha string");
}----------------------------------EDOC----------------------------------

A função strcpy(), fornecida pelo header string.h, copia caracteres para uma variável do tipo vetor de chars e adiciona um \0 (NULL) na última posição. Perceba que iniciamos o vetor de char com 20 posições para abrigar os 19 caracteres da frase proposta mais o NULL, que é um caractere só. As coisas começam a fazer sentido, apesar de “feias”, não?

E assim é o C. Exigente, porém elegante. Se tem os conceitos de computação, sem dúvida não terá grandes dificuldades com a linguagem.

Usando o C na vida e no mercado de trabalho

Certo, você se convenceu de que C é legal de aprender, poderoso e aprendeu. E agora, faz o quê? Tem um colega seu ganhando dinheiro fazendo sites em Ruby on Rails. Outro ficando faturando uma grana fazendo sistemas em Delphi para clientes, com imagens, botões brilhantes e multimídia. O que você, recém-estudado programador em C vai fazer com aquela tela preta pedindo dados com scanf()? Nada. Não é assim que se trabalha com C, ou pelo menos, não mais. Já foi o tempo em que os sistemas eram feitos dessa maneira. Além disso, mesmo nesse tempo a linguagem C foi rapidamente substituída neste meio pela linguagem CLIPPER e pelo COBOL, que reinou (e ainda reina) nos mainframes.

O forte do C e C++ hoje são aplicações desktop, inclusive as baseadas em rede. C também é útil para escrever compiladores e interpretadores para outras linguagens, por exemplo. Sabia que o PHP é escrito em C? Pois é, assim como Python, Ruby, BASH e muitos outros interpretadores. Então tem alguém ganhando dinheiro com C por aí, concorda?

Em novembro do ano passado houve uma edição de um evento chamado Universidade Livre em que Olivier Hallot, diretor da BrOffice.org falou durante alguns minutos numa faculdade carioca da dificuldade de encontrar programadores para contratar e fez um apelo para que os alunos levem a sério que o mercado está muito carente de bons programadores, principalmente em C/C++. Também em setembro do ano passado uma empresa publicou uma vaga no Rio de Janeiro buscando um profissional com os seguintes conhecimentos:

Sistema Operacional Linux;
Banco de dados MySQL;
Criação e manutenção de tabelas, relacionamentos, scripts, etc.;
Linguagem C, e das APIs: (V4L2), GTK, além de OpenGL;
Adobe Flex.O salário inicial era de R$ 5.000,00. A vaga deve estar aberta até hoje…

Então por que aprendo Java na faculdade?

A faculdade tenta ser a mais moderna impossível, mas esquece de verdadeiramente orientar na profissão. Java é uma linguagem potente, flexível e poderosa mas tem um fim completamente diferente da linguagem C. Com Java se programa para web, dispositivos móveis, aplicações locais (pouco usada), sistemas de informação, embarcados etc. A flexibilidade é enorme, mas o foco é outro. Não se faz uma suíte de aplicativos em Java, simplesmente porque existe o C pra isso. Um sniffer de rede ou um software ping, por exemplo, são feitos em C, porque C é pra isso. Já uma interface de um aparelho GPS, é feita em Java. Questão de adeqüação. O mercado de Java é tão grande quanto o de C no mundo, mas é maior no Brasil. No entanto, o que não pode é a faculdade tratar a linguagem C como uma introdução à programação, para que o aluno depois aprenda Java. Uma coisa não tem nada a ver com a outra. São dois nichos completamente diferentes e em ambos os casos, é possível conseguir um bom emprego e alavancar na profissão, tanto aqui quanto em lá fora.

Minha faculdade usa Python para ensinar a programar. É legal?

Claro que não. Python é super divertido e viciante mas não exige os conceitos de computação que todo programador deve ter. A resposta é a mesma para todas as linguagens de alto nível. Como escrevei anteriormente, se começa a programar com uma pseudo-linguagem, para desenvolver a lógica. Antes do estudo de programação médio/alto nível, é preciso estudar computação (que vai incluir Assembly) e aí sim, subir de nível. Se bem gerenciado, é possível manter estas disciplinas em paralelo, mas o programa deve ser cuidadoso (o que as instituições não andam respeitando – Eu já vi projeto de bancos de dados no segundo período. O aluno, teoricamente, nunca usou uma mysql.h ou outras bibliotecas para acesso a SGBD’s em outras linguagens).

Quem aprende direto no alto nível e se dá bem, ótimo – e está de parabéns. Mas o objetivo do artigo é trazer a linguagem C à tona e não competir com outras linguagens.

E o que dá pra fazer com o C aprendido na faculdade?

Só com ele, não muita coisa, mas com um pouquinho de pesquisa e afinco, gera-se resultados. Um exemplo é o grupo Coding 40°, onde eu e mais três alunos do curso de Ciência da Computação nos unimos para estudar e acabamos desenvolvendo um pequeno software, capaz de imprimir a versão de um binário PE na tela. Nada complicado, agora que já está pronto. rs

Sabe quando você está no Windows e vai nas propriedades de um EXE ou DLL e há uma aba “Versão” como na imagem abaixo?

A proposta era criar um software capaz de conseguir essa informação, recebendo como entrada o caminho do arquivo executável. O software deveria funcionar no Linux, já que nesse SO não é possível ver esta aba “Versão” nas propriedades dos executáveis de Windows, obviamente. Foi aí que fizemos o pev. Ele roda em Windows e Linux e cumpre o que promete. O processo de evolução do software está registrado no blog do grupo.

Conclusão

Estudar C, C++, Assembly e outras linguagens tidas como “terríveis” é, sem dúvida, uma boa pedida. Há inúmeros projetos no mundo todo precisando de bons programadores nessas linguagens. Não encare o “C de faculdade” como um vilão ou uma introdução à programação porque não é. A linguagem C é uma linguagem poderosa e comercial. Nada de dizer que C é coisa de maluco. 😉

C programming language back at number 1 position

Fonte: http://www.tiobe.com/index.php/content/paperinfo/tpci/index.html

After more than 4 years C is back at position number 1 in the TIOBE index. The scores for C have been pretty constant through the years, varying between the 15% and 20% market share for almost 10 years. So the main reason for C’s number 1 position is not C’s uprise, but the decline of its competitor Java. Java has a long-term downward trend. It is losing ground to other languages running on the JVM. An example of such a language is JavaFX script that is now approaching the top 20.

The TIOBE Programming Community index gives an indication of the popularity of programming languages. The index is updated once a month. The ratings are based on the number of skilled engineers world-wide, courses and third party vendors. The popular search engines Google, MSN, Yahoo!, Wikipedia and YouTube are used to calculate the ratings. Observe that the TIOBE index is not about the best programming language or the language in which most lines of code have been written.

The index can be used to check whether your programming skills are still up to date or to make a strategic decision about what programming language should be adopted when starting to build a new software system. The definition of the TIOBE index can be found here.

Google Code Jam 2010

Estão abertas as inscrições para o Google Code Jam 2010. O evento promove uma competição entre programadores para a solução de algoritmos complexos dentro de um tempo limitado. Podem participar desde profissionais até estudantes. Os concorrentes também podem escolher a linguagem de programação que desejam participar.

As inscrições iniciou em 07/04, e se estenderá até o dia 08/05. O Code Jam tem alcance global, e suas quatro primeiras etapas são realizadas pela Internet. Os primeiros 25 colocados no ranking, vão para a final, que ocorre presencialmente em Dublin (Irlanda), no dia 30 de julho.

O Google deve distribuir US$25 mil entre os 25 finalistas. O primeiro lugar recebe US$5 mil, o segundo US$2 mil, o terceiro US$ 1 mil e o restante recebe US$100 cada. A inscrições podem ser feitas no site oficial do evento.(http://code.google.com/codejam)

Livros de Programação Gratuitos

Pessoal,

O site Free Programming Books disponibiliza gratuitamente livros de linguagens de programação. Vale a pela dar uma olhada nos links que tem lá e no TOP 20. Os livros estão catalogados por categorias específicas.

Até