Posts Tagged "c"

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

Combinando o Arduino com o AVR Studio 5

avr_logoQuase sempre que faço algo com a IDE do Arduino fico com saudades do auto completar ou algum recurso mais aprimorado de verificação de mapa de memória. Quando se trabalha com códigos pequenos até que vai, mas ao utilizar os códigos muito extensos fica meio cansativo ter que lembrar e copiar cada uma das palavras reservadas do sistema. Também por causa de meu aluno José Carlos, que queria utilizar o AVR para seus experimentos.

Como missão dada é missão cumprida fui em frente. Por isso depois de fazer umas “googadas” por aí descobri um site bem legal, disponível nesse link. Tem alguns tutoriais bem interessantes sobre microcontroladores e IDE´s. E decidi seguir um dos tutoriais de integração do AVR Studio 6 – ferramenta bem legal da atmel que é bem semelhante ao Visual Studio da Microsoft e com alguns recursos de desenvolvimento bem legais (se bem para mim o que valeu mais é não ter mais aquela tela de terminal feia de erro do Arduino GUI). Ele pode ser baixado diretamente nesse link, que após um breve cadastro você está apto a utilizar o software. Também no site vocês vão encontrar uns video aulas bem interessantes nesse aqui http://www.atmel.com/Microsite/atmel_studio6/videos.aspx que de certa forma ajuda a se familiarizar com o ambiente.

Logo de cara tive um problema. A versão do AVR Studio que tenho na minha máquina é a 5, mas lendo o tutorial não muda muita coisa da versão 6 para a anterior. Na verdade não muda nada. Porém na outra máquina que tenho em casa a versão da AVR Studio é a 4. E por isso muita coisa mudou… uma atualização vai bem. Lembrando que estou utilizado o SO Windows 7 – 64 bits, porém creio que funcionará bem no Linux. Ainda não testei mas quem testar me manda um feedback para saber se funcionou. E estou usando uma Arduino MEGA 2560P.

Depois de baixar o AVR Studio, aconselho atualizar o software do Arduino no site:

http://arduino.cc/en/Main/Software

Até o fechamento deste post, verifiquei que já tem a versão 1.0.3 do software da Arduino, porém o utilizado na minha máquina é a versão 1.0.2. A versão é muito importante para utilizar a IDE mais adiante. De certa forma versões anteriores podem ser utilizadas em softwares mais atualizados, mas é bom manter a compatibilidade.

Ao baixar o software da Arduino é interessante deixá-lo na raiz do SO. Renomeie a pasta para “Arduino” então a pastinha irá ficar como a seguir:

 

arduino1

 

 

1º passo – Agora é após baixar o AVR Studio. Execute pela primeira vez e escolha a opção “Tools -> Extension Manager…” Na tela que irá aparecer escolha “Online Galery” e mande instalar o Terminal Windows, como na imagem a seguir:

 

arduino2


 

Durante a instalação pode aparecer alguns erros dizendo que o usuário não tem permissão… não foi possível instalar… etc…. para resolver, feche o AVR Studio e abra novamente o programa só que dessa vez mande executar como administrador e repita o passo anterior.

Agora vamos configurar o ambiente:

2º Passo – Para permitir que o AVR copie a imagem .hex para a placa escolha o menu “Tools -> External tools…” A tela que irá aparecer pede para configurar a ferramenta externa coloque:

Title: Arduino UP

Command: C:\Arduino\hardware\tools\avr\bin\avrdude.exe

Arguments: -CC:\Arduino\hardware/tools/avr/etc/avrdude.conf -v -v -v -v -patmega2560 -cwiring -P\\.\COM2 -b115200 -D -V -Uflash:w:”$(ProjectDir)Debug\$(ItemFileName).hex”:i

Marque a opção “Use Output window

Lembrando que essas opções são para a Arduino Mega. Caso queira saber a versão da sua arduino, aconselho verificar a placa impressa no chip conforme a figura abaixo:

 

 

arduino3


 

Notem que a área de azul tem a versão do chip e a frequencia logo abaixo. Nesse caso o chip é um ATMEGA2560 e a frequência 16MHz.

Caso queira saber o que escrever na tela uma dica bem simples é abrir a IDE original da Arduino e escolher “Arquivo -> Preferências…” e na tela que aparecer deixe conforme a imagem abaixo:

 

arduino4


 

Deixem marcada apenas a opção “carregar”, a de compilação vamos utilizar só mais adiante.

Depois escolha as opções “Arquivo –> Exemplos –> 01.Basics –> Blink“. Tenha certeza que sua placa esteja conectada no computador e as configurações de Placa e Porta Serial estejam corretamente ligadas com a Arduino.

Mande Carregar o projeto na placa. Suba a tela de status e observe as primeiras linhas de carregamento, conforme a imagem abaixo:

 

arduino5


 

As primeiras linhas em branco dizem respeitos aos comandos que devem ser digitados no ambiente do AVR Studio. É só digitar no local informado anteriormente no passo 1 e a única alteração pertinente é ao invés de colocar o caminho do .hex conforme a tela da arduino coloque logo depois do w: a seguinte informação: “$(ProjectDir)Debug\$(ItemFileName).hex”

Não esqueça das aspas. Não feche a IDE da Arduino ainda. Vamos utilizar a mesma no passo seguinte novamente. Feito isso o próximo passo é configurar o ambiente para utilizar a bibliotecas da Arduino.

3º Passo – Dentro da pasta do usuário, no explorer, geralmente o AVR studio cria uma pasta, como: C:\Users\<user>\Documents\AVRStudio 5.1. Dentro dela crie uma pasta chamada ArduinoAVR. Com isso a estrutura no explorer vai ficar: C:\Users\<user>\Documents\AVRStudio 5.1\ArduinoAVR. Neste local que irão ficar todas as referências da IDE Arduino, agora é copiar as bibliotecas para esta pasta. Para saber isso vamos refazer algo bem semelhante ao que fizemos no passo 2. Abra a IDE da Arduino e abra as Preferências. Agora ao invés de marcar Carregar, marque Compilação, conforme imagem abaixo:

 

arduino6


 

Isso é importante pois irá mostrar o caminho no computador das bibliotecas. Mande verificar o mesmo código Blink utilizado no passo 2 e vejas as linhas da tela de status, conforme imagem abaixo:

 

arduino7


 

Notem que essa pasta contem o core da Arduino já configurada para a sua máquina! A uns tempos atrás eu fiz um tutorial de Arduino com Eclipse e tive de gerar esta biblioteca na “mão” para depois utilizá-la. Copiem esse caminho, abra no explorer e copie a biblioteca que tem o nome “core.a” para pasta ArduinoAVR com o nome de “libcore.a“. Isso por causa da IDE que retira o nome lib e a extensão do arquivo em suas compilações.

Agora abra no explorer também a pasta de bibliotecas da Arduino. O caminho é C:\Arduino\hardware\arduino\cores\arduino . Copie todos os arquivos com extensão .h para a pasta ArduinoAVR. Também aproveite e copie o arquivo de pinagem (pins_arduino.h) da sua arduino para a mesma pasta. O caminho C:\Arduino\hardware\arduino\variants . Dentro desta pasta possui várias outras pastas, coloque a pinagem de acordo com sua controladora. Como no meu caso é a ATMEGA, copiei a pinagem da pasta “mega“, se fosse uma Arduino UNO, a pasta seria a “standard“. Copiada as bibliotecas principais, copie agora as secundárias no caminho C:\Arduino\libraries. Pode copiar todas as pastas para a pasta ArduinoAVR. Depois esta pasta irá ficar conforme a imagem abaixo:

 

arduino8


 

Pronto! O próximo passo é criar o projeto.

4º Passo – Abra o AVR Studio 5. Escolha “File –> New –> Project…” Escolha “AVRGCC C++ Executable Project“. O nome coloque qualquer um. No meu caso coloquei TesteArduino. Clique em OK. Na tela da solução, aparece um código que é o padrão do AVR. Pode apagar o mesmo, pois não iremos utilizar. Agora aperte ALT+F7, ou escolha “Project –> Properties…“. Na tela que segue escolha:

Toolchain –> AVR/GNU C Compiler –> Directories. Inclua o caminho da biblioteca C:\Users\<user>\Documents\AVRStudio 5.1\ArduinoAVR e também marque a opção “Relative path

Toolchain –> AVR/GNU C Compiler –> Optimization. Escolha em Optimization Level (Optimization for Size). Marque as 3 primeiras opções (Prepare functions for garbage collection, Pack Structure members, Allocate only as many bytes…) e desmarque a última (Use rjmp/rcall…).

Faça a mesma coisa para a opção Toolchain –> AVR/GNU C ++ Compiler, os passos são os mesmos do anterior. O mesmo ocorre na opção Toolchain –> AVR/GNU C ++ Linker. Inclua em “Libraries” logo abaixo da biblioteca “m” a “libcore”, sem a extensão. e em “Library search path” o caminho C:\Users\<user>\Documents\AVRStudio 5.1\ArduinoAVR e marque a opção “Relative path” e em Toolchain –> AVR/GNU C ++ Linker –> Optimization só deixe marcada a opção “Garbage collect unused sections“. Pronto. Agora salve as alterações. Lembrando que nos caminhos digitados nesse passo onde tem a opção <user> coloque o nome do seu usuário do computador. No meu caso ficaria C:\Users\anderson\… Altere conforme o seu computador.

5º Passo – Digite o seguinte código:

*
 * TesteArduino.cpp
 *
 * Created: 26/12/2012 13:34:08
 *  Author: anderson
 */ 

#define F_CPU 16000000
#define ARDUINO 102
#include "Arduino.h"

int Led = 13;

void setup(){
	pinMode(Led, OUTPUT);
	Serial.begin(9600);
}
void loop(){
	digitalWrite(Led, HIGH);
	Serial.println("LIGADO");
	delay(1000);
	digitalWrite(Led, LOW);
	Serial.println("APAGADO");
	delay(1000);
}

Salve o arquivo. Lembrando que em todo projeto que fizer deve incluir as seguintes linhas:

#define F_CPU 16000000
#define ARDUINO 102
#include "Arduino.h"

A opção F_CPU deve ser de acordo com sua placa, se é 4, 6, 8, 16, etc… A linha do #define ARDUINO 102, diz respeito a versão da arduino baixada. Como anteriormente disse que baixei a versão 1.02, então essa opção deve ser 102, se for a versão 1.03, coloque 103 e assim por diante.  Agora está pronto! Salve o projeto e mande compilar. Aperte F7 ou em “Build –> Build Solution“. Se tudo ocorrer sem erros, vai aparecer a mensagem de 1 solução compilada com sucesso. Depois escolha a opção “Tools” e logo acima da opção External Tools… terá a opção “Arduino UP“. Clique nela e aguarde a mensagem avrdude.exe  done.  Thank you. Pronto! Você verá o blink funcionando na sua Arduino e terá um ambiente de testes extremamente completo (auto completar, debug, terminal, biblioteca de processadores, etc…). Caso queira utilizar um Terminal Serial escolha a opção “View –> Terminal Window” Escolha a porta que está conectada a Arduino e Aperte o botão “Connect“, que está logo ao lado da porta. Qualquer dúvida mande um comentário ou uma mail. Os arquivos utilizados nesse tutorial estão logo abaixo:

ArduinoAVR

ModeloEclipse

 

 

Pilha – Estrutura de Dados usando C

Sempre que falo sobre pilhas em minhas aulas os alunos ficam confusos e as vezes não entendem que essa é uma parte fundamental do entendimento de estrutura de dados. Não falo de pilha como a da figura ao lado mas sim uma das várias estruturas de dados que admitem remoção de elementos e inserção de novos elementos.

Mais especificamente, uma  pilha (também chamado de stack)  é uma estrutura sujeita à seguinte regra de operação: sempre que houver uma remoção, o elemento removido é o que está na estrutura há menos tempo. Em outras palavras, o primeiro objeto a ser inserido na pilha é o último a ser removido. Essa política é conhecida pela sigla LIFO (Last-In First-Out).

O uso de pilha está diretamente ligado ao uso de vetores. Suponha que nossa pilha está armazenada em um vetor  pilha[0..n-1] Suponha ainda que os elementos de pilha são números inteiros. (Isto é só um exemplo; os elementos de pilha poderiam ser objetos de qualquer outro tipo.) A parte do vetor ocupada pela pilha será

pilha[0..t-1] .

0 t N-1

 

O índice t indica o topo (top) da pilha.  Esta é a primeira posição vaga da pilha.  A pilha está vazia se t vale 0 e cheia se t vale n. Para remover um elemento da pilha — esta operação é conhecida como desempilhar (to pop) — faça

   x = pilha[--t];

Isso equivale ao par de instruções  “t -= 1; x = pilha[t];”  nesta ordem.  É claro que você só deve desempilhar se tiver certeza de que a pilha não está vazia. Para consultar a pilha sem desempilhar faça x = pilha[t-1].

Para inserir — ou seja, para empilhar (= to push) — um objeto y na pilha faça

   pilha[t++] = y;

Isso equivale ao par de instruções  “pilha[t] = y; t += 1;”  nesta ordem.  Antes de empilhar, verifique se a pilha já está cheia para evitar que ela transborde (ou seja, para evitar um overflow).  Em geral, a tentativa de inserir em uma pilha cheia é uma situação excepcional, que indica um mau planejamento lógico do seu programa.

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

Informação – Turma de Arquitetura de Computadores (20112.Y6-RC.2M)

Pessoal de Arquitetura,

O e-mail do monitor da disciplina Ítalo é

Informo também que os projetos já estão no site. E com os respectivos grupos. Mais informações clique aqui.

Variáveis e valores em C (parte 1)

É difícil você entrar em sala de aula e ver alunos das disciplinas de Sistemas Operacionais e Arquitetura não saberem trabalhar direito com variáveis em C/C++. Necessária durante a época da graduação, a linguagem de programação, além de ajudar o alunado em suas questões do dia-a-dia, também auxilia na parte didática e lógica. Alunos devem entender  que não existem só valores inteiro não, podem existir os valores contendo parte fracionária que são, impropriamente,  chamados em C e em outras linguagens de programação de valores reais. O impropriamente é porque qualquer que seja o número que representemos dentro de uma variável, sua precisão será sempre finita. Sempre terá um número finito de dígitos, o que não é exatamente o caso dos números reais em matemática. Melhor os chamarmos de valores com parte fracionária, ou mesmo valores em ponto flutuante, aludindo ao fato que os mesmos podem ser representados na forma M*10E , isto é, mantissa M multiplicada por 10 elevado a um expoente E. O ponto decimal flutua, dependendo do expoente considerado.

Exemplos:

1.2  = 1.2 * 100 ,ou 0.12 * 101 , ou 12 * 10-1, ou ainda 0.0012 * 103

Constantes em ponto flutuante em C

Podem ser representadas com ponto decimal ou em notação exponencial.

Exemplos:

  1. 1.2
  2. 0.12e1 ( o mesmo que 0.12 * 101)
  3. –54.897
  4. –467.8907e12 ( o mesmo que –467.8907 * 1012)
  5. 2.45e0 ( o mesmo que 2.4 * 100)
  6. 23.0

Variáveis em ponto flutuante

São declaradas como float ou double

Float (de ponto flutuante) – contém valores no intervalo –3.4 * 1038 a 3.4 * 1038

O valor positivo mais próximo de zero é 3.4 * 10-38

Double (dupla) – contém valores no intervalo –1.7 * 10308 a 1.7 * 10308

O valor positivo mais próximo de zero é 1.7 * 10-308

Cuidado: nem todos os valores destes intervalos podem ser representados. Existem infinitos valores no intervalo e só são representados aqueles cuja precisão (quantidade de dígitos significativos) cabem dentro de uma variável do tipo correspondente.

Exemplos:

float x,y;
double a,b,c;

Fazendo um resumo de todos os tipos básicos da linguagem C:

Tipos básicos

 

tipo tamanho Description
char

1

Tipo caracter. Pode ser com ou sem sinal (unsigned).
short

2

Inteiro armazenado em 16 bits. Pode ser com ou sem sinal.
int

4

Inteiro aramazenado em 32 bits. Pode ser com ou sem sinal.
long

4

Idêntico ao int.
long long

8

Inteiro armazenado em 64 bits. Com ou sem sinal.
float

4

Ponto flutuante com precisão simples.
double

8

Ponto flutuante em precisão dupla

 

Exemplos:

char b;
unsigned char c;
short c;
unsigned short d;
int e;
unsigned int f;
long g;
unsigned long h;
long long i;
unsigned long long j;
long int k;
unsingned long int l;
float m;
double m;

Funções intrínsecas

Existem no C várias funções pré-definidas. Tais funções podem ser usadas nas expressões aritméticas de um programa. Para isso basta incluir (#include) o arquivo de definições adequado.

Abaixo algumas delas. Para ver uma lista completa, consulte o help do compilador.

 

função e tipo Resultado #include
int abs(int i) | x | – valor absoluto de i <stdlib.h>
int rand (void) um número aleatório <stdlib.h>
double cos (double x) cos x <math.h>
double sin (double x) sen x <math.h>
double tan (double x) tan x <math.h>
double fabs (double x) | x | – módulo de x <math.h>
double exp (double x) ex <math.h>
double log  (double x) Ln x <math.h>
double log10 (double x) Log x (base 10) <math.h>
double acos (double x) Arc cos x <math.h>
double asin (double x) Arc sen x <math.h>
double atan (double x) Arc tan x <math.h>
double sqrt (double x) raiz quadrada de x <math.h>
double cbrt (double x) raiz cúbica de x <math.h>
double pow (double x, double y) xy <math.h>

 

Exemplos:

int a,b;
double x, y, z;
a = abs(i);
a = rand();
z = sqrt(x) + cbrt (x);
z = sin(x) * cos(y) + tan (x+y);
if (exp(x) > 3.1416) printf(“logaritmo de x = %12.5f”, log10(x));
else printf(“logaritmo neperiano de x = %12.5f”, log(x));
z = atan(x+y) + acos(x)/asin(y)
z = pow(x, fabs(y));

O formato f para valores float

 O formato f é usado para imprimoir valores reais. Pode-se especificar o número de casas com que os valores são impressos. Exemplos:

%f – imprime o valor com o comprimento necessário

%12f – imprime o valor com 12 posições. Nestas 12 posições estarão o ponto decimal e 6 casas decimais.

%12.5f – imprime o valor com 12 casas sendo 5 decimais

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

// exemplos do formato f

int main() {
float x;
// entre com x
printf("digite o valor de x:");
scanf("%f", &x);
// imprime x com vários formatos
printf("\n%f", x);
printf("\n%12f", x);
printf("\n%10f", x);
printf("\n%12.5f", x);
printf("\n%10.5f", x);
system(“pause”); return 0;
}

Será impresso:

digite o valor de x: 3.1415

3.1415

    3.141600

  3.141600

     3.14160    

   3.14160

O formato %lf para valores double

Idem ao formato f. Exemplo:

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

// exemplos do formato lf
int main() {
double x;
// entre com x
printf("digite o valor de x:");
scanf("%lf", &x);
// imprime x com vários formatos
printf("\n%lf", x);
printf("\n%12lf", x);
printf("\n%10lf", x);
printf("\n%12.5lf", x);
printf("\n%10.5lf", x);
system(“pause”); return 0;
}

Será impresso:

digite o valor de x:-12.34

-12.340000

-12.340000

-12.34000

-12.34000

-12.34000

No próximo post vamos ver misturas de tipos de expressões.

Cygwin + MinGW + Eclipse CDT + Windows

Será que dá certo juntar isso tudo? claro que sim. Tive de fazer uma “gambiarra” esses dias já que trabalho muito mais no Windows do que no Linux. Até ando com dois notebooks, um com Windows e outro com Linux, mas sempre que preciso utilizar algo que seja feito em C/C++ padrão POSIX tenho de sempre utilizar um pendrive para mover de um canto a outro. Isso porque o Eclipse CDT para Windows, geralmente usa o MinGW, que não é padrão POSIX e sim Win32. Para resolver este problema vai um tutorial que mostra como instalar o Eclipse CDT (o que utilizo para C/C++) e de quebra poder utilizar o Cygwin e o MinGW no Windows. Então vamos lá.

1º) Baixe o Cygwin, MinGW e Eclipse Helios. O processo de instalação de cada um dos pacotes, fica por conta de vocês procurarem na Internet, mas vai algumas dicas. Descompacte o arquivo do Eclipse, eclipse-cpp-helios-SR2-win32.zip no C:\. Assim como o MinGW e o Cygwin. Um tutorial bem legal de instalação do Cygwin é o do Professor Gil Gonçalves, disponível nesse link.  Inclusive este é o tutorial que usei para instalar o Cygwin aqui na minha máquina, mas usei os fontes da Internet ao invés do que ele sugere no site.

2º) Agora coloque os caminhos do Cygwin e do MinGW no PATH do Windows. Para isso, clique com o botão direito, no meu computador e escolha “Propriedades”

Na tela que aparecer escolha no lado direito a opção “Configurações Avançadas do Sistema”. Na aba “Avançado”, clique no botão “Variáveis de Ambiente…” e em “Variáveis do sistema”, escolha a opção PATH e clique em editar conforme figura a seguir:

Acrescente no final da edição do PATH os caminhos dos binários do MinGW e do Cygwin que são:

C:\MinGW\bin

e

C:\Cygwin\bin

Obs.: Se você usar algum tutorial de instalação do MinGW, vai notar que o Msys que vem junto com ele deve ser colocado o caminho de binários do mesmo, mas no nosso caso, como vamos utilizar o Cygwin, não tem necessidade de colocar os binários do Msys no PATH.

Para testar abra um shell do Windows indo em executar e digite

cmd

Deve aparecer uma tela preta do shell, então digite os seguintes comandos:

gcc

e logo após

bunzip2

Deve aparecer uma resposta conforme figura abaixo.

Isso indica que está buscando o gcc no caminho de executáveis do MinGW e o bunzip2 do caminho de executáveis do Cygwin.

Também execute o Cygwin e o MynGW Shell para configurar as preferências do usuário. Isso é essencial para o passo seguinte. Agora vamos a configuração do Eclipse.

3º) Abra o eclipse e escolha um caminho para o Workspace. Aqui deve colocar o caminho da <HOME> da MinGW conforme figura:

Observe que coloquei uma pasta chamada workspace, dentro da <HOME> do meu usuário.

4º) Agora vamos a configuração do Eclipse. Na tela inicial escolha “Window” -> “Preferences”, depois escolha a opção “C/C++” -> “New CDT Project wizard”, onde tem a aba “Preferred toolchain” e “Wizard defaults” coloque conforme as figuras abaixo:


Observe que as opções “Show project types…” e “Show only suported toolchain” estão desmarcadas. Dessa forma na criação de um novo projeto poderá ser visualizada a opção de criar um projeto com Cygwin.

5º) Vamos aos testes. Os testes a seguir foram feitos utilizando o código “processo.c” que está nesse link. Vamos simular a execução do programa com o MinGW e com o Cygwin e ver o que acontece com cada um deles. Primeiro crie um novo Projeto C no Eclipse. Para isso escolha “File” -> “New” -> “C Project”. Na tela que aparecer escolha, dê um nome ao projeto, no nosso caso colocamos “PROCESS_2” e escolhemos a opção de “Executable” -> “Empty Project” -> “MinGW GCC”. Depois escolha “Next” e após “Finish”. Agora escolha “File” -> “New” -> “Source File”. Coloque o nome do código de processo.c, conforme figura abaixo:

 

Agora coloque o conteúdo conforme o arquivo processo.c baixado anteriormente. Mande salvar com apertando CTRL+S e depois compilar com CRTL+B. Note que aparece uma mensagem de erro dizendo;

Description    Resource    Path    Location    Type
undefined reference to `fork'    processo.c    /PROCESS_2    line 27    C/C++ Problem
undefined reference to `sleep'    processo.c    /PROCESS_2    line 46    C/C++ Problem

Isso porque o programa foi feito fora do padrão POSIX. Apague esse projeto e vamos criar agora um novo projeto e repetir os passos anteriores, só que agora vamos escolher para isso escolha “File” -> “New” -> “C Project”. Na tela que aparecer escolha, dê um nome ao projeto, no nosso caso colocamos “PROCESS_2” e escolhemos a opção de “Executable” -> “Empty Project” -> “Cygwin GCC”. Agora antes de compilar o programa. Vamos clicar com o botão direito em cima de PROCESS_2 e escolher “Properties” e selecione “Environment” conforme figura.

 

O lance agora é colocar o caminho do Cygwin no PATH do eclipse, coisa que não vem por padrão! Para isso clique em PATH e escolhe “Edit…”. Na tela que aparece coloque no início do arquivo a opção C:\cygwin\bin, conforme figura:

Agora clique em OK, depois OK novamente e mande compilar o programa com CRTL+B. Notem agora que não tem mais mensagens de erro no mesmo programa! Mande executar o programa, escolhendo a opção “Run” -> “Run as…” -> “C/C++ Application”. Vai aparecer a seguinte mensagem no Console do Eclipse:

Comecando o programa PROCESS_2 ...
Valor de pid 5796   Processo pai ...
Valor de pid 5796   Processo pai ...
Valor de pid 5796   Processo pai ...
Terminando o programa PROCESS_2 ...
Valor de pid 0                      Processo filho...
Valor de pid 0                      Processo filho...
Valor de pid 0                      Processo filho...
Valor de pid 0                      Processo filho...
Valor de pid 0                      Processo filho...
Terminando o programa PROCESS_2 ...

Isso é tudo. Dúvidas ou sugestões serão bem vindas!

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. 😉