Baixe Linguagem de programa c++ e outras Teses (TCC) em PDF para Linguagem Lógica em Banco de Dados, somente na Docsity!
O Fantástico Mundo da Linguagem C
Alexandre Fiori
Sumário
Lista de Tabelas
- 1 Introdução
- 1.1 O que é a Linguagem C?
- 1.2 Memória
- 1.3 Tipos de variáveis
- 1.3.1 Tipos básicos
- 1.3.2 Modificadores de tamanho
- 1.3.3 Modificadores de sinal
- 1.3.4 Variáveis const e static
- 1.3.5 Os enumeradores, enum
- 1.3.6 As estruturas, struct
- 1.3.7 As uniões, union
- 1.3.8 O typedef
- 1.4 Utilizando a área de memória
- 1.5 A função sizeof()
- 2 Asteriscos na Memória
- 2.1 Enxergando os vetores
- 2.2 Enxergando os ponteiros
- 2.2.1 Aritmética dos ponteiros
- 2.3 Particularidades
- 2.3.1 Ponteiro para estrutura
- 2.3.2 Ponteiro para função
- 2.3.3 Ponteiro em argumento de função
- 2.3.4 Ponteiro para ponteiro
- 2.3.5 Matriz de Ponteiros
- 2.4 Técnicas com ponteiros
- 2.4.1 Inicialização
- 2.4.2 Funções
- 2.4.2.1 A função memset()
- 2.4.2.2 A função signal()
- 2.4.2.3 A função atexit()
- 2.4.3 Alocação dinâmica de memória
- 2.4.3.1 A função malloc()
- 2.4.3.2 A função calloc()
- 2.4.3.3 A função free()
- 2.4.3.4 A função realloc()
- 2.4.3.5 As funções mmap(), munmap() e msync()
- 2.4.4 Listas ligadas
- 3 Manipulação de Dados
- 3.1 Os sistemas de arquivos
- 3.2 A função stat()
- 3.3 A função perror()
- 3.4 Funções para manipular arquivos
- 3.5 Os arquivos stdin, stdout e stderr
- 3.6 Lista de argumentos variáveis
- 3.7 Interpretando arquivos texto
- 3.7.1 Arquivos de configuração
- 3.7.2 Os parsers
- 3.8 Interpretando arquivos XML
- 3.8.1 Criando arquivos XML
- 3.8.2 O parser XML
- 3.9 Manipulando strings
- 3.9.1 Interpretando dados digitados pelo usuário
- 3.10 Expressões Regulares
- 3.10.1 Utilizando expressões regulares
- 3.10.2 Expressões Regulares em parsers
- 3.11 Unicode
- 3.11.1 Implementação do Unicode
- 4 Desenvolvimento de Projetos
- 4.1 Dividindo o projeto em arquivos
- 4.2 Os arquivos de cabeçalho
- 4.2.1 Bibliotecas
- 4.2.2 Criando arquivos de cabeçalho
- 4.3 Procedimento de compilação
- 4.3.1 O arquivo Makefile
- 4.3.2 Definição de sessões
- 4.3.3 Criação manual de Makefile
- 4.3.4 Dica do editor vim
- 4.3.5 Ferramentas GNU
- 4.3.5.1 A ferramenta m4
- 4.3.5.2 A ferramenta aclocal
- 4.3.5.3 A ferramenta automake
- 4.3.5.4 A ferramenta autoconf
- geLog 4.3.5.5 Os arquivos AUTHORS, README, NEWS e Chan-
- 4.3.5.6 O arquivo configure.ac
- 4.3.5.7 O arquivo Makefile.am
- 4.3.6 Exemplo
- 4.4 Funções e bibliotecas importantes
- 4.4.1 Utilizando getopt
- 4.4.1.1 O programa getopt
- 4.4.1.2 A função getopt()
- 4.4.2 Utilizando gettext
- 4.4.2.1 Sistema de localização locale
- 4.4.2.2 Implementando gettext nas aplicações
- 4.4.2.3 A função setlocale()
- 4.4.2.4 A função bindtextdomain()
- 4.4.2.5 A função textdomain()
- 4.4.2.6 A função gettext()
- 4.4.2.7 Código fonte internacionalizado
- 4.5 Criando bibliotecas
- 4.5.1 Bibliotecas estáticas
- 4.5.1.1 Criando bibliotecas estáticas
- 4.5.2 Bibliotecas dinâmicas
- 4.5.2.1 Criando bibliotecas dinâmicas
- 4.6 Carregando bibliotecas manualmente
- 4.6.1 Imagens digitais
- 4.6.2 O formato portable anymap
- 4.6.3 Escrevendo a biblioteca que trata imagens
- 4.6.4 O programa que carrega as bibliotecas
- 4.7 Registrando eventos no syslog
- 5 Redes Interconectadas
- 5.1 Origem das redes de computadores
- 5.2 Equipamentos da infra-estrutura
- 5.2.1 O switch
- 5.2.2 O roteador
- 5.2.3 O filtro de pacotes, firewall
- 5.2.4 O access point
- 5.3 Sistema Operacional
- 5.3.1 Características
- 5.3.2 Ambiente
- 5.4 OSI - Open System Interconnection
- 5.4.1 Camada #7 - Aplicação
- 5.4.2 Camada #6 - Apresentação / Sintaxe
- 5.4.3 Camada #5 - Sessão
- 5.4.4 Camada #4 - Transporte
- 5.4.5 Camada #3 - Rede
- 5.4.6 Camada #2 - Enlace de dados
- 5.4.7 Camada #1 - Física
- 5.5 O modelo real, da Internet
- 5.5.1 Aplicação
- 5.5.2 Transporte
- 5.5.3 Internet
- 5.5.4 Máquina-Rede
- 5.6 O Protocolo IP
- 5.6.1 Classes de IP
- 5.6.2 Endereços IP especiais
- 5.6.3 Máscaras de Rede
- 5.6.4 Simulando o endereçamento IP
- 5.7 Roteamento IP
- 5.7.1 Gateway
- 5.7.2 Teoria do Roteamento IP
- 5.7.3 Subnet
- 5.7.4 Simulando o roteamento IP
- 5.7.5 Conclusão
- 5.8 Desenvolvendo aplicações simples
- 5.9 Byte Order
- 5.10 Conversão de byte order
- 5.10.1 A função inet_aton()
- 5.10.2 A função inet_ntoa()
- 5.10.3 Programa para calcular endereço de rede
- 5.11 Utilizando DNS para resolver endereços
- 5.11.1 As funções gethostbyname() e gethostbyaddr()
- 5.11.2 As funções sethostent() e endhostent()
- 5.11.3 A função herror()
- 5.11.4 Programa para resolver endereços para nome e vice-versa
- 5.11.5 Conclusão
- 5.12 Identificando serviços e protocolos
- 5.12.1 O arquivo /etc/services
- 5.12.2 As funções getservbyname() e getservbyport()
- 5.12.3 As funções setservent() e endservent()
- 5.12.4 As funções htons() e ntohs()
- 5.12.5 Programa para identificar serviços e portas
- 5.12.6 O arquivo /etc/protocols
- 5.12.7 As funções getprotobyname() e getprotobynumber()
- 5.12.8 As funções setprotoent() e endprotoent()
- 5.12.9 Programa para identificar protocolos
- 5.13 Conexões lógicas: Sockets
- 5.14 Famílias de Socket
- 5.14.1 Sockets da família AF_INET
- 5.14.2 Sockets da família AF_UNIX
- 5.15 Tipos de Socket
- 5.15.1 Sockets do tipo SOCK_STREAM
- 5.15.2 Sockets do tipo SOCK_DGRAM
- 5.15.3 Sockets do tipo SOCK_RAW
- 5.16 Protocolos
- 5.16.1 O Protocolo TCP
- 5.16.2 O Protocolo UDP
- 5.16.3 O Protocolo ICMP
- 5.17 Funções e estruturas
- 5.17.1 A função socket()
- 5.17.2 A estrutura struct sockaddr
- 5.17.3 A estrutura struct sockaddr_in
- 5.17.4 A estrutura struct sockaddr_un
- 5.17.5 As funções shutdown() e close()
- 5.17.6 A função connect()
- 5.17.7 As funções bind(), listen() e accept()
- 5.17.8 As funções send() e recv()
- 5.17.9 As funções sendto() e recvfrom()
- 5.18 Técnicas
- 5.18.1 A função getpeername()
- 5.18.2 A função getsockname()
- 5.18.3 A função fcntl()
- 5.18.4 A função setsockopt()
- 5.18.5 A função getsockopt()
- 5.18.6 A função select()
- 5.18.7 A função fork()
- 5.18.8 A função daemon()
- 5.18.9 A função sendfile()
- 5.19 Aplicações reais
- 5.19.1 icmpd: recebendo pacotes puros (raw packets)
- 5.19.1.1 Código fonte
- 5.19.1.2 Notas
- 5.19.2 multid/multisend: recebendo e enviando multicast
- 5.19.2.1 multid.c: daemon que recebe mensagens multicast
- 5.19.2.2 multisend.c: envia mensagens UDP
- 5.19.2.3 Notas
- 5.19.3 minihttpd.c: mini servidor HTTP non-blocking
- 5.19.3.1 Ambiente do mini servidor
- 5.19.3.2 Código fonte
- 5.19.3.3 Testando conexões simultâneas no mini servidor
- 6 Acesso a Banco de Dados
- 6.1 Bancos de Dados gratuitos
- 6.2 MySQL
- 6.3 PostgreSQL
- 6.4 Criação do ambiente de laboratório
- 6.4.1 MySQL
- 6.4.2 PostgreSQL
- 6.5 API de programação
- 6.5.1 MySQL
- 6.5.2 PostgreSQL
- 6.6 Inserindo dados
- 6.6.1 MySQL
- 6.6.2 PostgreSQL
- 6.7 Realizando pesquisas
- 6.7.1 MySQL
- 6.7.2 PostgreSQL
- 1.1 Mapeamento de arquivos na memória Lista de Figuras
- 1.2 Dentro do address space do programa
- 1.3 Utilizando Type Casting
- 2.1 Representação gráfica de vetor
- 2.2 Representação gráfica de ponteiro
- 2.3 Representação gráfica de matriz de ponteiros
- 3.1 Representação gráfica de sistema de arquivos
- 3.2 Divisão de string em matriz de strings
- 3.3 Fonte TrueType baseada em Unicode
- 4.1 Imagem portable anymap original
- 4.2 Imagem portable anymap processada
- 5.1 Modelo OSI
- 5.2 Redes IP roteadas
- 5.3 Ambiente de rede local
- 5.4 Rede IP funcional
- 5.5 Transmissão de dados do TCP
- 6.1 Banco de Dados Relacional
- 1.1 Tipos básicos de variáveis e funções
- 1.2 Modificadores de tamanho
- 1.3 Modificadores de sinal
- 1.4 Diferenças entre unsigned e signed
- 2.1 Tabela de sinais (signal)
- 3.1 Macros e tipos de arquivos
- 3.2 Funções para manipulação de arquivos
- 5.1 Notação decimal e binária de endereço IP
- 5.2 Classes de endereços IP
- 5.3 Divisão binária de classes IP
- 5.4 Endereços IP não roteáveis na Internet
- 5.5 Máscaras de rede para classes IP
- 5.6 Notação binária de endereçamento IP
- 5.7 Notação decimal de endereçamento IP
- 5.8 Cálculo de subnet
- 5.9 Máscaras de subnet
- 5.10 Evolução dos bits em subnets
- 5.11 Internet Byte Order
- 5.12 Famílias de Socket
- 5.13 Tipos de Socket
- 5.14 Controle de fluxo do TCP
- 5.15 Maneiras de eliminar parte da conexão
- 5.16 Campos da estrutura struct sockaddr_in
- 5.17 Campos da estrutura struct sockaddr_un
- 6.1 Organização das tabelas do MySQL
Quando um programa é executado, o sistema operacional pega o código binário que está no disco e mapeia na memória, utilizando uma ou mais páginas para alocar seu conteúdo - essas páginas de memória podem ter as permissões de leitura, gravação ou execução.
Figura 1.1: Mapeamento de arquivos na memória
Na figura 1.1 a área marcada em cinza representa a as páginas de memória alocadas para o arquivo executável no sistema operacional. Ali é o address space do programa. Todas as variáveis e funções serão criadas naquela área, exceto a memória alocada dinamicamente que será vista posteriormente. Durante o desenvolvimento de aplicações o programador pode cometer erros e acessar áreas de memória fora de seu address space. Quando isso ocorre o sistema operacional finaliza o programa emitindo um sinal chamado SIGSEGV^3 , ou Falha de Segmentação (Segmentation Fault). O programa a seguir será utilizado para detalhar o address space e as áreas internas dentro dele.
addrspace.c
/*
- addrspace.c: demonstração^ de^ address^ space
- Para compilar:
- cc -Wall^ addrspace.c^ -o^ addrspace
- Alexandre Fiori / / variável global / int varglobal; void func1(void) { / variáveis de func1() / int var1, var2; } void func2(void) { / variáveis de func2() / char str1, str2; } int main() { / variável de main() */ floar calc; return 0; } (^3) SIGNAL(7) - Linux Programmer’s Manual
Ali, algumas variáveis foram criadas e estão em espaços de memória segmentados dentro do ad- dress space. As variáveis globais estão disponíveis em todo o address space, enquanto as variáveis declaradas dentro das funções só estão disponíveis naquele escopo, veja:
Figura 1.2: Dentro do address space do programa
A figura 1.2 mostra claramente que a variável varglobal está no mesmo nível das funções, portanto é global. As variáveis var1 e var2 só existem no escopo de func1() e as variáveis str1 e str2 só existem no escopo de func2(). Na função main() há apenas uma variável, calc, que só existe naquele escopo. A variável global pode ser acessada por qualquer uma das funções mencionadas, porém as demais só podem ser acessadas dentro de seu próprio escopo. Note que as variáveis possuem tamanhos diferentes, relativas ao tipo. Como já foi mencionado, o tipo da variável int, float ou char serve apenas para delimitar o tamanho que ela ocupa na memória. As funções também têm tipo, utilizado para o comando return que coloca naquela área de memória o valor a ser retornado. O tipo void é considerado nulo, utilizado para funções que não retornam valores.
1.3 Tipos de variáveis
A linguagem C provê alguns tipos de variáveis e modificadores, que serão apresentados aqui. Embora muitos acreditem que o tipo char só deve ser utilizado para caracteres e o tipo int para números, isso não é verdade. Como eles são apenas delimitadores de tamanho, foram convencionados a ter o uso por determi- nado conteúdo, mas podemos fazer contas utilizando char e colocar caracteres em variáveis do tipo int sem nenhum problema. Já os tipos float e double são utilizados para armazenar números fracionários - chamados de ponto flutuante.
1.3.1 Tipos básicos
Seguem os tipos básicos:
Quando a variável é do tipo signed o bit mais à esquerda é o bit do sinal. Se ele for 1, o número sempre será negativo, veja:
Tabela 1.4: Diferenças entre unsigned e signed binário unsigned signed 00100001 33 33 01010001 81 81 10001000 136 - 10101010 170 -
Quando convertemos o binário para decimal, somamos o valor dos bits. No caso do número 10001000 unsigned temos apenas o bit7 e o bit3 ligados, portanto 128+8 resulta em 136. Porém, quando somamos o bit7 e o bit3 do número 10001000 signed, fazemos -128+8, resultando em -120.
1.3.4 Variáveis const e static
O tipo const faz com que a variável seja apenas para leitura, portanto é necessário inicializá-la durante a programação, veja:
const float pi = 3.141516;
Durante a execução do programa esta variável nunca poderá ser alterada. Ela é uma constante e só pode ser lida. O tipo static é um pouco diferente. Quando o compilador gera o código binário ele deixa pré- definido que ao entrar no escopo de uma função as variáveis de lá devem ser criadas. Portanto, quando uma função é chamada sua área de memória é criada com as variáveis e quando ela encerra aquela área deixa de existir. Isso faz com que o address space tenha tamanho variável. As variáveis do tipo static sempre estarão presentes, mesmo que declaradas dentro uma função que não está em uso. Isso permite armazenar valores dentro de funções para uso em chamadas subsequentes, veja:
int count(void) { static int current = 0; return current++; }
Ao iniciar o programa, a variável current terá o valor 0. Cada vez que a função count() for chamada, este valor irá incrementar um. Se a variável não fosse do tipo static ela seria criada no memento da chamada de func(), que sempre retornaria 0. As variáveis static fazem com que o programa consuma mais memória pelo fato de sempre existi- rem lá.
1.3.5 Os enumeradores, enum
Os enumeradores são utilizados para criar variáveis com nomes diferentes e valores numéricos sequenciais. Essas variáveis são sempre do tipo const int. Exemplo:
enum { jan = 1, fev, mar, abr, ... };
A partir desta declaração, as variáveis jan, fev, mar e assim por diante serão constantes com os valores 1 , 2 , 3 e assim consecutivamente. Caso o valor inicial não seja definido, será sempre 0.
1.3.6 As estruturas, struct
As estruturas são utilizadas para criar novos tipos de dados à partir dos tipos básicos. São de extrema utilidade na programação. Exemplo: struct pessoa { int idade; double telefone; };
Agora o programa conta com um novo tipo de dados, o tipo struct pessoa. É possível criar variáveis com este tipo, porém há uma pequena regra para acessar seu conteúdo, veja: struct pessoa p1, p2; p1.idade = 33; p1.telefone = 50723340; p2.idade = 21; p2.telefone = 82821794;
As variáveis p1 e p2 são do tipo struct pessoa. Para acessar os membros dessas variáveis é neces- sário utilizar o. e depois o nome do membro. Na memória, essas variáveis são organizadas exatamente como são definidas, sem nenhum bit a mais nem menos. No caso do tipo struct pessoa, é uma variável com 32+64 bits, resultando em 96 bits ou simplesmente 12 bytes.
1.3.7 As uniões, union
Como o próprio nome já diz, são utilizadas para unir uma mesma área de memória que pode ser acessada de diferentes maneiras. Segue a declaração de uma union de exemplo: union valores { unsigned int vi; float vf; }; union valores temp; temp.vi = 10;
Assim como nas estruturas, as uniões premitem a criação de tipos. O tipo union valores é utilizado para criar uma variável com 32 bits que pode ser acessada como int ou float. Quando a variável temp foi criada, ao invés de ter o tamanho de unsigned int + float, passou a ter o tamanho do maior. Como ambos são do mesmo tamanho, o temp passou a ter 32 bits. A vantagem é que podemos atribuir qualquer número à temp.vi e depois trabalhar com ele em temp.vf ou vice-versa.