Professional Documents
Culture Documents
PIC
Apostila do Aluno
1. Introduo
A linguagem C, uma linguagem adequada programao estruturada, foi criada por Dennis Ritchie, em 1972. Com o sucesso desta linguagem em 1980 j existiam vrios compiladores C, desta forma a linguagem no se restringia ao UNIX, sistema operacional que foi reescrito em C. As caractersticas da linguagem C so: portabilidade, modularidade, compilao separada, recursos de baixo nvel, gerao de cdigo eficiente, confiabilidade, regularidade, simplicidade e facilidade de uso. A linguagem C segue algumas etapas para a gerao do executvel, estas etapas so:
Lincador (executvel)
Figura 01
E para cada um destes tipos existem seus modificadores, desta forma podendo restringir ou ampliar sua faixa de valores: signed unsigned long short
Os modificadores no so aplicados como um todo para cada tipo, o float no pode ser modificado, double pode ser modificado apenas pelo long e o int pode ser modificado por todos. Tipos int assumem o tamanho indicado pela maquina, ou seja, se a maquina for de 16 bits o int provavelmente ter 16 bits, em caso de uma maquina de 32 bits o mesmo ter 3 bits. Os modificadores short e long possuem algumas restries: short int e int devem ocupar pelo menos 16 bits long int pelo menos 32 bits short int no pode ser maior que int, que no pode ser maior que long int.
Unsigned serve para indicar que os valores so sem sinais. Os inteiros que so modificados usando o unsigned podem assumir apenas valores positivos. Para uma compreenso maior do tamanho que cada tipo ter ou suas faixas de valores vide anexo. A linguagem ANSI C tambm permite que o usurio crie seus prprios tipos de dados usando estruturas, unies e enumeraes. Uma estrutura o agrupamento de varias varivel e para declarar uma estrutura usamos a palavra-chave struct e os elementos que iro compor esta estrutura so chamados de membros, estes membros no necessariamente devem ser do mesmo tipo.
#include <stdio.h> struct Ponto{ int x; int y; char z; }; int main() { struct Ponto p1; struct Ponto p2; struct Ponto p3 = {10,11,'w'}; /*Inicializao dos valores*/
Console:
p1. x:1 / p1.y:2 / p1.z:z p2. x:1 / p2.y:2 / p2.z:z p3. x:10 / p3.y:11 / p3.z:w
Quando as variveis do tipo da struct so declaradas necessrio adicionar a palavra-chave struct para que indique que aquele tipo refere-se a um tipo de dado criado pelo programador, isto pode ser suprido caso seja usado o typedef e desta forma adicionando o nome na criao da struct.
#include <stdio.h> typedef struct { int x; int y; char z; }Ponto; int main() { Ponto p1; Ponto p2; Ponto p3 = {10,11,'w'}; ... return(0); }
Console:
p1. x:1 / p1.y:2 / p1.z:z p2. x:1 / p2.y:2 / p2.z:z p3. x:10 / p3.y:11 / p3.z:w
PROJETO AGIT | PIC Uma unio bem parecida com uma estrutura, mas pela diferena a struct aloca um espao de memoria igual soma de todos os tipos de seus elementos, enquanto a union aloca espao de memoria igual ao tamanho do elemento de maior tipo e para sua declarao usamos a palavra-chave union.
#include <stdio.h> union Ponto{ int x; int y; char z; }; int main() { union Ponto p1; union Ponto p2; union Ponto p3 = {10,11,'w'}; /*Inicializao dos valores*/ p1.x = 1; p1.y = 2; p1.z = 'z'; /*Outra maneira de inicializar os valores*/ printf("p1. x:%d / p1.y:%d / p1.z:%c \n",p1.x,p1.y,p1.z); /*Copia da struct p1 para a p2*/ p2 = p1; printf("p2. x:%d / p2.y:%d / p2.z:%c \n",p2.x,p2.y,p2.z); printf("p3. x:%d / p3.y:%d / p3.z:%c \n",p3.x,p3.y,p3.z); return(0); }
Console:
p1. x:122 / p1.y:122 / p1.z:z p2. x:122 / p2.y:122 / p2.z:z p3. x:10 / p3.y:10 / p3.z:
Enumerao possui um nmero de valores finitos definidos pelo programador. A varivel que for declarada com um tipo enumerador s poder assumir os valores conhecidos pelo enumerador. Para criar um enumerador usamos palavra-chave enum.
#include <stdio.h> typedef enum{ VERDE, AZUL, ROXO, AMARELO, VERMELHO, }cores; /* /* /* /* /* 0 1 2 3 4 */ */ */ */ */
tipo_cores = AMARELO; if(tipo_cores if(tipo_cores if(tipo_cores if(tipo_cores if(tipo_cores return(0); } == == == == == 0){printf("COR 1){printf("COR 2){printf("COR 3){printf("COR 4){printf("COR ATRIBUIDA ATRIBUIDA ATRIBUIDA ATRIBUIDA ATRIBUIDA VERDE!!!!\n");} AZUL!!!!\n");} ROXO!!!!\n");} AMARELO!!!!\n");} VERMELHO!!!!\n");}
Console:
COR ATRIBUIDA VERDE!!!! COR ATRIBUIDA AMARELO!!!!
Enumerao no possui declarao de tipos nos membros, no so divididos usando ; e a ordem de declarao definir o valor de cada membro caso o programador no defina um valor default.
#include <stdio.h> typedef enum{ VERDE, AZUL = 10, ROXO, AMARELO = 15, VERMELHO, }cores; int main() { cores tipo_cores; tipo_cores = ROXO; if(tipo_cores if(tipo_cores if(tipo_cores if(tipo_cores if(tipo_cores == == == == == 0){printf("COR ATRIBUIDA VERDE!!!!\n");} 10){printf("COR ATRIBUIDA AZUL!!!!\n");} 11){printf("COR ATRIBUIDA ROXO!!!!\n");} 15){printf("COR ATRIBUIDA AMARELO!!!!\n");} 16){printf("COR ATRIBUIDA VERMELHO!!!!\n");} /* 0 */ /* 11 */ /* 16 */
tipo_cores = VERMELHO;
Console:
COR ATRIBUIDA ROXO!!!! COR ATRIBUIDA VERMELHO!!!!
#include <stdio.h> int main() { int _valor_int_1 = 0; int valor_int_2 = 0; float valor_float_1 = 0.0; char valor_char_1 = 'a'; }
Existem algumas boas praticas para a elaborao do nome da varivel: 1 regra) O nome deve comear com uma letra ou sublinhado ( _ ) e os outros caracteres que compem o nome devem ser letras, nmeros ou sublinhado ( _ ). 2 regra) O nome da varivel no pode ser o mesmo que uma palavra reservada da linguagem ANSI C ou tambm um nome de funo declarada pelo programador ou
PROJETO AGIT | PIC existente em alguma biblioteca do ANSI C, para verificar as palavras reservadas existentes no padro ANSI C vide anexo. No podemos considerar que toda varivel declarada ser iniciada com um valor prdefinido pelo compilador. Em geral costuma-se assumir que uma varivel recm declarada automaticamente iniciada com zero ou vazio: Isto no e verdade. Uma varivel recm declarada ira ocupar uma regio de memria e conter o valor daquela regio, que pode ser desde zero, vazio ou qualquer outro valor. necessrio, ento, que inicialize a mesma. O operador de atribuio =.
#include <stdio.h> int main() { int _valor_int_1 = 0; int valor_int_2 = 0; float valor_float_1 = 0.0; char valor_char_1 = 'a'; }
Para um bom entendimento durante a leitura do cdigo, boas praticas podem ser adotadas: usar os nomes das variveis somente em minscula e para as constantes somente maisculas, lembrando que a linguagem ANSI C case sensitive, o que significa que diferencia entre letras maisculas e minsculas.
#include <stdio.h> int main() { int valor1 = 0; int valor2 = 0; int valor3 = valor1+VaLoR2 */ } /* varivel VaLoR2 no existe
Console:
PROJETO AGIT | PIC Outro tipo de declarao a de constantes, ou seja, seu valor considerado fixo pelo compilador e no mudar em tempo de execuo. Constantes podem ser dos tipos convencionais, hexadecimais, octais, string e barra invertida. Para declarar uma constante podemos usar const ou a diretiva #define.
#include <stdio.h> #define CONSTANTE 100 int main() { const int valor1 = CONSTANTE; int valor2 = 1; int valor3 = valor1+valor2; */ printf("valor3: %d",valor3); return(0); } /* varivel VaLoR2 no existe
Console:
valor3: 101
3. Operadores
Os operadores na linguagem ANSI C so usados para realizar operaes aritmticas, comparaes e expresses.
3.1.
Precedncia de operadores
Em ANSI C existe precedncia entre os operadores. A tabela abaixo exibe estes operadores. Maior precedncia
() [] -> ! ~ ++ -- . -(unrio) (cast) *(unrio) &(unrio) sizeof
Menor precedncia
Esta precedncia define qual a ordem que os operadores sero executados e esta precedncia pode alterar o resultado da expresso que estiver sendo montada. ...
result result result result = = = = 2+5*7; /* Resultado da expresso 37*/ (2+5)*7; /* Resultado da expresso 49*/ 2+2+2+2/4; /* Resultado da expresso 6,5 como o tipo int ser exibido 6*/ (2+2+2+2)/4; /* Resultado da expresso 2*/
...
Console:
2+5*7 = 37 (2+5)*7 = 49 2+2+2+2/4 = 6 (2+2+2+2)/4 = 2
Para alterar a ordem de precedncia podemos usar os parnteses ( e), assim tornando as expresses mais legveis. A Linguagem ANSI C avalia os tipos de dados envolvidos em determinada expresso. Quando forem diferentes entre eles, avalia se possvel convert-los. Caso a converso seja possvel, ser feita, a converso do contrario exibido uma mensagem de erro pelo compilador. As regras para converso so: 1) char convertido para int 2) float convertido para double 3) se um dos valores for long double ento o resultado ser convertido long double 4) se um dos valores for double ento o resultado ser convertido double 5) se um dos valores for long ento o resultado ser convertido long 6) se um dos valores for unsigned ento o resultado ser convertido unsigned
3.2.
Operadores Aritmticos
Os operadores aritmticos so os mesmos para muitas linguagens, estes so usados para realizar operaes matemticas. Os operadores em ANSI C so: + ADIO (inteiro e ponto flutuante)
PROJETO AGIT | PIC - SUBTRAO (inteiro e ponto flutuante) * MULTIPLICAO (inteiro e ponto flutuante) / DIVISO (inteiro e ponto flutuante) % RESTO DA DIVISO (inteiros) ++ INCREMENTO (inteiro e ponto flutuante) -- DECREMETNO (inteiro e ponto flutuante)
#include <stdio.h> int main() { ... result result result result result result result result result result result result result ... } = 2 + 2; //Adio de inteiros += 2; // result = result + 2; = 2.2 + 2.2; //Adio de ponto flutuante = 2 - 2; //Subtrao de inteiros -= 2; //result = result 2; = 2.2 - 2.2; //Subtrao de ponto flutuante = 2 * 2; //Multiplicao de inteiros *= 2; //result = result *2; = 2.2 * 2.2; // Multiplicao de ponto flutuante = 2 /2; //Diviso de inteiros /= 2; //result = result/2; = 2.2/2.2; //Diviso de ponto flutuante = 2 %2; //Resto da diviso de inteiros
3.3.
Os operadores lgicos e relacionais realizam comparaes entre variveis e valores, variveis e variveis, variveis e constantes, valores e constantes e por fim constantes e constantes. Estes operadores tambm se mantem para vrias linguagens. Em ANSI C eles so: Operadores relacionais: > maior que < menor que >= maior ou igual que <= menor ou igual que == igual que != no igual
printf("Falso: %d \n", valor1 > valor2); printf("Falso: %d \n", valor1 < valor2); result = 2 > 3; printf("Falso: %d \n", result); result = 2 < 3; printf("Verdadeiro: %d \n", result); result = 2 <= 3; printf("Verdadeiro: %d \n", result); result = 2 <= 2; printf("Verdadeiro: %d \n", result); result = 2 >= 3; printf("Falso: %d \n", result); result = 2 >= 2; printf("Verdadeiro: %d \n", result); result = 2 == 2; printf("Verdadeiro: %d \n", result); result = 2 == 3; printf("Falso: %d \n", result); result = 2 != 3; printf("Verdadeiro: %d \n", result); result = 2 != 2; printf("Falso: %d \n", result); return(0); }
Console:
VERDADEIRO Falso: 0 Falso: 1 Falso: 0 Verdadeiro: Verdadeiro: Verdadeiro: Falso: 0 Verdadeiro: Verdadeiro: Falso: 0 Verdadeiro: Falso: 0
1 1 1 1 1 1
Em ANSI C, valores booleanos como Verdadeiro (true) e Falso (false) so reconhecidos como 0 (verdadeiro) e 1 (falso).
Operadores lgicos: && || ! AND - todas as condies devem ser verdadeiras OR - apenas uma condio deve ser verdadeira NOT - inverte o resultado da condio
#include <stdio.h> int main() { int valor1 = 0; int valor2 = 1; printf("%d printf("%d AND %d %d\n", valor1,valor2, valor1 && valor2); OR %d %d\n", valor1,valor2, valor1 || valor2);
if(valor1 < 1 && valor2 > 0){printf("Ambas as condies so verdade\n");} if(valor1 < 1 || valor2 <= 0){ printf("Uma condio verdade\n");} if(!(valor1 > 1)){ printf("Condio falsa, mas o NOT inverte para verdadeiro\n");} if(!(valor1 < 1)){printf("Condio verdadeira, mas o NOT inverte para falso\n");}/*No executa pois foi invertido para falso*/ return (0); }
Console:
0 AND 1 0 0 OR 1 1 Ambas as condies so verdade Uma condio verdade Condio falsa, mas o NOT inverte para verdadeiro
3.4.
Operadores bit-a-bit
Podemos realizar operaes lgicas bit-a-bit, ou seja, o nmero e reconhecido por sua forma binaria e ento executada a operao em cada um de seus bits. & AND - todas as condies devem ser verdadeiras | OR - apenas uma condio deve ser verdadeira ^ XOR - inverte o resultado da condio ~ NOT << Deslocamento de bits a esquerda >> Deslocamento de bits a direita
3.5.
Operador cast
Podemos converter o resultado de uma expresso para o tipo que queremos. Isto possvel usando cast ou modelador.
#include <stdio.h> int main() { int x,y; x=y=2; printf("%d / %d / 3 = %f",x,y,(float)x/y/3); return (0); }
Console:
2 / 2 / 3 = 0.333333
4.1.
Entrada de dados
Os comandos de entrada so: scanf, getchar,getch,getche e gets. Vamos explicar e exemplificar cada um deles, mas antes uma diferena dentre estes comandos. Alguns comandos so usados para receber strings, ou seja, sequncia de caracteres. Outros so usados para receber apenas um nico caractere. Outros, ainda, podem ser formatados para receber outros tipos de dados. O comando scanf usado para ler um valor de entrada. Deve-se especificar a varivel que ir receb-lo e seu tipo, ou seja, no prprio comando definido o tipo de dado de entrada. Em resumo com a funo scanf possvel formatar a entrada que ser recebida.
return (0); }
Console:
Digite dois caracteres:qq Digite dois nmeros inteiros:12 -1 Um ou mais valores so menores que zero!!!!!! Os nmeros digitados foram: 12 -1
No scanf indicamos o endereo da varivel que armazenar o valor digitado, para isto usado o &.
PROJETO AGIT | PIC Quando codificamos &x e &y estamos dizendo a posio e memria, ou seja, o endereo onde o valor esta armazenada e quando codificamos x e y estamos acessando o valor da posio de memria. Vamos ilustrar de uma forma computacional:
Aps digitar os valores usando o endereo da varivel ento conhecida a posio de memria que ira armazenar o valor digitado. A figura acima ilustra a hiptese de terem sido digitados os valores 5, para a varivel y, e 3 para a varivel z. Onde 1001002 referem-se a C4 e 1001004 refere-se E7. Importante: No confundir o & de endereo de memria com o & operador bit-abit AND. Outra funo de entrada usada para ler caracteres o getchar, que a funo original do UNIX. Ela armazena o valor digitado em uma rea de memoria intermediaria at que a tecla Enter seja pressionada, e ento retorna o contedo digitado.
int main() { char ch; printf("Digite um caractere:\n"); fflush(stdout); ch = getchar(); printf("O caracter digitado foi: %c \n", ch); return (0); }
Console:
Com a funo getchar temos um problema em potencial no padro ANSI C, pois esta funo deveria ser implementada de uma forma iterativa, mas isto raramente ocorre. Ento esta funo seguiu o padro UNIX, de onde originaria, onde existia uma rea de memria (buffer) linear para os terminais de entrada, onde os valores digitados eram armazenados at que a tecla ENTER fosse pressionada e aps isto a entrada era enviada para o computador. Isto causa um problema para ambientes iterativos, pois ficam caractres neste buffer esperando o ENTER ser pressionado. recomendado que usemos duas alternativas para a funo getchar: So as funes getch e getche. Os prottipos para estas funes esto definidas na biblioteca CONIO.H. A funo getch captura os caractres inseridos sem exibi-los enquanto que a funo getche tem o mesmo funcionamento, mas com o diferencial de exibir o que foi digitado na tela. As funes mencionadas so usadas especificamente para capturar caracteres. Para que seja possvel capturar uma cadeia de caracteres necessrio usar uma repetio de fluxo, ou seja, um loop.
#include <stdio.h> int main() { char c[10],c1; int i;
printf("Digite dois valor inteiro:"); fflush(stdout); for(i=0; i<10;i++){ scanf("%c", &c1); fflush(stdin); c[i] = c1; } c[10] = '\0'; printf("String digitada: %s", c); return (0); }
Console:
Digite dois valor inteiro:a a a a
#include <stdio.h> int main() { char c[10],c1; printf("Digite dois valor inteiro:"); fflush(stdout); for(int i=0; i<10;i++){ c[i] = getchar(); fflush(stdin); } printf("String digitada: %s", c); return (0); }
Console:
Digite dois valor inteiro:a a a a a a a a a a String digitada: aaaaaaaaaa
Podemos usar a funo gets para capturar uma cadeia de caracteres digitada, ou seja, uma string.
#include <stdio.h> #include <string.h> int main() { int x,y,z; char str[100]; printf("Digite uma frase:"); gets(str); printf("A frase digitada foi -> %s e possui a quantidade de caracteres: %d",str, strlen(str));
Console:
Digite uma frase: Testando A frase digitada foi -> Testando e possui a quantidade de caracteres: 8
A funo gets armazena a entrada no endereo de memria indicado em seu parmetro e a funo retorna um ponteiro, Figuras 1 e 2, para a mesma posio de memria. O prottipo desta funo existe na biblioteca STDIO.H.
4.2.
Sada de dados
Os comandos de sada so: printf e puts e vamos explicar e exemplificar cada um deles, mas antes uma diferena dentre estes comandos. A funo puts usada para exibir strings, mas no possvel formatar sua sada ou imprimir valores, por este motivo sua execuo acaba sendo mais rpida em comparao a funo printf que possvel formatar a sada.
#include <stdio.h> #include <string.h> int main() { int x,y,z; char str[100]; printf("Digite uma frase:"); gets(str); printf("A frase digitada foi -> %s e possui a quantidade de caracteres: %d",str, strlen(str)); return (0); }
A funo printf nos permite aumentar a preciso do valor que ser exibido isto possvel incluindo no campo de formato, indicado pela porcentagem (%).
#include <stdio.h> int main() { float x = 2.22; char y[] = "Teste_teste"; printf("%f \n",x); printf("%.4f \n",x); preciso .4*/ printf("%-4f \n",x); 4*/ printf("%04f \n",x); 04*/ printf("%5.7f \n",x); /*Especificador de /*Especificador de preciso /*Especificador de preciso /*Especificador de preciso
Console:
2.220000 2.2200 2.220000 2.220000 2.2200000 Teste_t
O especificador de preciso usado para definir a largura mnima de um determinado campo a partir de seu tipo de dado. O especificador de preciso pode ser identificado verificando o campo de formato, mais especificamente o valor entre o porcentagem e o tipo de dado.
5. Fluxo de Execuo
Podemos mudar o fluxo de execuo do programa que ser executado de acordo com uma condio, repetio ou salto.
5.1.
Desvios condicionais
Em ANSI C temos os comandos de desvios condicionais if e switch, ambos funcionam de forma similar. O comando if necessita de uma expresso que, caso seja satisfeita, o comando ou o bloco de comandos sero executados, ou seja, o fluxo de comando ira sofrer um desvio ou no.
#include<stdio.h> int main(int argc, char** argv) { int pos = 0; do{ printf("Digite um numero entre 0 e 10:"); scanf("%d", &pos); if(pos>0 && pos<10){ puts("Valor no esta no intervalo de 0 at 10. \nPor favor, digite novamente."); } }while(pos < 0 && pos > 10); printf("Valor digitado foi: %d", pos);
Uma vez que a expresso verdadeira ento o bloco de funo associado ao comando if ter seu contedo executado, caso a expresso seja falsa ento no ser executado e o comando else ser executado. Lembrando que o comando else no obrigatrio.
#include<stdio.h> int main(int argc, char** argv) { int pos = 0; do{ printf("Digite um numero entre 0 e 10:"); scanf("%d", &pos); if(pos < 0 && pos >10){ puts("Valor no esta no intervalo de 0 at 10. \nPor favor, digite novamente."); }else{ puts("Valor esta no intervalo de 0 at 10.");} }while(pos < 0 && pos >10); puts("Fora do do-while!!!!!!"); return (0); }
Podemos ter vrios ifs encadeados com ou nenhum else associado a eles, mas o else, quando existir, deve estar aplicado a um comando if diretamente.
#include<stdio.h> int main(int argc, char** argv) { int pos = 0; do{ printf("Digite um nmero entre 0 e 10:"); fflush(stdout); scanf("%d", &pos); if(pos < 0){puts("Valores negativos. \n Por favor, digite novamente."); } if(pos > 10){puts("Valor acima de 10. \n Por favor, digite novamente. ");} }while(pos < 0 || pos > 10); puts("Valor return (0); } valido. Obrigado!!!!!!");
Console:
Digite um nmero entre 0 e 10: 11 Valor acima de 10.
Os operadores usados em comparaes entre valores so: == , != , <= ,>= , < e >. O ANSI C nos disponibiliza outro comando para realizar estes tipos de comparaes e este comando o switch. O switch recebe um determinado valor como parmetro, caractere ou inteiro, e ento ele ir executar o case cujo valor seja igual ao respectivo valor do parmetro.
#include <stdio.h> int main() { int x; printf("Digite um valor:"); fflush(stdout); scanf("%d",&x); switch(x){ case 1: printf("Case 1 foi acessado\n"); case 2: printf("Case 2 foi acessado\n"); case 3: printf("Case 3 foi acessado\n"); } return (0); }
Console:
Digite Case 1 Case 2 Case 3 um valor:1 foi acessado foi acessado foi acessado
Podemos adicionar o default caso nenhum dos cases seja executado, mas o default no obrigatrio.
#include <stdio.h> int main() { int x; printf("Digite um valor:"); fflush(stdout); scanf("%d",&x); switch(x){ case 1:
Console:
Digite um valor:4 Default foi acessado
Os exemplos acima, referentes ao comando switch, possuem um problema referente ao acesso dos cases, vamos supor que fosse digitado o valor 2, ento seria exibido:
Case 2 foi acessado Case 3 foi acessado Case default foi acessado
Pois uma vez que o case foi acessado, assim que finalizar sua execuo os outros cases subsequentes sero acessados ao termino do mesmo. Ao final de cada case necessrio adicionar o comando break para que o case subsequente no seja executado.
#include <stdio.h> int main() { int x; printf("Digite um valor:"); fflush(stdout); scanf("%d",&x); switch(x){ case 1: printf("Case 1 foi acessado"); break; case 2: printf("Case 2 foi acessado"); break; case 3: printf("Case 3 foi acessado"); break; default: printf("Default foi acessado"); break; } return (0); }
Switch e if so os comando condicionais que ANSI C nos fornece para que possamos manipular/desviar o fluxo de execuo caso seja necessrio.
5.2.
Repetio de fluxo
Algumas vezes necessrio executar um comando ou bloco de comandos, um exemplo seria percorrer posies de memoria. Para isto podemos usar os comandos for, while e do while. Estes comando so os mesmos para a maioria das linguagens procedurais. O comando for possui 3 parmetros: Uma varivel que e considerada o contador de loops, uma expresso que quando falsa o loop interrompe sua execuo e o incremento da varivel contadora.
#include <stdio.h> int main() { char str[10]= "Testando01"; int i; /*Exemplo 1 de comando for*/ for(i = 0; i < 10; i++){ printf("Exemplo1 : str[%d] = %c \n",i,str[i]); } printf("\n\n"); /*Exemplo 2 de comando for*/ int j = 0; for(; j < 10; j++){ printf(" Exemplo2 : str[%d] = %c \n",j,str[j]); } printf("\n\n"); /*Exemplo 3 de comando for*/ int k = 0; for(;; k++){ if(k < 10){printf(" Exemplo3 : str[%d] = %c \n",k,str[k]);} else{break;} } printf("\n\n"); /*Exemplo 4 de comando for*/ int t = 0; for(;;){ if(t < 10){printf(" Exemplo4 : str[%d] = %c \n",t,str[t]);} else{break;} t++; }
Console:
Exemplo1 Exemplo1 Exemplo1 Exemplo1 Exemplo1 Exemplo1 Exemplo1 Exemplo1 Exemplo1 Exemplo1 Exemplo2 Exemplo2 Exemplo2 Exemplo2 Exemplo2 Exemplo2 Exemplo2 Exemplo2 Exemplo2 Exemplo2 Exemplo3 Exemplo3 Exemplo3 Exemplo3 Exemplo3 Exemplo3 Exemplo3 Exemplo3 Exemplo3 Exemplo3 : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : str[0] str[1] str[2] str[3] str[4] str[5] str[6] str[7] str[8] str[9] = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = T e s t a n d o 0 1 T e s t a n d o 0 1 T e s t a n d o 0 1
str[0] str[1] str[2] str[3] str[4] str[5] str[6] str[7] str[8] str[9] str[0] str[1] str[2] str[3] str[4] str[5] str[6] str[7] str[8] str[9]
Exemplo4 Exemplo4 Exemplo4 Exemplo4 Exemplo4 Exemplo4 Exemplo4 Exemplo4 Exemplo4 Exemplo4
: : : : : : : : : :
str[0] str[1] str[2] str[3] str[4] str[5] str[6] str[7] str[8] str[9]
= = = = = = = = = =
T e s t a n d o 0 1
No exemplo 1 a varivel i pode ser usada apenas no bloco de funo do for uma vez que o mesmo foi declarado no comando for e no no bloco de comando da funo, j nos outros exemplos possvel usar as variveis contadoras fora do for, pois foram declaradas fora do comando. Exemplificamos 5 variantes do comando for importante perceber que os parmetros do for foram alterados e mesmo assim o comando aceito, ou seja, para o compilador basta que exista os delimitadores de parmetros que so os ;. Mas, observando o exemplo 4 podemos verificar um problema, caso no fosse adicionado um comando if no haveria condio de parada ento o comando for seria executado infinitamente. Isso chamado de loop infinito e com isto podem ocorrer problemas de estouro de buffer. Outro comando o while. O uso deste comando prximo do exemplo 5, referente ao comando for ele possui somente a expresso de condio.
#include <stdio.h> int main() { int i = 0; char str[10]= "Testando01"; while(i < 10){ printf("str[%d] = %c \n",i,str[i]); i++; } return (0); }
Console:
str[0] str[1] str[2] str[3] str[4] str[5] str[6] str[7] str[8] str[9] = = = = = = = = = = T e s t a n d o 0 1
PROJETO AGIT | PIC O while tem a mesma execuo que o for, mas possui um nico parmetro que caso no seja verdadeiro, interrompe o loop. While e for executam o loop assim que so verdadeiros, no existe a possibilidade de realizar uma verificao, atribuio ou qualquer outra ao caso a condio do loop seja falsa.
#include <stdio.h> int main() { char str[10]= "Testando01"; int i , j; char c = ' '; i = j = 11; for(; i < 10; i++){ printf("Escolha uma opo:\n"); printf("(I) Inserir:\n"); printf("(D) Deletar:\n"); printf("(A) Alterar:\n"); fflush(stdout); scanf("%c", &c); if(c != ' ' && (c != 'I' || c != 'D' || c != 'A')){continue;} } while(j < 10){ printf("Escolha uma opo:\n"); printf("(I) Inserir:\n"); printf("(D) Deletar:\n"); printf("(A) Alterar:\n"); scanf("%c", &c); if(c != ' ' && (c != 'I' || c != 'D' || c != 'A')){continue;} j++; } return (0); }
Console:
No exemplo acima o comando for no seria executado, uma vez que a condio seria falsa de primeira instncia, ento o menu nunca seria exibido e a execuo do programa finalizada. Podemos notar o comando continue ele o oposto do break, o break interrompe o loop enquanto que o continue no executar o que esta aps sua declarao, mas continua o loop normalmente at que a condio seja falsa. Para isto temos o comando do while com este comando possvel executar o bloco de comando do loop, uma primeira vez, mesmo que a condio no seja verdadeira de primeira instancia.
Console:
Escolha uma opo: (I) Inserir: (D) Deletar: (A) Alterar:
Uma vez que a condio do while falsa o bloco executado uma nica vez, esta a diferena do comando do while para o comando while/for. ANSI C nos proporciona outra maneira de alterar o fluxo de execuo ou repetio o comando o goto. Este comando executa um salto assim alterando a execuo, mas fortemente recomendado que NO use este comando uma vez que pode deixar o fluxo to desordenando que compreender a execuo do programa ser impossvel. O fluxo de execuo no somente pode ser repetido ou desviado, mas ele pode ser interrompido e para isto podemos usar a funo exit() esta funo interrompe o fluxo e retornando para o sistema operacional um indicativo o retorno 0 indica que foi finalizado com sucesso e um valor diferente de zero indica que ocorreu algum erro.
6. Vetores e Matrizes
Vetores so estruturas de dados que possuem todas as posies de memoria que ocupam do mesmo tipo definido, nesta seo veremos como manipul-las. As explicaes e exemplos aqui apresentados so referentes a vetores estticos.
6.1.
Vetores Unidimensionais
Quando definimos um vetor, estamos alocando espaos de memria de um determinado tipo de dado. Durante sua declarao definimos a quantidade de memria que queremos e definimos um tipo de dado que ser propagado para cada posio de memria reservado, ou seja, se alocarmos um vetor do tipo int de 15 posies todas as 15 posies sero do tipo int.
Console:
string de caracteres: aaaaaaaaaa string de inteiros:0123456789
Vetores contendo somente char so chamados tambm de strings. Todo vetor associado a um ponteiro e quando usamos um loop para percorrer este vetor alterando o valor de endereo, localizado entre os [ ] ou alteramos manualmente, estamos fazendo com que este apontador mude a posio de memria que o mesmo esta acessando, por este motivo importante lembrar do terminador de string \0 pois uma vez que este ponteiro invade outra posio de memoria que no pertence a seu programa pode ocorrer problemas, pois esta outra regio de memoria pode estar sendo usado por outro programa.
#include <stdio.h> int main() { int vet_int[10] = {0,1,2,3,4,5,6,7,8,9}; char vet_char[10] = {'a','a','a','a','a','a','a','a','a','a'}; int i; printf("string de caracteres: %s \n",vet_char); printf("string de inteiros:"); for(i=0;; i++){ printf("%d",vet_int[i]); } return (0); }
PROJETO AGIT | PIC Neste exemplo a varivel i incrementada, mas no temos condio de parada ento ocorrera um loop infinito desta forma acessando posies de memoria alm do \0 e isto um problema serio e o compilador gera um erro.
6.2.
Vetores Bidimensionais
Um vetor bidimensional tambm conhecido como matriz similar ao vetor unidimensional quanto ao funcionamento, mas no quando a estruturao este tipo de vetor possui linhas e colunas.
#include <stdio.h> int main() { int i,k,j; int vet_int[2][10] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19}; char vet_char[10] = {'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a' }; for(i = 0;i <2; i++){ for(j=0; j<10; j++){ printf("string de inteiros: %d \n",vet_int[i][j]); } } for(k=0; k<10; k++){ printf("string de caracteres: %c \n",vet_char[k]); } return (0); }
Console:
string string string string string string string string string string string string string string string string string string string string string string string string string de de de de de de de de de de de de de de de de de de de de de de de de de inteiros: 0 inteiros: 1 inteiros: 2 inteiros: 3 inteiros: 4 inteiros: 5 inteiros: 6 inteiros: 7 inteiros: 8 inteiros: 9 inteiros: 10 inteiros: 11 inteiros: 12 inteiros: 13 inteiros: 14 inteiros: 15 inteiros: 16 inteiros: 17 inteiros: 18 inteiros: 19 caracteres: a caracteres: a caracteres: a caracteres: a caracteres: a
vet_char:
6.3.
A Linguagem ANSI C nos permite inicializar strings sem especificar a quantidade de memria que deve ser alocada.
#include <stdio.h> #include<string.h> int main() { char vet_char[] = "abcdefghij"; printf("Tamanho da string: %d", strlen(vet_char)); return (0); }
Console:
Neste caso o vet_char ir assumir o tamanho da string sendo atribuda como seu tamanho. O comando strlen pertence a biblioteca string.h dentre outros comandos para manipular strings, alguns so: strcpy, strcomp, strcat dentre outros. Para evitar um ataque por estouro de pilha por alocao podemos no substituir as funes strXX pelas strnXX, estas recebem um parmetro adicional que indica o tamanho que a sequencia de caracteres possui. Quando manipular strings necessrio tomar muitos cuidados uma vez que o ponteiro que manipula a regio de memoria referente a string alocada pode nos confundir um exemplo :
#include <stdio.h> #include<string.h> int main() { char vet_char[] = "abcdefghij"; char vet_char2[10]; vet_char2 = vet_char; /*NO FAA ISSO*/ strcpy(vet_char2, vet_char); /*ACEITAVEL*/ printf("Tamanho da string: %d", strlen(vet_char)); return (0); }
Acima exemplificamos algo que no seria legal de ser feito, pois pode no ocorrer o esperado neste caso ambos os ponteiro iro apontar para o mesmo endereo de memoria. Vetores podem ser usados para tipos de dados criados pelo usurio, por exemplo, uma estrutura.
#include <stdio.h> #include<string.h> typedef struct{ char* login; char* senha; }Usuario; int main() { char login[7], senha[10]; int loop = 0; Usuario usr; usr.login = (char*) malloc (7*sizeof(char)); usr.senha = (char*) malloc (10*sizeof(char)); do{
Console:
Digite Digite LOGIN: SENHA: o login: aaa o senha: aaa aaa aaa
Foi declarado uma estrutura Usuario onde existem 2 elementos, login e senha, que so ponteiros e os valores digitados foram copiados para estes elementos.
7. Buffer Overflow
Ao lidarmos com dados que esto na memria principal estamos suscetveis a cometer pequenos deslizes que podem causar problemas no programa o que pode levar a inmeras consequncias. Nesta seo iremos estudar o que um buffer e quais so os problemas que podem advir do seu mau uso.
7.1.
Definies
Um buffer qualquer regio da memria utilizada para armazenar um amontoado de dados. Computacionalmente falando, um buffer pode ser entendido como uma varivel (regio da memria) que possui um tamanho especfico para armazenar qualquer tipo de dado.
PROJETO AGIT | PIC Desta forma, dizemos que ocorreu um Buffer Overflow (transbordamento de buffer) quando tentamos colocar mais dados em um buffer do que ele pode armazenar, ou seja, quando atingimos seu limite mximo, mas ainda continuamos a inserir informaes.
7.2.
Todo cdigo que apresente Buffer Overflow possui o que chamamos de bug. Bugs so qualquer tipo de problema, erro ou anomalia existente no cdigo. Existem vrios tipos de bug, mas quando programamos em C uma boa parte deles so causados por transbordamento de buffer. Quando causamos um buffer overflow (por exemplo, tentando inserir uma string de tamanho 10 numa string de tamanho 7) o programa pode encerrar de forma inesperada, causando um comportamento no condizente com o esperado. Alm disto, problemas de buffer podem ser uma fonte para hackers injetarem cdigo malicioso na aplicao, desviando o fluxo normal de execuo e causando os mais diversos tipos de danos (roubo de dados pessoas, invaso de computadores, derrubamento de sistemas, etc.). Alm disto, quando um programa encerra inesperadamente, ele pode deixar os dados manipulados num estado inconsistente, estejam eles em banco de dados ou em arquivos.
7.3.
Existem vrias formas de evitar o buffer overflow. A mais usual por meio da checagem do limite de dados que pode ser copiado. Isto pode ser feito de duas formas: ou por meio de compiladores que j fazem isto em tempo de compilao, ou por meio da insero de cdigo que cheque condies de erro. Para manipular strings, por exemplo, recomenda-se o uso de funes com a letra n no seu nome. Tais funes possuem a mesma funcionalidade das equivalentes sem a letra n, porm so mais seguras, pois verificam o limite de dados que pode ser manipulado. Por exemplo, a funo strcat(str1, str2) copia contedo de str2 para o final de str1. Se str1 no for suficientemente grande para comportar str1 + str2, iremos causar um buffer overflow. Uma forma de contornar este problema por meio do uso da funo strncat(str1, str2, n) que copia no mximo n caracteres de str2 para o final de str1. Um possvel valor para n seria a capacidade mxima de str1 menos seu tamanho real. Por fim, outra maneira de evitar buffer overflow por meio da anlise dos dados informados pelo usurio. Antes de atribuir os dados para as variveis, verifique se elas possuem o tamanho necessrio para armazen-los.
8. Ponteiros
8.1. Declarando ponteiros
Para declarar um ponteiro devemos pegar uma declarao de uma varivel e adicionar o asterisco ( * ) e desta forma o compilador entende que esta varivel ir conter um endereo e no um valor.
#include <stdio.h> #include <stdlib.h> #include <string.h> int main() { int *p; int end = 10; printf("Valor do end: %d \n\n", end); p = &end; *p = 13; puts("Valores printf("Valor printf("Valor printf("Valor return (0); } referentes ao ponteiro\n"); do end: %d\n", end); do ponteiro: %d \n", *p); do endereo do ponteiro: %d \n", p);
Console:
Valor do end: 10 Valores referentes ao ponteiro Valor do end: 13 Valor do ponteiro: 13 Valor do endereo do ponteiro: 2686748
Sempre que atribumos um endereo de memria para um ponteiro o mesmo aponta, ou seja, acessa a posio de memoria referente ao endereo atribudo. Incremento(adio) e decremento(subtrao) tambm podem ser aplicados a ponteiros, mas reforando ponteiros contm endereos e no valores, ento quando est incrementando ou decrementando estamos alterando o endereo do ponteiro e no algum valor de varivel. Os operadores relacionais <,>,<=,>=, != e == quando estes operadores so usados eles comparam o endereo do ponteiro.
#include <stdio.h> int main() {
if(p1 > p2){puts("p1 esta mais adiante que p2");} else{ puts("p2 esta mais adiante que p1");} return (0); }
Console:
Valor do end1: 10 Valor do end2: 20 Valores referentes ao ponteiro p1 Valor do end1: 13 Valor do ponteiro: 13 Valor do endereo do ponteiro: 2686744
Valores referentes ao ponteiro p2 Valor do end2: 15 Valor do ponteiro: 15 Valor do endereo do ponteiro: 2686748 p2 esta mais adiante que p1
#include <stdio.h> #include <stdlib.h> int main() { int *p1, *p2; int end1 = 10; int end2 = 20;
Console:
Valor do end1: 10 Valor do end2: 20 p1 esta apontando para um endereo diferente de p2 Aps atribuir p2 para p1p1 esta apontando para o mesmo endereo que p2
Existem aes que no podem ser aplicadas a ponteiros como diviso e multiplicao, adicionar 2 ponteiros, adicionar ou subtrair floats ou doubles de ponteiros. Reforando o contedo de ponteiros so endereos de memria e no valores. Tambm possvel declararmos ponteiros de ponteiros para isto basta colocar 2 asteriscos (**), na verdade podemos declarar ponteiros de ponteiros de ponteiros de N ponteiros basta adicionar a quantidade de asterisco (*) referente a quantos ponteiros desejar.
#include <stdio.h> int main() { int **pp_val, v=10,*p_val; p_val = &v; pp_val = &p_val; printf("pp_val valor %d \n", **pp_val); printf("pp_val endereo %d \n", pp_val); printf("p_val valor %d \n", *p_val); printf("p_val endereo %d \n", p_val); return (0); }
Ponteiro de ponteiro funciona da mesma forma para acessar o valor em sua posio de memria basta colocarmos a varivel com os 2 asteriscos (**). Para deixar mais claro a ideia de ponteiros e ponteiro de ponteiro, vamos exemplificar realizando um comparativo com vetores. Ponteiro int *vet; int **vet; Iint vet[10]; int vet[10][2]; Vetor
Ponteiro (Endereo)
Vetor(Endereo)
&vet
vet[9];
&vet
vet[9][2]
Lembrando, que para que um ponteiro seja semelhante devemos alocar o ponteiro dinamicamente, usando os comandos malloc ou calloc, e desta forma definir o tamanho da regio de memria apontado.
8.2.
Sempre que um vetor criado ele possui um ponteiro associado a ele e isto explica por que todos os vetores comeam na posio 0 (zero), pois nesta posio indexa o ponteiro associado a esta regio de memoria. O compilador ANSI C no sabe quanto de memria est sendo alocado para aquela regio de memria, por isto quando alocamos um vetor o ponteiro colocado no inicio do vetor e quando percorre o vetor encontra os valores, lembrando que percorrer o vetor caminhar incrementando ou decrementando o vetor.
#include <stdio.h>
Console:
Tamanho da string: 10
Console:
Tamanho da string: 10 vet_char[0] : a vet_char[1] : b vet_char[2] : c vet_char[3] : d vet_char[4] : e vet_char[5] : f vet_char[6] : g vet_char[7] : h vet_char[8] : i vet_char[9] : j
Aps associar o ponteiro vet_char ao vetor aux o ponteiro est com o endereo referente posio 0 (zero) do vetor. Ento quando feito o vet_char++ o ponteiro ir
PROJETO AGIT | PIC incrementar o endereo e desta forma caminhar pelas posies de memria referente ao aux. Como podemos notar strings em ANSI C podem ser escritas com char* ao invs de char[].
#include <stdio.h> void trocaLetra(char *str, char letra_real, char letra_troca){ while(*str != '\0'){ if(*str == letra_real){*str = letra_troca;} str++; } } int main() { char aux[10] = "abcdefghij"; int i = 0; trocaLetra (&aux, 'e', 'z'); printf("String aps troca:"); while(i < 10){ printf("%c", aux[i]); i++; } return (0); }
Console:
Quando uma funo recebe uma string como parmetros a mesma indicada como um ponteiro (char*) e ento manipul-la.
9. Funes
Quando estamos desenvolvendo um sistema complexo importante estrutur-lo de forma que existam funes que faam aes especficas, desta forma o programa fica mais legvel. As funes podem ou no possuir parmetros e podem ou no retornar valores.
9.1.
Toda funo possui um valor de retorno que especificado durante sua declarao antes do nome da funo e caso no haja um tipo de retorno ento e especificado como void.
#include <stdio.h> float soma(float x, float y) { return (x+y) ;
Console:
Quando uma funo recebe um valor por parmetro, o mesmo deve se responsabilizar por atualizar, pois os parmetros so uma copia do valor, ou seja, quando uma funo altera um parmetro no necessariamente o valor real foi alterado. Um exemplo a funo multiplicacao(float,float,float): Isto conhecido como passagem de parmetro por valor. Com ponteiros o funcionamento um pouco diferente possvel alterar os valores de variveis que esto chamando a funo.
#include <stdio.h> void soma(float x, float y, float *result) { *result = x+y; } void exibeMensagem(char *msg, float val) { printf("%s : %3.2f\n",msg,val); } void exibeMensagem2() {
Console:
Calculo realizado com sucesso o resultado : 4.50 FIM DO PROGRAMA!!!!!
Lembrando um pouco de ponteiro, quando passamos o endereo da varivel por parmetro, ento possvel manipular o valor da mesma mesmo que no seja a funo em que foi declarada. Isto conhecido como passagem de parmetros por referncia. Perceba que neste exemplo a funo main tem 2 parmetros uma varivel inteira argc (argument count) e um ponteiro de ponteiro do tipo caractere o argv (argument value) os valores destes parmetros so passados durante a compilao do programa ento o argc armazena a quantidade de valores passadas pela linha de comando, enquanto que o argv uma matriz que armazena os valores que foram passados pela linha de comando.
9.2.
Quando um vetor passado por parmetro, o mesmo recebido como um ponteiro. Este ponteiro onde est armazenado o endereo da primeira posio do vetor, ou seja, a posio 0 (zero). Ento, neste caso no passada uma cpia de todos os itens e sim o endereo do inicio, para que desta maneira seja possvel percorrer o vetor, lembrando que desta forma possvel alterar os valores do vetor.
10.
Manipulao de arquivos
Em C, um arquivo identificado como qualquer dispositivo de entrada e sada, desde um arquivo em disco (no formato .txt, por exemplo), uma impressora, ou o terminal (entrada e sada padro). Um arquivo pode ainda ser associado a um leitor de caracteres no formato binrio.
PROJETO AGIT | PIC Para manipularmos arquivos em C, necessitamos de uma estrutura utilizada para descrever tal arquivo, ou seja, apontar para a regio de memria onde ele est armazenado fisicamente. Esta estrutura chamada de descritor e um ponteiro definido na biblioteca stdio.h. FILE * arquivo; Ao declarar um ponteiro para arquivo podemos associ-lo a qualquer tipo de arquivo. Aps associ-lo, podemos manipul-lo da maneira que nos conveniar. Quando falamos de manipulao de arquivos, estamos nos referindo s operaes bsicas abaixo. Para consultar exemplos de cdigo, vide Slides de aula.
fopen(Agit.txt,
A r q u i v o s
t u r a
d e
PROJETO AGIT | PIC fgets fscanf fread L uma string (Linha completa) char* fgets(char*, int, FILE*) int fscanf(FILE*, formatos (cont char*), L uma string formatada argumentos) L dados binrios do arquivo size_t fread(void*, size_t, size_t, FILE*)
ferror
ferror(FILE*)
fseek
rewind
void rewind(FILE*)
11.
Alocao Dinmica
Existem 4 funes de alocao de memria malloc, calloc, realloc e free e todas esto definidas na biblioteca stdlib.h.
for(i=0; i<pos;i++) { str_malloc[i] = 97+i; str_calloc[i] = 97+i; } for(i=0; i<pos;i++){ printf("str_malloc[%d] = %c \n",i,str_malloc[i]); printf("str_malloc[%d] = %c \n",i,str_calloc[i]); } }
Console:
Digite o numero str_malloc[0] = str_malloc[0] = str_malloc[1] = str_malloc[1] = str_malloc[2] = str_malloc[2] = de posies:3 a a b b c c
PROJETO AGIT | PIC O sizeof retorna o tamanho do tipo de dado. Desta forma ser alocada uma quantidade de posies de memria onde cada posio ter o tamanho do tipo que ir armazenar. Como o malloc retorna um void*, como mencionado anteriormente, ento foi feito um cast para char*. Quando alocamos a memria com calloc todas as posies so inicializadas, diferente de quando alocamos com malloc que as posies no so inicializadas. Esquema de alocao dinmica usando malloc:
Existe outra maneira de alocar memria dinamicamente, mas apartir de outra memria j alocada, usando malloc ou calloc, esta funo o realloc.
for(i=0; i<pos;i++){str_malloc[i] = 97+i;} for(j=0; j<pos;j++){ str_calloc[j] = 97+j;} for(i=0; i<pos;i++){printf("str_malloc[%d] = %c \n",i,str_malloc[i]);} for(j=0; j<pos;j++){printf("str_calloc[%d] = %c \n",j,str_calloc[j]);} str_malloc = (int*)realloc(str_malloc, pos_1*sizeof(int)); str_calloc = (int*)realloc(str_calloc, pos_1*sizeof(int)); for(l=0; l<(pos+pos_1);l++){str_malloc[l] = 97+l;} for(k=0; k<(pos+pos_1);k++){ str_calloc[k] = 97+k;} printf("\n\n APS O REALLOC \n\n"); for(i=0; i<pos+pos_1;i++){printf("str_malloc[%d] = %c \n",i,str_malloc[i]);} for(j=0; j<pos+pos_1;j++){printf("str_calloc[%d] = %c \n",j,str_calloc[j]);} free(str_malloc); free(str_calloc); }
Console:
Digite o numero str_malloc[0] = str_malloc[1] = str_calloc[0] = str_calloc[1] = APS O REALLOC str_malloc[0] = a str_malloc[1] = b str_malloc[2] = c str_malloc[3] = d str_malloc[4] = e str_malloc[5] = f str_malloc[6] = g str_malloc[7] = h str_malloc[8] = i str_malloc[9] = j str_malloc[10] = k str_malloc[11] = l de posies:2 a b a b
O comando realloc pode mover todo o bloco de memria alocado anteriormente para conseguir mais espao de memria, ento o mesmo devolve um ponteiro para o bloco de memria e como este ponteiro refere-se ao bloco todo ento as informaes no so perdidas, caso os ponteiros str_malloc ou str_calloc, no exemplo acima, fossem nulos seria alocada a memoria referente ao pos_1 e seria retornado um ponteiro para esta memria. Caso no haja memria suficiente para ser realocado ento retornado um ponteiro NULL e o bloco de memria original se mantem o mesmo sem alteraes. Matrizes tambm podem ser alocadas usando estes comandos, mas neste caso cada item, linha e coluna, devem ser alocados.
#include<stdio.h> #include<stdlib.h> #define QTD_POS 3 void main(int argc, char** argv) { char **mtr; int i, linha, coluna, letra; i=linha=coluna=0; /*Alocar linhas*/ mtr = (char**)malloc(QTD_POS*sizeof(char*)); /*Alocar colunas*/ for(i=0; i<QTD_POS;i++){ mtr[i] = (char*)malloc(QTD_POS*sizeof(char)); } /*Preenche a matriz*/ letra = 65; for(linha=0; linha < QTD_POS; linha++){ for(coluna=0; coluna < QTD_POS; coluna ++){ mtr[linha][coluna] = (char)letra; /*cast converter o 65 para 'a'*/ letra++; } } /*Imprime a matriz*/ letra = 0; for(linha=0; linha < QTD_POS; linha++){ for(coluna=0; coluna < QTD_POS; coluna ++){ printf("mtr[%d][%d] = %c\n",linha, coluna, mtr[linha][coluna]); } printf("\n"); }
Console:
mtr[0][0] = A mtr[0][1] = B mtr[0][2] = C mtr[1][0] = D mtr[1][1] = E mtr[1][2] = F mtr[2][0] = G mtr[2][1] = H mtr[2][2] = I
Todas as alocaes foram feitas com char para que facilitasse a explicao, mas poderiam ser feitas com ints, floats doubles e qualquer outro tipo de dado.
Console:
Uma vez que a memria foi liberada ela pode ser usada novamente pelo malloc ou calloc para uma nova alocao.
12.
Diretivas de Compilao
O compilador C examina o arquivo fonte e realizam certas modificaes baseando-se nas diretivas de compilao, estas diretivas podem ser identificadas por conter um # antes do comando. Esta seo ir abordar as diretivas #include e #define para verificar outras diretivas existentes vide anexo.
12.1. #include
Esta diretiva informa ao compilador que inclua um programa em outro, em tempo de compilao, desta forma podendo utilizar as funcionalidades do mesmo. Deve-se indicar o arquivo que se deseja incluir ento podemos usar ou <> o que difere ambos que caso seja usado o ser necessrio indicar o caminho completo do arquivo ou garantir que ele esteja no mesmo diretrio do arquivo sendo compilado. Caso o arquivo esteja no caminho pr-definido pelo compilador, ou seja, forem arquivos de sistema como, por exemplo: stdio.h, string.h, stdlib.h ou conio.h ento usamos o <>. Todas estas bibliotecas possuem apenas prottipos, pois esto previamente compiladas e so adicionadas no programa durante a linkagem(linkedio). Todos os cdigos exemplificados aqui neste documento usam a diretiva #include.
12.2. #define
A diretiva define nos permite indicar ao compilador que sempre que o nome do macro for encontrado o mesmo deve ser substitudo pela sequencia de caracteres associado a ele na diretiva. Isto possibilita que caso um valor seja necessrio inmeras vezes ento adicionamos o nome do macro e assim caso seja necessrio modificar isto ser feito em um nico ponto do cdigo e no em vrios lugares, isto torna o programa mais flexvel. importante ressaltar que #define no possibilita declarar apenas constantes, mas tambm outros tipos, por exemplo, um comando. Vamos alterar o cdigo da seo 9.2 para que comporte a diretiva #define.
#include<stdio.h> #include<stdlib.h> #define QTD_POS 10 #define MSG_ERR_MALLOC printf("Memria no foi alocada com malloc!!!!!\n");
Console:
Memria foi alocada com malloc!!!!! Memria foi alocada com calloc!!!!!
No cdigo acima a quantidade de posies que sero alocadas no ser mais digitada pelo usurio foi criado um define com o nome de macro QTD_POS e as 2 mensagens de erro caso as memorias com malloc e calloc no seja alocado com sucesso tambm foram adicionadas com a diretiva #define com o nome de macro MSG_ERR_MALLOC e MSG_ERR_CALLOC.
13.
Bibliotecas
J mencionamos que bibliotecas so um conjunto de funes j implementadas que podem ser utilizadas em qualquer programa que importe tal biblioteca em tempo de execuo por meio da diretiva #include. Uma biblioteca definida por um arquivo com extenso .h (header), o qual possui a assinatura de todas as funes da biblioteca.
PROJETO AGIT | PIC Existem inmeras bibliotecas prontas em C. As principais, e suas funcionalidades esto descritas na tabela abaixo. Para maiores detalhes dos mtodos disponveis, bem como informaes de como utiliz-las, consulte manuais da Internet. Biblioteca string.h Funcionalidades Manipulao de strings Converso de tipo Gerao de sequencia aleatria Alocao e liberao de memria Controle de processos Ordenamento e procura Matemtica Operaes matemticas Manipulao de Tempo Manipulao de caracteres Manipulao de erros
stdlib.h
14.
Prottipos
Todas as funes mencionadas neste documento possuem os padres aceitveis pelo compilador, estes padres so conhecidos como sintaxe. Aqui ser exibido os prottipos e suas sintaxe.
14.3.2.
Union
14.3.3.
Enum
14.6.2.
Switch
PROJETO AGIT | PIC declarao_1; break; case constante_2: declarao_2; break; case constante_3: declarao_3; break; case constante_n: declarao_n; break; defaut: Declarao_defaut; }
14.6.3.
for
14.6.4.
while
14.6.5.
do {
do-while
14.6.6.
goto
14.6.7.
exit
void exit(cdigo_de_retorno);
PROJETO AGIT | PIC 14.7.1. Vetor unidimensional tipo_da_varivel nome_da_varivel[tamanho]; 14.7.2. Vetor bidimensional tipo_da_varivel nome_da_varivel[tamanho_1][ tamanho_2]; 14.7.3. Vetor multidimensional tipo_da_varivel nome_da_varivel[tamanho_1][ tamanho_2]...[tamanho_n];
14.8. Ponteiros
tipo_da_varivel *nome_do_ponteiro;
14.9. Funes
14.9.1. Declarao
tipo_de_retorno nome_da_funo(lista_de_parametro){ declarao; }
14.9.2.
return
14.10.2. calloc
void *calloc(unsigned int num , unsigned int size);
14.10.3. realloc
void *realloc(void *ponteiro; unsigned int num);
14.10.4. free
void *free(void *ponteiro);
14.11.2. #define
#define nome_da_macro sequencia_de_caracteres
15.
Anexos
Tamanho em Bytes
Faixa Mnima
-127 a 127 0 a 255 -127 a 127 -2.147.483.648 a 2.147.483.647 0 a 4.294.967.295 -2.147.483.648 a 2.147.483.647 -32.768 a 32.767 0 a 65.535 -32.768 a 32.767 -2.147.483.648 a 2.147.483.647 -2.147.483.648 a 2.147.483.647 0 a 4.294.967.295 Seis dgitos de preciso Dez dgitos de preciso Dez dgitos de preciso
PROJETO AGIT | PIC isso, pois aps um longo prazo sem analisar o cdigo voc mesmo pode no lembrar que este For na verdade uma varivel ou funo e at mesmo outro desenvolvedor pode se confundir assim dificultando a compreenso do cdigo. Segue as palavras reservadas pelo padro ANSI:
Palavras reservadas
auto
double
int
struct
break
else
long
switch
case
enum
typedef
register
char
extern
return
union
const
float
short
unsigned
continue
for
signed
void
default
goto
sizeof
volatile
do
if
static
while
Este material no abordara todas as palavras reservadas aqui mencionadas, mas interessante que saibamos de sua existncia.
Diretivas
#if
#ifdef
#ifndef
#else
#error
#pragma
#elif
#endif
#include
#define
#undef
#line
PROJETO AGIT | PIC Modo de Abertura r r+ Arquivo existente Arquivo existente ou ser criado um novo Arquivo existente ou ser criado um novo Arquivo existente ou ser criado um novo Arquivo existente ou ser criado um novo Arquivo existente ou ser criado um novo O mesmo que r+, mas para arquivo binrio. O mesmo que w+, mas para arquivo binrio. O mesmo que a+, mas para arquivo binrio. O mesmo que w, mas para arquivo binrio. O mesmo que a, mas para arquivo binrio. Somente Leitura Leitura e Escrita
Somente Escrita Escrita e Leitura (Apaga o contedo existente no arquivo) Escrita no final do arquivo (No apaga o contedo existente no arquivo) Escrita no final do arquivo
w+
a+
r+b
w+b a+b
wb
ab
16.
Referencias