Buscar

Resumo Linguagem de programação

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes
Você viu 3, do total de 39 páginas

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes
Você viu 6, do total de 39 páginas

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes
Você viu 9, do total de 39 páginas

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Prévia do material em texto

COMPILADOR
Se refere ao processo de tradução da linguagem de programação para linguagem de máquina (de uma linguagem fonte para uma linguagem que o computador entenda). Um programa que lê outro programa escrito em uma linguagem – a linguagem de origem – e a traduz em um programa equivalente em outra linguagem – a linguagem de destino. A grande maioria dos compiladores de hoje faz uso da técnica chamada tradução dirigida pela sintaxe.
Existem duas tarefas principais executadas por um compilador no processo de tradução:
1. Análise – momento em que o texto de entrada (código escrito na linguagem de programação) é examinado, verificado e compreendido;
2. Síntese (ou geração de código) – momento em que o texto de saída (texto objeto) é gerado.
Geralmente, um compilador é dividido em quatro fases. São elas: 1) analisador léxico; 2) analisador semântico; 3) analisador sintático; 4) geração/otimização do código.
Compilar um programa C é um processo que pode ser dividido em quatro estágios separados: Pré-processamento (as linhas que começam com um #caractere são interpretadas pelo pré-processador como comandos. A primeira fase da tradução de código fonte para código de máquina inclui a remoção de comentários; expansão de macros; expansão dos arquivos incluídos.
Um comentário em C consiste em uma sequência de caracteres que começa com uma barra e um asterisco (/*) e termina com um asterisco e uma barra (*/). Os comentários são tratados pelo compilador como um espaço em branco, isto é, são ignorados. Outra forma de formar comentários em C consiste em escrever duas barras (//) na coluna em que queremos iniciar o comentário. Todos os caracteres, até ao final dessa linha.
Compilação – nesse estágio, o código pré-processado é traduzido para instruções de
montagem específicas da arquitetura do processador de destino. É gerado um arquivo de saída intermediário, arquivo já preparado com as instruções no nível da montagem.
Montagem – somente o código existente é convertido em linguagem de máquina, as
chamadas de função, como printf (), não são resolvidas nesse momento.
 Linkagem – essa é a fase final, em que todas as chamadas de função e suas definições são resolvidas. O Linker sabe onde todas essas funções são implementadas e, caso tenha algum código extra, também será adicionado ao nosso programa.
Para produzir um programa executável, as peças existentes precisam ser reorganizadas; as que faltam, são preenchidas.
A linguagem de Programação C é de alto nível e apresenta sintaxe estruturada e flexível. Com a linguagem de Programação C, são criados programas compilados, gerando programas executáveis. A linguagem de Programação C tem estrutura simples e gera códigos mais enxutos e velozes se comparada a outras linguagens, pois permite a
inclusão de uma farta quantidade de rotinas.
Um código fonte em C pode ser formado por um ou mais arquivos fonte. Em um arquivo fonte, encontramos declarações e definições de funções e identificadores. Tais declarações e definições podem estar contidas em arquivos fonte, arquivos cabeçalho, bibliotecas e outros arquivos necessários ao programa. Para se obter um arquivo executável, é necessário compilar cada um dos arquivos do projeto e gerar arquivos objeto destes.
Um programa em C é constituído de:
 Um cabeçalho – que contém inclusão de bibliotecas, as diretivas de
compilador onde se define o valor de constantes simbólicas, a declaração
de variáveis, a declaração de funções, entre outros.
 Um bloco principal de instruções e outros blocos de rotinas.
 Documentação do programa em forma de comentários.
Código C olaAlunos.c
A primeira linha do programa #include <stdio.h> informa ao compilador a biblioteca que deve incluir à biblioteca stdio (standard input/output) no programa.
Na segunda linha, foi declarada a única função, a função main, e nessa existe apenas uma única instrução, que é a função printf() na linha 4 (disponível na biblioteca stdio.h da linguagem C) para escrever uma mensagem no monitor.
A função main serve como o ponto de partida para a execução do programa. Em geral, ela controla a execução, direcionando as chamadas para outras funções no programa. A função main é a primeira função de um programa C a ser executada e a única que não precisa ser explicitamente chamada.
A seguir, os formatos usados na função main:
1 int main()
2 int main(void) vai ser passado um parâmetro, mas não sei qual 
3 int main(int argc, char * argv[ ])
4 int main(int argc, char * const argv[ ], char * const envp[ ])
A 1 e 2 são usados quando nenhum argumento é passado ao programa. Já as 3 e 4 fornecem, respectivamente, dois e três argumentos.
argc – é o contador de argumentos. Ele informa quantos argumentos
foram passados juntos com o nome do programa. Se o valor é 1, então
argumentos não foram fornecidos com o nome do programa. 
argv – o nome do programa é armazenado em argv[0]. Vetor de ponteiros, em que cada ponteiro indica um argumento passado.
envp – apresenta informações sobre o ambiente do processo. 
Toda função é declarada com uma identificação e parênteses após seu nome. No exemplo main(), a função é identificada com o nome “main” e acompanhada de “(” e “)”. Essas características que permitem que o compilador saiba que se trata de uma função. Sem os parênteses, o compilador pode tratar o nome como se fosse uma variável, ocasionando um erro, uma vez que a palavra main é reservada pelo compilador.
Após a chamada da função, vem o bloco de código. Toda função em linguagem de programação C delimita o bloco com chaves. Começar com uma chave de abertura de bloco ({) e terminar com uma chave de fechamento de bloco (}). Essas chaves determinam o corpo da função.
Em uma função, é permitido inserir espaços em branco, tabulações e pular linhas (esses caracteres são ignorados pelo compilador). É permitido também escrever várias instruções em uma única linha ou escrever uma instrução em várias linhas. 
O objetivo da indentação (que significa recuo) é o de tornar os códigos mais legíveis tornando claramente visíveis todas as instruções de um ou mais blocos de código.
Indentação IF, ELSE IF, ELSE
Indentação SWITCH CASE
As primeiras linhas de um programa não são instruções da linguagem C, mas sim diretivas do pré-processador que executa um conjunto de processos preliminares sobre os arquivos fonte antes de estes serem fornecidos para o compilador. Uma diretiva ensina o pré-processador no sentido de efetuar determinada ação sobre o texto do programa antes da compilação.
Na linguagem de programação C, existem comandos processados durante a compilação do programa, conhecidos como diretivas de compilação. Esses comandos informam ao compilador as constantes simbólicas usadas na linguagem de programação e as bibliotecas que devem ser anexadas ao programa compilado.
A diretiva #include diz ao compilador para incluir na compilação do programa outros arquivos. Geralmente estes arquivos contém bibliotecas de funções ou rotinas do usuário. A diretiva #define diz ao compilador quais são as constantes simbólicas que serão usadas no programa.
Código C com diretivas
a constante QUANTIDADE_MAXIMA será
executada antes da compilação do texto escrito pelo programador. A diretiva
#include provoca a inclusão das bibliotecas stdio.h e stdlib.h em nosso programa
fonte, não interferindo no tempo de execução da programação, mas impactando
no tempo de compilação. O compilador substitui a linha que contém essa diretiva pelo
conteúdo do arquivo indicado. Essa substituição também é executada antes de o programa ser compilado. Assim, o efeito obtido é a apresentação de um texto,como se tivéssemos digitado todo o conteúdo de arquivo stdio.h e do arquivo stdlib.h na posição em que escrevemos as linhas. O arquivo stdlib.h contém as definições e declarações necessárias para o uso da função printf(), já o stdlib.h contém as declarações necessárias para o uso da função system(). 
Quando usamos os sinais < e >, o arquivo é procurado somente na pasta include, criada na instalação do seu compilador. Quando usamos " e " (aspas duplas), oarquivo é procurado primeiramente na pasta atual e, depois, se não for encontrado, na pasta include.
Códigos especiais
Códigos para impressão formatada com printf().
A memória de um computador é dividida em bytes, numerados de zero até o limite de memória da máquina. Esses números são chamados endereços de bytes, usados como referências, pelo computador, para localizar as variáveis. Toda variável tem uma localização na memória, e o endereço de identificação desta variável é o primeiro byte ocupado por ela. O programa deve saber que toda variável está armazenada em bytes
sequenciais e, identificando o tamanho desta variável pelo seu tipo, infere até
onde a leitura dos endereços deve ir.
Por exemplo, uma variável do tipo int em C com tamanho 4 bytes, ou seja, sabendo o endereço inicial, sabe que, a partir, dele temos mais três endereços sequenciais que correspondem à variável desejada. 
Quando o programa é carregado na memória, ocupa certa quantidade de bytes, e toda variável e função desse programa terão seu espaço e endereço particular. Para conhecer o endereço em que uma variável está alocada, usa-se o operador de endereços &.
Memória é uma unidade organizada logicamente em palavras. Uma palavra é uma unidade lógica de informação constituída por um número de bits de único endereço, que é possível transportar de uma vez só, um computador de 32 bytes tem a capacidade de transportar uma palavra de 32 bytes pela placa mãe. Consequentemente, um conjunto de palavras armazenadas na memória é um programa, e pode ser dividido em duas categorias:
Instruções – operações (programa propriamente dito) realizadas pela máquina;
Dados – variáveis, ou valores, processadas nessas operações. Transportados por processador em barramentos separados 
Cada palavra é identificada por meio de um endereço de memória sem ambiguidade.
A capacidade, ou tamanho, de uma memória vai depender do número de palavras que ela pode suportar e transportar. A posição de uma palavra dentro da memória é tida como o seu endereço. A primeira palavra da memória tem o endereço 000, a próxima, 001, e assim por diante.
Ponteiros são variáveis que armazenam o endereço memória de outras variáveis na memória, ou seja, sua localização. O uso do asterisco * serve para determinar que a variável usada será um ponteiro. 
nt x, y, z; // Essa instrução declara três variáveis comuns.
int *x, y, z; // Essa instrução declara somente x como ponteiro.
int *x, *y, *z; // Essa instrução declara três ponteiros.
Para obter o tamanho de um tipo de variável em bytes na linguagem de programação C, utiliza-se a função sizeof.
Considera-se como endereço o menor valor na região ocupada. Sendo assim, a variável a terá como endereço o numeral 3, e a variável b, o numeral 7.
Variável tem que conter um tipo, ela guarda apenas um único dado. Vetor é um conjunto de variáveis do mesmo tipo, posso usar um mesmo nome para conter quantas variáveis eu quiser. Vou alocar vários espaços na memória com o mesmo nome, só que tem que ser do mesmo tipo. Tenho conjunto de dados do mesmo tipo e vários endereços, mas como é um vetor e eu quero referenciar o endereço desse vetor, o endereço, o ponteiro que vai ser armazenado naquela variável vai ser o endereço do primeiro elemento do vetor.
As instruções acima são usadas para criar um ponteiro que vai apontar para o primeiro elemento do vetor x[ ]. A expressão “pont = x;” faz com que o ponteiro “pont” atribua o endereço do primeiro elemento do vetor x[ ].
Instruções usadas de forma análoga para obter o endereço do quinto elemento:
1. int x[] = {2, 16, 15, 3, 10};
2. int *pont;
3.
4. pont = &x[4] 		Ponteiro com o endereço do quinto elemento do vetor
Vetor de ponteiros: pode-se ter vários ponteiros sendo referenciados também por um nome que esta apontando para outro vetor do tipo variável.
Um vetor de ponteiros com 4 elementos, e mais quatros vetores de 3 elementos. 
A implementação da passagem de parâmetros por referência
.
1. Na linha 8, declara-se a variável ‘a’ com o valor 8;
2. Na linha 11, a função printf() imprime o valor da variável ‘a’ antes do seu
endereço ser passado para a função; 
3. Na linha 13, a instrução soma_mais_1(&a) recebe o endereço da variável ‘a’;
4. Na linha 22, a função soma_mais_1(*num) altera diretamente o dado na
memória.
5. Na linha 15, a função printf() imprime o valor da variável ‘a’ depois que a
função soma_mais_1(*num) foi executada.
Esses efeitos não ocorrem quando os parâmetros são passados por valor
(sem o uso do asterisco ‘*’ e o operador ‘&’), em que uma cópia do dado é
passada como parâmetro para a função e a variável origem não sofre qualquer
alteração.
Uma struct pode ser compreendida como um conjunto de variáveis referenciadas pelo mesmo nome, sendo que cada uma delas pode ter um mesmo tipo de dado, ou então vários. A ideia básica por trás da uma struct é criar uma variável que contenha várias outras variáveis, ou seja, estamos criando uma variável que contém dentro de si outras variáveis.
Na linguagem de programação C podemos declarar tipos de variáveis
como:
 Tipos básicos: char, int, float, double; Exemplo: int x; float y;
 Tipos compostos homogêneos: array; Exemplo: int x[5]; char nome[25].
Declaração de uma struct
m a union pode-se criar variáveis capazes de suportar dados diferentes, alocados no mesmo espaço de memória, em momentos diferentes. Isto é, a union permite que um conjunto de variáveis compartilhem o mesmo espaço na memória
Declara-se uma union de forma muito semelhante à uma struct; estas só se diferem no aspecto da struct ser alocada com espaço suficiente para todos os objetos, e o union só aloca espaço para o maior objeto que o compõe. Esse espaço alocado é suficiente para armazenar o maior dos seus membros.
A sintaxe da union é similar à da struct , conforme mostrado a seguir:
union <nome_da_union>
{
<tipo 1> e <variável 1>;
<tipo 2> e <variável 2>;
<tipo 3> e <variável 3>;
...
<tipo n> e <variável n>;
};
A declaração começa com a palavra union seguida do identificador da união. A primeira linha da declaração indica ao compilador que este é um novo tipo de dados para ser usado em outras partes do programa. Após as chaves, declara-se as variáveis que vão compor a union. É interessante quando se trabalha com dispositivos móveis ou equipamentos que contém um pequeno espaço de memória. Se o algoritmo vai usar as variáveis em momentos diferentes usa-se a union. É reservdo um espaço de 16 bits para colocar número inteiro, só que será necessário mais 8 bits para colocar carácter, totalizando 24 bits. Mas como o espaço maior é de 16 bits, só esse é alocado e quando for preciso usar o carácter, é apagado o anterior, otimizando assim o espaço
Compartilhamento da memória entre variáveis
Enum ou enumeração, é um tipo de dado definido pelo usuário, com o uso de uma lista de identificadores. Os identificadores podem ser vistos como uma lista de constantes, onde cada constante tem um nome significativo.
A enum segue a seguinte sintaxe:
enum <nome_da_enum>{lista_de_identificadores};
Assim como em um vetor, uma estrutura enum também começa com o
valor zero (0).
O comando typedef é usado para criar “sinônimo” ou um “alias” para tipos de dados existentes. Na prática podemos dizer que estamos renomeando um tipo de dados. Essa renomeação de tipos facilita a organização e o entendimento do código. Sintaxe:
typedef <nome do tipo de dado> <novo nome>;
É importante ressaltar que o comando typedef não cria um novo tipo. Ele apenas permite que um tipo existente seja denominado de uma forma diferente, de acordo com a especificação desejada pelo programador.
typedef com struct
PONTEIRO: STRUCT criar ponteiros para membro do struct
Na linguagem C, uma struct é uma coleção de variáveis referenciada pelo mesmo nome, conhecida também como tipo de dado agregado. Uma vez que variáveis do tipo estrutura são tratadas exatamente da mesma forma que variáveis de tipos básicos, é possível definir variáveis do tipo ponteiro para estruturas, conforme a seguinte sintaxe:
 struct <nome_da_struct> *<nome_do_ponteiro>;Para passar um endereço de uma variável, basta colocar o símbolo ‘*’antes da definição do seu ponteiro e o operador ‘&’ – operador unário que retorna o endereço na memória de seu operando, na chamada da struct. O uso do asterisco indica o componente da struct pode ser modificado diretamente na memória.
Substituímos (*depois).dia por depois->dia. O operador ‘->’ (seta) é usado para substituir o operador ‘.’ (ponto), eliminando também a necessidade de usar o ponteiro entre os parênteses.
1. Entre as linhas seis e dez, criamos uma struct “calendário” com três componentes do tipo inteiro “dia”, ”mês” e “ano”;
2. Na linha seis, criamos a variável “agora”, juntamente com o ponteiro “*depois”;
3. Na linha 12, é atribuído o endereço de endereço de “agora” para o ponteiro “depois”;
4. Nas linhas 19, 20 e 21, os componentes da struct são instanciados com o uso de ponteiros;
5. Na linha 25, os componentes são impressos na tela do usuário e os acessos aos dados se dá por meio dos ponteiros.
O printf %i vai receber três valores, sendo eles do endereço depois, do membro dia, mês e ano
STRUCT DE PONTEIROS é uma estrutura que vai guardar o endereço de membros de uma outra estrutura
Struct de ponteiros
1. Entre as linhas seis e dez. criamos uma struct “calendário” com três ponteiros “*dia”, “*mês” e “*ano”;
2. Nas linhas 12, 13 e 14, criamos três variáveis com suas instancias;
3. Nas linhas 16, 17 e 18, atribui-se os endereços das variáveis para os ponteiros:
4. Nas linhas 20, 21, 22, 23, 24 e 25, são impressos os endereços das variáveis e o apontamento dos ponteiros;
5. Na linha 27, são impressos os conteúdos das variáveis usando os ponteiros da struct.
STRUCT: FUNÇÃO
Assim como uma variável, uma struct também pode ser passada como parâmetro para uma função; essa passagem é feita de duas formas: por valor e por referência.
Uma struct é tratada com uma variável comum, e a passagem por valor é feita por meio da passagem de uma cópia do seu componente para uma função. Na passagem por valor, uma cópia do componente da struct é usada e alterado dentro da função sem afetar a variável da estrutura, na memória da qual ela foi gerada.
1. Entre as linhas 4 e 6, criamos uma struct “p_valor” com dois componentes ‘a’ e ‘b’;
2. Na linha 12, os componentes da struct são instanciados;
3. Nas linhas 15 e 16, são impressos os valores dos componentes antes da execução da função “imprimir_valor()”;
4. Nas linhas 19 e 20, são feitas duas chamadas para a função “imprimir_valor”, que traz como resultados a impressão dos valores da struct processados;
5. Nas linhas 23 e 24, são impressos os valores dos componentes depois da execução da função “imprimir_valor()”;
6. Entre as linhas 30 e 34, é criado a função “imprimir_valor()”.
Para que a passagem de um parâmetro seja por referência, basta colocar o símbolo “*” antes da sua definição dos parâmetros formais e o operador “&”1, na chamada do parâmetro (Mizrahi, 2008). O uso do asterisco indica que esses parâmetros podem ser modificados dentro da função, ou seja, as alterações dos parâmetros sofridas dentro da função também serão sentidas fora dela. Esses efeitos não ocorrem quando os parâmetros são passados por valor (sem o uso do asterisco (*)).
1. Entre as linhas 7 e 9, criamos uma struct “p_valor” com dois componentes ‘a’ e ‘b’;
2. Na linha 9, criamos a variável “x”, que vai referenciar a struct “p_valor”, juntamente com o ponteiro “*px”;
3. Na linha 11, o ponteiro “px” recebe o endereço de ‘x’;
4. Nas linhas 13 e 14, os componentes da struct são instanciados;
5. Nas linhas 17 e 18, são impressos os valores dos componentes antes da execução da função “imprimir_soma_valor()”;
6. Nas linhas 20 e 22, são feitas duas chamadas para a função “imprimir_soma_valor”; como parâmetros, são passados os endereços dos componentes da struct, que traz como resultado a impressão dos valores processados;
7. Nas linhas 25 e 26, são impressos os valores dos componentes depois da execução da função “imprimir_soma_valor()”;
8. Entre as linhas 33 e 36, é criada a função “imprimir_soma_valor()”.
Ponteiro do tipo void 
Um ponteiro também pode ser declarado como void, ou seja, um ponteiro sem tipo definido. Um ponteiro void tem como objetivo armazenar endereço de memória. Um ponteiro do tipo void tem a seguinte sintaxe:
1. void *nome_do_ponteiro;
1. Na linha 6, declaramos a variável ‘x’ com a instância 3;
2. Na linha 7, criamos um ponteiro “y” do tipo void que vai receber o endereço
de ‘x’;
3. Na linha 10, ‘z’ recebe o apontamento de ‘y’;
4. Na linha 11, z altera a instância de x;
5. Na linha 13, imprimimos a atual instância de ‘x’;
6. E nas linhas 14, 15 e 16, os endereços da variável e apontamentos, respectivamente.
ALOCAÇÃO DINÂMICA: CALLOC() E FREE()
A alocação dinâmica de memória é um mecanismo que reserva uma
quantidade de memória, em região conhecida como durante heap, durante a
execução de um programa (Mizrahi, 2008). Na biblioteca stdlib da linguagem C,
temos quatro funções para trabalhar com alocação dinâmica de memória:
Calloc; free; malloc e realloc.
A função calloc( ) tem como objetivo criar um vetor com tamanho dinâmico. Essa função serve para alocar memória durante a execução do programa. Ela faz o pedido de memória ao computador e retorna um ponteiro com o endereço do início do espaço alocado. Um fato interessante dessa função é que, após alocar a memória, ela preenche com zero todos os bits. Para declarar uma função calloc( ), usa-se a seguinte sintaxe:
1. void* calloc(unsigned int nElementos, unsigned int tElemento)
Nela, nElementos é a quantidade de posições que queremos no vetor, tElemento é o tamanho de cada posição de memória do nosso vetor, e unsigned int é números inteiros sem sinais (só números inteiros positivos). Na alocação da memória, devemos considerar o tamanho do tipo alocado.
A função free( ) libera o espaço de memória que foi previamente alocado.
Essa função recebe um ponteiro, que foi usado para receber o endereço do bloco de memória alocada, e não retorna nada. Para declarar uma função free( ), usa-se a sintaxe mostrada abaixo:
1. void free(void *nomePonteiro);
Se um projeto for mais simples, vai precisar liberar ao final de sua execução. É uma boa prática de programação liberar o que foi alocado antes da aplicação terminar sua execução.
MALLOC() 
A função malloc() é bem parecida com a função calloc(). Ela também aloca
um espaço de memória e retorna um ponteiro do tipo void para o início do espaço
de memória alocado. Mas há uma grande diferença: essa função não coloca zero
nos bits do espaço alocado (Mizrahi, 2008). Para declarar uma função malloc( ),
usa-se a sintaxe mostrada abaixo:
1. void* malloc(unsigned int nElementos);
A função malloc() recebe por parâmetro a quantidade de bytes que será
alocada na memória, tendo também o valor NULL, caso apresente algum erro,
ou o ponteiro para a primeira posição do vetor, caso tenha sucesso na locação.
E REALLOC ()
A função realloc( ) aloca e realoca um espaço na memória durante a execução do programa. Essa função realiza um pedido de memória e retorna um ponteiro com o endereço do início do espaço de memória alocado.
Para declarar uma função realloc( ), usa-se a sintaxe mostrada abaixo:
1. void* realloc(void* nomePonteiro, unsigned int nElementos);
Essa função recebe por parâmetro um ponteiro para um bloco de memória já alocada, na nossa sintaxe o *nomeponteiro, e a quantidade de Bytes a ser alocada, nElementos e retorna NULL em caso de erros ou um ponteiro para a primeira posição do vetor em caso de sucesso. 
RECURSIVIDADE
Recursividade ou recursão é quando uma função chama ela mesma para resolver um problema. E como funciona a recursividade?
De um modo geral, a recursividade é considerada como um processo repetitivo de uma rotina (procedimento ou função), que faz uma chamada para ela mesma. Por conseguinte, se essa função realiza essa chamada inúmeras vezes, é necessário tomar muito cuidado com a quantidade de repetições no processo. Quando não controlada, será executada de forma infinita, que é o que conhecemos comoloop eterno. Um loop eterno ou infinito ocorre quando um bloco do código repete a instrução descontroladamente, sobrecarregando a memória e ocasionando o travamento de todo o sistema. Para evitar é necessário definir uma condição que vai parar o processo. Para definir a condição de terminação, são necessárias análise e avaliação detalhadas do
problema que será resolvido de forma recursiva, entendendo como a rotina deverá terminar.
Uma rotina recursiva pode ser escrita de duas formas:
 Recursão Direta – É uma rotina composta por um conjunto de instruções, e uma dessas instruções faz a chamada para a rotina. A rotina X chama a própria rotina X;
 Recursão Indireta – É uma rotina que contém uma chamada a outra rotina que tem uma chamada a outra rotina e assim sucessivamente. A rotina X chama uma rotina Y, que por sua vez chama X.
RECURSÃO X ITERAÇÃO
A regra é: se dá pra resolver o problema com iteração, é possível resolver o mesmo problema com recursão.
As duas formas precisam de uma condição para terminar o ciclo repetitivo – um teste de terminação. A iteração se encerra quando a condição de teste falha, já a recursão se encerra quando se alcança o caso trivial (quando se tem um grande problema, ele é dividido em pequenos problemas e esse tem que ser fácil de resolver). 
A iteração é quando precisa repetir um algoritmo n vezes para resolver um problema; “while”, “for” e “do while”.
Na iteração, se o teste jamais se tornar falso, o laço vai se repetir eternamente e na recursão, se o problema não for reduzido de forma que se converta para o caso trivial, o laço vai se repetir até sobrecarregar a memória.
Analise sempre o tempo computacional que será gasto com uma função recursiva e veja se vale ou não a pena, se o gasto for muito alto, implemente a mesma função de forma iterativa.
FUNÇÃO RECURSIVA
As funções recursivas geralmente tornam o programa mais legível. Elas são definidas como na Matemática para evitar a retribuição de valores a variáveis. Essas funções podem substituir trechos de código que envolvem laços de repetição, como os laços de repetições while e for.
Dizemos que uma função é recursiva quando, dentro do corpo de uma função, se faz uma chamada para a própria função.
Função recursiva com vetor
É quando usamos a chamada de uma função passando um elemento de um array como parâmetro e, depois, dentro dessa função, fazemos uma nova chamada para ela mesma, isto é, funciona do mesmo modo que uma função recursiva simples, porém, em vez de o parâmetro ser uma variável, agora será um vetor (conjunto de variáveis referenciada pelo mesmo nome, do mesmo tipo).
Funções macro – diretiva #define
Basicamente, uma diretiva avisa o compilador que ele deve procurar todos os eventos de determinada expressão e substituí-la por outra na compilação do programa, isso permite criar o que chamamos de funções macro. 
Uma função macro é um tipo de declaração de função em que são informados o nome e os parâmetros da função como sendo o nome da macro e o trecho de código semelhante a ser aplicado na substituição. A diretiva #define associa um identificador a uma cadeia de caracteres de token. Após a definição da macro, o compilador pode substituir a cadeia de caracteres de token em cada ocorrência do identificador no arquivo de origem. 
OPERAÇÕES EM MEMÓRIA
As sub-rotinas de memória operam diretamente em áreas de memória e a linguagem de programação C possui algumas funções para manipulação dessas sub-rotinas, essas funções pertencem à biblioteca “string.h”. 
 Memset – Usada para preenchimento de memória memcpy – Faz cópia de memória. A função memset() preenche (inicializa) uma quantidade de memória (variável, constante, vetor, estrutura, entre outros) com um determinado valor de Byte Sintaxe: void * memset(void * nPonteiro , int nValor , size_t nBytes)
A função memset() sempre tem um retorno que é uma cópia do ponteiro “nPonteiro” ou tem um retorna “NULL”, que nesse caso é um erro e tem que ser tratado. 
A função memcpy() copia uma quantidade de Bytes de uma área de memória para outra. Ambas as regiões de memória são tratadas com unsigned char Sintaxe: void* memcpy(void* pDestino, void* pOrigem, size_t num).
memcpy() Retorno: Retorna uma cópia do ponteiro “pDestino” Retorna “NULL” em caso de erro.
As funções para manipular a memória estão agrupadas nas categorias definidas na biblioteca string.h.
Função memmove()
Essa função copia uma quantidade de bytes de uma área de memória para outra. Ambas as regiões de memória são tratadas com unsigned char.
Sintaxe memmove():
Void* memmove(void* pDestino, void* pOrigem, size_t num);
Na qual temos:
 pDestino – refere-se ao ponteiro para região de memória que receberá
os dados copiados.
 pOrigem – refere-se ao ponteiro para a região de memória de onde os
dados serão copiados.
 num – refere-se ao número de bytes que serão copiados, não
necessariamente o tamanho de um vetor.
Retorno:
 Retorna uma cópia do ponteiro pDestino.
 Retorna NULL em caso de erro.
O funcionamento da função memmove() é igual ao da função memcpy(), só que mais lenta. Porém, a função memmove() é mais segura no caso da existência de duas regiões sobrepostas na memória, ou seja, duas áreas em comum na memória.
A memcpy() usa um vetor auxiliar para fazer a cópia.
1. char nome[20];
2. memcpy(&nome[0], &nome[4], 10); //Comportamento inesperado no caso
de sobreposição.
3. Memmove(&nome[0], &nome[4], 10); //Usa um vetor para tratar o
problema de sobreposição.
A função memcmp() é usada para saber se uma string é maior, menor ou igual a outra. Essa função compara as n primeiras posições de duas strings, ou seja, de 0 até n-1.
Essas strings são, na verdade, números inteiros, e sua representação está na tabela ASCII. Essa comparação é feita caractere por caractere.
Essa função compara diretamente os caracteres das strings. Se, em algum momento da comparação, algum caractere da string 1 for menor que o da string 2, a função para e retorna -1. Ademais, caso algum caractere da string 1 for maior que o da string 2, a função para e retorna 1. Caso o processo não retorne nem 1 ou -1, caracteriza strings idênticas. Ambas as regiões da memória são tratadas como unsigned char, e a comparação é feita na ordem lexicográfica, ou seja, ordem do dicionário ou ordem alfabética.
Sintaxe memcmp():
Int memcmp(void* pRegiao1, void* pRegiao2, size_t N);
Na qual temos:
 pRegiao1 – refere-se ao ponteiro para uma região de memória.
 pRegiao2 – refere-se ao ponteiro para uma região de memória.
 N – refere-se ao número de bytes que serão comparados, não necessariamente o tamanho de um vetor.
Retorno:
 Se o valor de retorno < 0, então pRegiao1 menor que pRegiao2.
 Se o valor de retorno == 0, então blocos de memória são iguais.
 Se o valor de retorno > 0, então pRegiao1 maior que pRegiao2.
algoritmo com memcmp() e struct:
ARQUIVOS EM C
Arquivo em linguagem de programação C é uma coleção de bytes
armazenados em um dispositivo secundário.
Exemplos:
 disco rígido;
 pen drive;
 cartão SSD;
 entre outros.
Quais as vantagens de se usar arquivos?
 Armazenamento durável;
 permitem armazenar uma grande quantidade de informação;
 acesso concorrente aos dados.
É importante ressaltar que a extensão do arquivo não define o seu tipo. O
que define um arquivo é a maneira como os dados estão organizados por um
programa para processar (ler e escrever) esse arquivo.
Para manipular arquivos na linguagem C usa-se a biblioteca stdio.h e um
tipo especial de ponteiro. A função desse ponteiro é apontar a localização de um
registro e controlar o fluxo de leitura e escrita dentro de um arquivo. A declaração
de um ponteiro para arquivo em C segue a sintaxe a seguir:
FILE *nomePonteiro;
Lembrando que FILE deve ser escrito em letras maiúsculas. 
A linguagem C trabalha com apenas dois tipos de arquivos:
 Arquivos texto – podem ser editados no bloco de notas.
 Arquivos binários – não podem ser editados no bloco de notas.
Um arquivo texto tem seus dados gravados exatamente como seriam impressos na tela. Esses dados são gravados como caracteres de 8 bits utilizando atabela ASCII. Para que isso ocorra, existe uma etapa de conversão dos dados. Se um dado inteiro tem 4 bits, quando convertido ele terá 8 bits, que é o padrão da tabela ASCII para um caractere. Consequentemente, os arquivos são maiores, tendo leitura e escrita mais lentos.
Um arquivo binário tem seus dados gravados exatamente como estão organizados na memória do computador. Não existe etapa de conversão dos dados para esse tipo de arquivo, e o conteúdo da memória será copiado diretamente para o arquivo. Consequentemente, os arquivos são menores, tendo leitura e escritas mais rápidas.
Em C, o arquivo é manipulado por meio de um ponteiro especial para o arquivo.
A manipulação de um arquivo na linguagem C se dá em três etapas:
1. abrir o arquivo;
2. ler e/ou gravar os dados;
3. fechar o arquivo.
Para trabalhar com um arquivo, a primeira operação necessária é abrir tal arquivo. Para se realizar essa operação, usamos a seguinte sintaxe de abertura de arquivo:
FILE *fopen(char *nome_do_arquivo, char *modo);
A função fopen recebe como parâmetros o nome do arquivo a ser aberto e o tipo de abertura a ser realizado. Vejamos um exemplo, em que a função fopen vai abrir um arquivo txt em modo de escrita:
1. FILE *arq;
2. arq = fopen(“arquivo.txt”,”w”);
Há duas formas para especificar o caminho do arquivo:
1. Caminho absoluto – o endereço completo é escrito. Exemplo: f = fopen(“C:\\Users\\Casa\\Documents\\uninter.txt","w");
Para representar a barra "\" em uma string, usamos duas barras: \\
2. Caminho relativo – relativo ao diretório do programa. Exemplo:
f = fopen(“uninter.txt","w");
f = fopen(“..\\Novo\\uninter2.txt","w");
Caso o arquivo não exista ou não tenha permissão de acesso, a função
fopen também irá retornar NULL para o ponteiro. Quando esse erro ocorre, o
ponteiro irá apontar para NULL, sendo essa prática (checar se o ponteiro aponta
para NULL) muito importante para o tratamento de erros na abertura de arquivos
em C.
Para saber o final do arquivo, a linguagem C procura um sinal — uma
constante conhecida por EOF —, que sinaliza o fim do arquivo.
Se o byte lido pelo algoritmo representa o EOF, a função fclose() "fecha"
a abertura do arquivo. Ou seja, libera a memória associado ao ponteiro do FILE*.
Assim como em ponteiros, quando usamos a função free() para liberar
memória alocada, fechar os arquivos que não estão mais sendo usados é uma
boa prática de programação.
MODOS DE ABERTURA: READ (R), WRITE (W) E APPEND (A)
O modo de acesso é uma string que contém uma sequência de caracteres
que informam se o arquivo será aberto para escrita ou leitura. Depois que abrir
o arquivo, podemos executar os tipos de ação previstos pelo modo de acesso.
Assim, não será possível ler de um arquivo que foi aberto somente para escrita.
Os modos de acesso usados na linguagem C são descritos a seguir.
read(r) – Leitura de arquivo
1. r – para ler um arquivo, usamos o modo read.
Exemplo: FILE *arquivo = fopen("uninter.txt", "r").
O arquivo será aberto unicamente para leitura.
2. r+ – na adição do sinal "+" no "r", iremos abrir o arquivo tanto para leitura como para escrita; caso ele não exista, o arquivo será criado. Se o arquivo existir, terá o seu conteúdo apagado e substituído pelo novo.
Exemplo: FILE *arquivo = fopen("uninter.txt", "r+").
3. rb – abre o arquivo em modo binário para leitura. 
Exemplo: FILE *arquivo = fopen("uninter.txt", "rb").
 write(w) – Escrita em arquivo
1. w – para abrir um arquivo no modo de escrita, usamos a letra w. Esse modo automaticamente cria o arquivo ou substitui seu conteúdo anterior.
Exemplo: FILE *arquivo = fopen("uninter.txt", "w").
2. w+ – para abrir um arquivo tanto para leitura quanto para escrita. Se o arquivo já existir, terá seu conteúdo substituído.
Exemplo: FILE *arquivo = fopen("uninter.txt", "w+").
3. wb – usado para escrita em arquivos no modo binário.
Exemplo: FILE *arquivo = fopen("uninter.txt", "wb").
 append(a) – escrevendo ao final do arquivo (anexando)
1. a – usamos o modo de abertura "a" para ANEXAR informações ao arquivo. Exemplo: FILE *arquivo = fopen("uninter.txt", "a").
2. a+ – para abrir um arquivo no modo de leitura ou no modo de escrita ao final do arquivo (anexar), usamos o símbolo "+" depois da letra "a".
Exemplo: FILE *arquivo = fopen("arquivo.txt", "a+").
3. ab – do mesmo modo que a leitura binária "rb" e a escrita binária "wb", podemos anexar informações ao final do arquivo de maneira binária usando o "ab".
Exemplo: FILE *arquivo = fopen("uninter.txt", "ab").
Sempre que se terminar de usar um arquivo é preciso fechá-lo. Para realizar essa tarefa, usa-se a função fclose() com a seguinte sintaxe:
Int fclose(FILE *ponteiro);
A função fclose() retorna ZERO (0) caso o algoritmo tenha sucesso no fechamento do arquivo.
função fclose():
GRAVAÇÃO E LEITURA DE ARQUIVOS
Existem várias funções em C para a operação de gravação e leitura de
dados em arquivos.
Função fputc()
Serve para gravar um caractere em um arquivo. Essa função grava o
caractere fornecido na posição indicada pelo ponteiro do arquivo e, em seguida,
avança o ponteiro do arquivo.
Para realizar essa tarefa, usa-se a função fputc(), que tem a seguinte
sintaxe:
int fputc(char c, FILE *arquivo);
Retorno:
 Se houver erro, a função retorna a constante EOF.
 Se o algoritmo tiver sucesso, retornará o próprio caractere.
Função fgetc()
Essa função lê o caractere presente na posição indicada pelo ponteiro do
arquivo e automaticamente já se posiciona no próximo campo, e assim segue
lendo até encontrar a constante EOF. É usada para obter entrada de um caractere de
arquivo por vez, contido na biblioteca stdio.h.
A sintaxe da função fgetc é:
int fgetc(FILE *arq)
E, como comentamos, uma coisa importante que ocorre “por debaixo dos
panos” é que, após retornar um caractere, essa função já passa a apontar para
o próximo caractere automaticamente, até encontrar -1 (EOF).
Retorno:
 Se houver erro a função, retorna à constante EOF.
 Se o algoritmo tiver sucesso, retornará à leitura do arquivo.
A Figura 25 apresenta um exemplo com a função fgetc():
Principais funções de manipulação de arquivos da biblioteca stdio.h
No algoritmo a seguir, temos três variáveis declaradas. Como você faria para imprimir na tela dos usuários os endereços de cada variável? 
Temos a declaração de uma variável e um ponteiro. O algoritmo vai ler um número digitado pelo usuário e imprimir na tela o endereço da variável de duas formas:
Chamando a própria variável.
Chamando o ponteiro.
Linguagem de alto nível = humano compreende e 
linguagem de baixo nível = maquina compreende
Palavras reservadas da linguagem C
	auto 
	break 
	case 
	char
	continue 
	default 
	do 
	double
	else 
	enum 
	extern 
	false
	float 
	for 
	goto 
	if
	int 
	struct 
	long 
	register
	return 
	short 
	signed 
	sizeof
	static 
	struct 
	switch 
	typedef
	union 
	unsigned 
	struct
 
	void
	volatile 
	while

Outros materiais