Bliblioteca ou header? [RESOLVIDO]

1. Bliblioteca ou header? [RESOLVIDO]

Rayssa Fernanda
dontlikenickname

(usa Debian)

Enviado em 05/11/2016 - 01:52h

Quando comecei a aprender C, entendia stdio.h como biblioteca que era incluída para entrada e saída de dados. Mas vi depois em muitas literaturas se referindo como cabeçalho da biblioteca padrão de C ou arquivo cabeçalho.
Alguém sabe esclarecer qual é o termo mais coerente ou se os dois são válidos?


  


2. Re: Bliblioteca ou header? [RESOLVIDO]

Perfil removido
removido

(usa Nenhuma)

Enviado em 05/11/2016 - 02:06h

Já experimentou dar uma olhada em stdio.h?
O caminho é /usr/include/stdio.h
Aqui vai um pedaço do conteúdo:

#ifndef _STDIO_H

#if !defined __need_FILE && !defined __need___FILE
# define _STDIO_H 1
# include <features.h>

__BEGIN_DECLS

# define __need_size_t
# define __need_NULL
# include <stddef.h>

# include <bits/types.h>
# define __need_FILE
# define __need___FILE
#endif /* Don't need FILE. */


É um arquivo de código C.
Aqui vai o conteúdo todo: http://pastebin.com/esf3ZqgW

Biblioteca é algo como libmath, cujo arquivo é algo como libm.so.6

----------------------------------------------------------------------------------------------------------------
Nem direita, nem esquerda. Quando se trata de corrupção o Brasil é ambidestro.
(anônimo)

Encryption works. Properly implemented strong crypto systems are one of the few things that you can rely on. Unfortunately, endpoint security is so terrifically weak that NSA can frequently find ways around it. — Edward Snowden



3. Re: Bliblioteca ou header? [RESOLVIDO]

Uilian Ries
uilianries

(usa Linux Mint)

Enviado em 06/11/2016 - 22:54h

Neste caso, o header funciona como uma interface. A implementação está contida dentro da biblioteca, já compilada.
Esta estratégia é utilizada quando queremos expor apenas parte do código, como a declaração de funções e estruturas, mas não queremos publicar a implementação. Logo, colocamos a mesma dentro de uma biblioteca. O segundo ponto de vantagem, uma vez compilado, apenas é necessário realizar o link com a aplicação do projeto.

Alguns projetos, como Boost, utiliza algumas partes em header only, ou seja, distribui todo o código em headers, sem o uso de bibliotecas. A vantagem neste caso, será poder acessar todo o código, e compilar apenas incluindo o header. Como desvantagem, para a construção da sua aplicação, será necessário compilar o seu código e o código dos headers, junto ao seu, e como resultado, aumentando o tempo de construção.



--
Uilian Ries
Linux Counter: 521986


4. Re: Bliblioteca ou header?

Paulo
paulo1205

(usa Ubuntu)

Enviado em 07/11/2016 - 17:09h

Usar biblioteca e cabeçalhos tem como objetivo poupar tempo, evitando ter de refazer esforço que já foi feito.

O meio para a concretização desse objetivo é a compilação em separado das partes que compõem um programa. Você só compila partes novas ou partes que sofreram alterações. Depois de compiladas, essas partes são ligadas (linked) com outras partes já compiladas.

Para tanto, a técnica que você, programador, utiliza é de informar ao compilador os nomes e os tipos de dados dos objetos (variáveis e funções) cuja implementação está fora da parte do programa que está sendo compilada naquele momento. Sabendo o nome e o tipo, o compilador saberá como usar (e como não deixar usar) tais objetos, mesmo sem conhecer detalhes de sua implementação.

Um dos usos de cabeçalhos (headers), que geralmente (mas não necessariamente) residem em arquivos (que, no C, têm sufixo “.h”; em C++ o uso de sufixo nos cabeçalhos padronizados foi suprimido justamente para evitar confusões com cabeçalhos do C), é justamente informar ao compilador os nomes e os tipos de objetos cuja implementação pode estar num outro local. Esse local não é o próprio cabeçalho: no cabeçalho fica apenas a informação de nome e forma.

O produto final da compilação não é um programa executável, mas sim arquivos contendo o que se chama código objeto. O código objeto já tem um bocado de código nativo do processador, mas os endereços das funções e de variáveis globais ainda não estão completamente determinados: as referências a elas continuam amarradas apenas pelos seus nomes, e essa indeterminação vale tanto para as variáveis e funções que você define quanto para aquelas que você invoca a partir de outras fontes. Os endereços finais desses objetos só são completamente determinados pelo programa que faz a “ligação” (link) de um ou mais arquivos de código objeto (por isso o nome do programa que executa essa função é linker).

Além dos arquivos que você compilou, podem entrar na composição do executável final outros arquivos com código objeto que acompanham o compilador ou o próprio sistema operacional. Frequentemente, os compiladores e sistemas trazem uma coleção de códigos objetos empacotada em apenas um ou num pequeno número de arquivos, e tais coleções recebem o nome de bibliotecas. Bibliotecas podem — e geralmente devem, pelo menos aquelas que são mais importantes — ser passadas ao linker juntamente com os arquivos de código objeto produzidos por você, a fim de serem ligadas no mesmo arquivo executável(*).

Bibliotecas acabam trabalhando em conjunto com cabeçalhos: os cabeçalhos apresentam ao compilador quais são os símbolos e quais formas eles têm, e as bibliotecas trazem a implementação funcional desses símbolos.

Geralmente, cada biblioteca que você utiliza traz junto consigo um ou mais cabeçalhos. Por exemplo, a biblioteca padrão do C apresenta vários cabeçalhos que declaram os objetos nela contidos, classificados por assunto. O programa abaixo ilustra isso.

// Divide o intervalo [0; 2*PI) em um número aleatório de faixas, e imprime o cosseno de cada faixa
#include <stddef.h> // definições de uso geral (e.g. NULL)
#include <stdio.h> // operações de entrada e saída (e.g. printf())
#include <stdlib.h> // funções de uso geral (e.g. RAND_MAX, srand() e rand())
#include <math.h> // matemática de ponto flutuante (e.g. M_PI e cos())
#include <time.h> // funções ligadas a relógio e tempos (e.g. time())

int main(void){
int n, i;

srand(time(NULL));
n=1+(int)(359.0*rand()/RAND_MAX);

for(i=0; i<n; i++)
printf("cos(%d*PI/%d)=%0.15f\n", 2*i, n, cos(2*i*M_PI/n));

return 0;
}


Todos os símbolos acima, exceto main() e as variáveis locais nela contidas, são definidos pelo padrão como parte da biblioteca padrão do C. Alguns dos símbolos são constantes que existem na forma de macros (e.g. NULL, RAND_MAX e M_PI), e só são conhecidos pelo compilador por nome numa etapa bem inicial da compilação, chamada preprocessamento, tendo seu valor imediatamente substituído durante a compilação. As variáveis locais automáticas dentro de main() também são resolvidas durante a compilação, mas os demais símbolos globais, incluindo main, são levados como símbolos não-resolvidos (unresolved) para o código objeto, e só assumem valores fixos após o linker ser invocado.

Se você salvar o programa acima como prog.c e o compilar com a seguinte linha de comando
gcc prog.c -o prog -lm 
já percebe logo de cara que uma parte das funções da biblioteca padrão não reside junto com as demais. No mundo UNIX, por razões históricas, as funções matemáticas de ponto flutuante residem numa biblioteca separada, a chamada libm. Para invocá-la através do GCC, você tem de especificar a opção -lm, que informa ao linker a necessidade de incluir a biblioteca matemática.

Outra coisa que você pode notar é que o GCC cuidou de chamar o linker para você, e ainda cuidou de invocar a biblioteca que contém as demais funções usadas pelo seu programa. Isso é uma conveniência oferecida pelo GCC, que quebra o galho muitas vezes com programas pequenos do dia-a-dia.

No entanto, quando você tem programas de maior porte, você geralmente vai trabalhar com ele dividindo-o em vários módulos menores, e vai querer usar compilação separada de cada um desses módulos. Para comunicar um módulo com outros, você provavelmente vai criar seus próprios cabeçalhos. Além disso, você pode querer ter mais controle sobre as opções passadas ao linker, até para poder fazer a ligação dos módulos já compilados de uma maneira ordenada.

---

Para dar uma ideia de como a coisa realmente funciona, vou mostrar o que a interface conveniente do GCC faz por trás das cortinas.

A primeira coisa é gerar um arquivo com o código objeto do prog.c. Para você fazer isso manualmente, você pode fazer o seguinte.

gcc -c prog.c 


Iso vai produzir um arquivo prog.o (ou prog.OBJ, se você estiver no Windows). Esse arquivo contém código objeto do seu programa. Note que não foi necessário especificar a biblioteca matemática agora, porque o linker não foi invocado: a única coisa que nós fizemos foi gerar o código objeto do seu programa.

Como o seu programa só tem um arquivo, não temos mais código objeto para gerar. Então podemos chamar o linker.

A pergunta é: o que tem de ser ligado junto com o seu programa para que ele funcione?

A resposta é um pouco complicada, e pode variar de acordo com cada tipo de sistema operacional ou ambiente de execução. No entanto, pelo menos uma parte da resposta é óbvia: você vai ter de incluir todas as bibliotecas que possuírem as implementações das funções usadas pelo seu programa. No caso do Linux, você já sabe que as funções matemáticas de ponto flutuante estão na libm. O restante da biblioteca padrão do C tem suas funções (ou pelo menos a maioria delas) na libc.

Mas existem mais dependências, pelo menos no caso do Linux.

O padrão do C e de sua biblioteca especifica ações que devem ser tomadas antes de um programa em C começar a executar e depois que ele sinaliza que quer terminar a execução (i.e. quando se chama exit() ou se executa o comando return dentro de main()). No caso do Linux e alguns outros sistemas à la UNIX, essas coisas residem em arquivos objetos separados da libc, mas tais arquivos têm de entrar na composição do executável, logo têm de ser informados ao linker.

No fim das contas, para invocar o linker a fim de produzir o executável para o programa acima usando bibliotecas dinâmicas, eis o que teríamos de fazer (numa máquina com Ubuntu 14.04).

ld \
-dynamic-linker /lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 \
/usr/lib/x86_64-linux-gnu/crt1.o /usr/lib/x86_64-linux-gnu/crti.o \
prog.o -lc -lm \
/usr/lib/x86_64-linux-gnu/crtn.o \
-o prog


Você precisa informar ao linker:

  • como chamar o programa que faz a resolução final dos últimos símbolos em tempo de execução (“-dynamic-linker /lib/x86_64-linux-gnu/ld-linux-x86-64.so.2”);
  • a lista dos arquivos de código objeto que inicializam o programa, antes de main() ser invocada (“/usr/lib/x86_64-linux-gnu/crt1.o /usr/lib/x86_64-linux-gnu/crti.o”);
  • o código objeto do seu programa (no seu caso, apenas “prog.o”, mas poderia ser uma lista com múltiplos arquivos);
  • as bibliotecas das quais ele depende (“-lc -lm”);
  • o código objeto que faz a finalização após o encerramento de main() (“/usr/lib/x86_64-linux-gnu/crtn.o”)); e
  • por fim, o nome do executável a ser produzido como saída do processo (“-o prog”).

Já uma versão estática (todas as bibliotecas embutidas dentro do executável), seria produzida com o seguinte comando.

ld -static \
/usr/lib/x86_64-linux-gnu/crt1.o /usr/lib/x86_64-linux-gnu/crti.o \
/usr/lib/gcc/x86_64-linux-gnu/4.8/crtbeginT.o \
prog.o -lc -lm \
-L /usr/lib/gcc/x86_64-linux-gnu/4.8 -lgcc -lgcc_eh -lc \
/usr/lib/gcc/x86_64-linux-gnu/4.8/crtend.o \
/usr/lib/x86_64-linux-gnu/crtn.o \
-o prog


Em relação à versão dinâmica, você pode notar que saiu a indicação do programa que carrega as bibliotecas compartilhadas, mas entraram outras coisas:

  • por conta da unificação de funções de I/O entre C e C++, você acaba sendo obrigado a incluir objetos e bibliotecas do C++ (crtbeginT.o, libgcc, libgcc_eh e crtend.o);
  • a libc (“-lc”) foi listada duas vezes, porque a ordem das bibliotecas importa para o comando ld, de modo que toda vez que algum objeto faz referência a um símbolo definido em outro módulo, você deve incluir novamente o objeto em que o símbolo é definido.

_________________________________

(*) Em alguns sistemas, incluindo Linux e Windows, a produção do executável pode não ser realmente a última etapa do processo. Nesses sistemas, com o intuito de economizar espaço em disco e espaço na memória RAM, os programas podem compartilhar trechos de código executável. Tal compartilhamento se faz sobretudo com bibliotecas do sistema. Para usar as bibliotecas compartilhadas, o linker embute no executável instruções que informam ao sistema que a ligação final de alguns dos símbolos deve ser feita no momento em que o programa for executado, e aponta os nomes dos arquivos em que tais símbolos residem. Na hora em que o executável é chamado, o sistema localiza tais instruções e verifica se os arquivos referidos já foram carregados para a memória por algum outro programa. Se não tiverem sido, o sistema primeiro os carrega, e mapeia os endereços em que residem cada um dos símbolos. Dali para frente, cada novo programa que fizer referência a essas bibliotecas vai aproveitar o mapeamento já feito, resolvendo a referência aos símbolos mapeados no momento em que esses programa forem executados.


5. Re: Bliblioteca ou header? [RESOLVIDO]

Perfil removido
removido

(usa Nenhuma)

Enviado em 07/11/2016 - 17:46h

Com isto acabei-me de lembrar de algo que aprendi na faculdade.
Eu perguntei sobre linkedição e o professor me explicou.
Coisa que eu só sabia do TLINK do Summer '87.

----------------------------------------------------------------------------------------------------------------
Nem direita, nem esquerda. Quando se trata de corrupção o Brasil é ambidestro.
(anônimo)

Encryption works. Properly implemented strong crypto systems are one of the few things that you can rely on. Unfortunately, endpoint security is so terrifically weak that NSA can frequently find ways around it. — Edward Snowden



6. Re: Bliblioteca ou header? [RESOLVIDO]

Rayssa Fernanda
dontlikenickname

(usa Debian)

Enviado em 09/11/2016 - 17:52h

Escarecido! Obrigada à todos pelas respostas!






Patrocínio

Site hospedado pelo provedor RedeHost.
Linux banner

Destaques

Artigos

Dicas

Tópicos

Top 10 do mês

Scripts