O que são erros de símbolos externos indefinidos / de referência não definidos? Quais são as causas comuns e como corrigi-las / evitá-las?
Sinta-se livre para editar / adicionar seus próprios.
c++
linker-errors
undefined-reference
c++-faq
unresolved-external
Luchian Grigore
fonte
fonte
Respostas:
A compilação de um programa C ++ ocorre em várias etapas, conforme especificado em 2.2 (créditos para referência a Keith Thompson) :
Os erros especificados ocorrem durante esse último estágio da compilação, mais comumente chamado de vinculação. Basicamente, significa que você compilou vários arquivos de implementação em arquivos ou bibliotecas de objetos e agora deseja que eles funcionem juntos.
Digamos que você definiu o símbolo
a
ema.cpp
. Agora,b.cpp
declarou esse símbolo e o usou. Antes de vincular, simplesmente assume que esse símbolo foi definido em algum lugar , mas ainda não se importa onde. A fase de vinculação é responsável por encontrar o símbolo e vinculá-lo corretamente ab.cpp
(bem, na verdade, ao objeto ou biblioteca que o utiliza).Se você estiver usando o Microsoft Visual Studio, verá que os projetos geram
.lib
arquivos. Eles contêm uma tabela de símbolos exportados e uma tabela de símbolos importados. Os símbolos importados são resolvidos nas bibliotecas às quais você vincula e os símbolos exportados são fornecidos para as bibliotecas que usam isso.lib
(se houver).Existem mecanismos semelhantes para outros compiladores / plataformas.
Mensagens de erro comuns são
error LNK2001
,error LNK1120
,error LNK2019
para Microsoft Visual Studio eundefined reference to
SymbolName para GCC .O código:
irá gerar os seguintes erros com o GCC :
e erros semelhantes com o Microsoft Visual Studio :
As causas comuns incluem:
#pragma
(Microsoft Visual Studio)UNICODE
Definições inconsistentesfonte
Membros da classe:
Um
virtual
destruidor puro precisa de uma implementação.Declarar um destruidor puro ainda requer que você o defina (ao contrário de uma função regular):
Isso acontece porque os destruidores da classe base são chamados quando o objeto é destruído implicitamente; portanto, é necessária uma definição.
virtual
métodos devem ser implementados ou definidos como puros.Isso é semelhante aos não
virtual
métodos sem definição, com o raciocínio adicional de que a declaração pura gera uma vtable fictícia e você pode obter o erro do vinculador sem usar a função:Para que isso funcione, declare
X::foo()
como puro:virtual
Membros não pertencentes à classeAlguns membros precisam ser definidos mesmo se não forem usados explicitamente:
O seguinte produziria o erro:
A implementação pode estar embutida, na própria definição de classe:
ou fora:
Se a implementação estiver fora da definição de classe, mas em um cabeçalho, os métodos deverão ser marcados
inline
para impedir uma definição múltipla.Todos os métodos de membro usados precisam ser definidos se usados.
Um erro comum é esquecer de qualificar o nome:
A definição deve ser
static
os membros dos dados devem ser definidos fora da classe em uma única unidade de tradução :Um inicializador pode ser fornecido para um
static
const
membro de dados do tipo integral ou de enumeração na definição de classe; no entanto, o uso de odr desse membro ainda exigirá uma definição de escopo de espaço para nome como descrito acima. O C ++ 11 permite a inicialização dentro da classe para todosstatic const
os membros de dados.fonte
Falha ao vincular bibliotecas / arquivos de objeto apropriados ou compilar arquivos de implementação
Geralmente, cada unidade de tradução gera um arquivo de objeto que contém as definições dos símbolos definidos nessa unidade de tradução. Para usar esses símbolos, você deve vincular esses arquivos de objeto.
No gcc, você especificaria todos os arquivos de objeto a serem vinculados na linha de comando ou compilaria os arquivos de implementação.
A
libraryName
aqui é apenas o nome nua da biblioteca, sem acréscimos específicos da plataforma. Então, por exemplo, nos arquivos de biblioteca do Linux geralmente são chamados,libfoo.so
mas você só escreve-lfoo
. No Windows, esse mesmo arquivo pode ser chamadofoo.lib
, mas você usaria o mesmo argumento. Pode ser necessário adicionar o diretório em que esses arquivos podem ser encontrados usando-L‹directory›
. Certifique-se de não escrever um espaço após-l
ou-L
.Para XCode : Adicione os Caminhos de Pesquisa de Cabeçalho do Usuário -> adicione o Caminho de Pesquisa da Biblioteca -> arraste e solte a referência real da biblioteca na pasta do projeto.
No MSVS , os arquivos adicionados a um projeto automaticamente têm seus arquivos de objetos vinculados e um
lib
arquivo é gerado (em uso comum). Para usar os símbolos em um projeto separado, você precisará incluir oslib
arquivos nas configurações do projeto. Isso é feito na seção Vinculador das propriedades do projeto, emInput -> Additional Dependencies
. (o caminho para olib
arquivo deve ser adicionadoLinker -> General -> Additional Library Directories
) Ao usar uma biblioteca de terceiros que é fornecida com umlib
arquivo, a falha em fazer isso geralmente resulta no erro.Também pode acontecer que você esqueça de adicionar o arquivo à compilação; nesse caso, o arquivo de objeto não será gerado. No gcc, você adicionaria os arquivos à linha de comando. No MSVS, adicionar o arquivo ao projeto fará com que ele seja compilado automaticamente (embora os arquivos possam ser excluídos manualmente da compilação).
Na programação do Windows, o sinal indicativo de que você não vinculou uma biblioteca necessária é que o nome do símbolo não resolvido começa com
__imp_
. Procure o nome da função na documentação e deve indicar qual biblioteca você precisa usar. Por exemplo, o MSDN coloca as informações em uma caixa na parte inferior de cada função em uma seção chamada "Biblioteca".fonte
gcc main.c
vez degcc main.c other.c
(o que os iniciantes costumam fazer antes que seus projetos se tornem tão grandes que constroem arquivos .o).Declarado mas não definiu uma variável ou função.
Uma declaração de variável típica é
Como esta é apenas uma declaração, é necessária uma única definição . Uma definição correspondente seria:
Por exemplo, o seguinte geraria um erro:
Comentários semelhantes se aplicam às funções. Declarar uma função sem defini-la leva ao erro:
Tenha cuidado para que a função que você implementa corresponda exatamente à que você declarou. Por exemplo, você pode ter cv-qualifiers incompatíveis:
Outros exemplos de incompatibilidades incluem
A mensagem de erro do compilador geralmente fornecerá a declaração completa da variável ou função que foi declarada, mas nunca definida. Compare-o com a definição que você forneceu. Verifique se todos os detalhes correspondem.
fonte
#includes
não adicionados ao diretório de origem também se enquadram na categoria de definições ausentes.A ordem na qual as bibliotecas vinculadas interdependentes são especificadas está incorreta.
A ordem na qual as bibliotecas estão vinculadas importa se as bibliotecas dependem uma da outra. Em geral, se a biblioteca
A
depende da bibliotecaB
,libA
DEVE aparecer anteslibB
nas bandeiras do vinculador.Por exemplo:
Crie as bibliotecas:
Compilar:
Então, para repetir mais uma vez, a ordem FAZ importa!
fonte
o que é "referência indefinida / símbolo externo não resolvido"
Vou tentar explicar o que é uma "referência indefinida / símbolo externo não resolvido".
Por exemplo, temos algum código
e
Crie arquivos de objeto
Após a fase de montagem, temos um arquivo de objeto, que contém quaisquer símbolos para exportar. Olhe para os símbolos
Rejeitei algumas linhas da saída, porque elas não importam
Então, vemos os seguintes símbolos para exportar.
src2.cpp não exporta nada e não vimos seus símbolos
Vincular nossos arquivos de objeto
e execute
O vinculador vê símbolos exportados e os vincula. Agora tentamos descomentar linhas no src2.cpp como aqui
e reconstruir um arquivo de objeto
OK (sem erros), porque apenas construímos um arquivo de objeto, a vinculação ainda não está concluída. Tente vincular
Isso aconteceu porque nosso local_var_name é estático, ou seja, não é visível para outros módulos. Agora mais profundamente. Obter a saída da fase de tradução
Portanto, vimos que não há rótulo para local_var_name, é por isso que o vinculador não o encontrou. Mas somos hackers :) e podemos corrigi-lo. Abra src1.s no seu editor de texto e altere
para
ou seja, você deve ter como abaixo
alteramos a visibilidade de local_var_name e configuramos seu valor para 456789. Tente criar um arquivo de objeto a partir dele
ok, veja a saída readelf (símbolos)
agora local_var_name tem Vincular GLOBAL (era LOCAL)
ligação
e execute
ok, nós cortamos isso :)
Portanto, como resultado - um "erro de referência externa indefinida / símbolo não resolvido" ocorre quando o vinculador não consegue encontrar símbolos globais nos arquivos de objeto.
fonte
Os símbolos foram definidos em um programa C e usados no código C ++.
A função (ou variável)
void foo()
foi definida em um programa C e você tenta usá-lo em um programa C ++:O vinculador C ++ espera que os nomes sejam desconfigurados, portanto, você deve declarar a função como:
Equivalentemente, em vez de ser definida em um programa C, a função (ou variável)
void foo()
foi definida em C ++, mas com ligação C:e você tenta usá-lo em um programa C ++ com ligação C ++.
Se uma biblioteca inteira estiver incluída em um arquivo de cabeçalho (e foi compilado como código C); o include precisará ser o seguinte;
fonte
#ifdef __cplusplus [\n] extern"C" { [\n] #endif
e#ifdef __cplusplus [\n] } [\n] #endif
([\n]
sendo retorno de carro real, mas não posso escrever isso corretamente no comentário).extern "C" { #include <myCppHeader.h> }
.Se tudo mais falhar, recompile.
Recentemente, consegui me livrar de um erro externo não resolvido no Visual Studio 2012 apenas recompilando o arquivo incorreto. Quando reconstruí, o erro desapareceu.
Isso geralmente acontece quando duas (ou mais) bibliotecas têm uma dependência cíclica. A biblioteca A tenta usar símbolos em B.lib e a biblioteca B tenta usar símbolos de A.lib. Nem existe para começar. Quando você tenta compilar A, a etapa do link falhará porque não consegue encontrar B.lib. A.lib será gerado, mas nenhuma DLL. Você compila B, que terá êxito e gerará B.lib. A recompilação de A agora funcionará porque o B.lib foi encontrado.
fonte
Importar / exportar incorretamente métodos / classes entre módulos / dll (específico do compilador).
O MSVS exige que você especifique quais símbolos exportar e importar usando
__declspec(dllexport)
e__declspec(dllimport)
.Essa funcionalidade dupla geralmente é obtida através do uso de uma macro:
A macro
THIS_MODULE
seria definida apenas no módulo que exporta a função. Dessa forma, a declaração:expande para
e diz ao compilador para exportar a função, pois o módulo atual contém sua definição. Ao incluir a declaração em um módulo diferente, ela seria expandida para
e informa ao compilador que a definição está em uma das bibliotecas às quais você vinculou (consulte também 1) ).
Você pode simular classes de importação / exportação:
fonte
visibility
e do Windows.def
, pois eles também influenciam o nome e a presença do símbolo..def
arquivos há muito tempo. Sinta-se livre para adicionar uma resposta ou editar esta.Essa é uma das mensagens de erro mais confusas que todos os programadores do VC ++ viram repetidamente. Vamos esclarecer as coisas primeiro.
A. O que é símbolo? Em resumo, um símbolo é um nome. Pode ser um nome de variável, um nome de função, um nome de classe, um nome de tipo de digitação ou qualquer coisa, exceto os nomes e sinais que pertencem à linguagem C ++. É definido pelo usuário ou introduzido por uma biblioteca de dependências (outro definido pelo usuário).
B. O que é externo? No VC ++, todo arquivo de origem (.cpp, .c, etc.) É considerado uma unidade de tradução, o compilador compila uma unidade de cada vez e gera um arquivo de objeto (.obj) para a unidade de tradução atual. (Observe que todo arquivo de cabeçalho que esse arquivo de origem incluiu será pré-processado e será considerado como parte dessa unidade de tradução) Tudo dentro de uma unidade de tradução é considerado interno, todo o resto é considerado externo. Em C ++, você pode fazer referência a um símbolo externo usando palavras-chave como
extern
,__declspec (dllimport)
e assim por diante.C. O que é "resolver"? Resolução é um termo de tempo de vinculação. No tempo de vinculação, o vinculador tenta encontrar a definição externa para cada símbolo nos arquivos de objeto que não conseguem encontrar sua definição internamente. O escopo deste processo de pesquisa, incluindo:
Esse processo de busca é chamado de resolução.
D. Finalmente, por que o símbolo externo não resolvido? Se o vinculador não puder encontrar a definição externa para um símbolo que não tem definição internamente, ele reportará um erro de Símbolo Externo Não Resolvido.
E. Possíveis causas do LNK2019 : Erro de símbolo externo não resolvido. Já sabemos que esse erro ocorre porque o vinculador não encontrou a definição de símbolos externos; as possíveis causas podem ser classificadas como:
Por exemplo, se tivermos uma função chamada foo definida em a.cpp:
Em b.cpp queremos chamar a função foo, então adicionamos
para declarar a função foo () e chamá-la em outro corpo da função, diga
bar()
:Agora, ao criar esse código, você receberá um erro do LNK2019 reclamando que foo é um símbolo não resolvido. Nesse caso, sabemos que foo () tem sua definição em a.cpp, mas diferente da que estamos chamando (valor de retorno diferente). Este é o caso em que a definição existe.
Se queremos chamar algumas funções em uma biblioteca, mas a biblioteca de importação não é adicionada à lista de dependências adicionais (definida em:) da configuração do
Project | Properties | Configuration Properties | Linker | Input | Additional Dependency
seu projeto. Agora, o vinculador reportará um LNK2019, pois a definição não existe no escopo de pesquisa atual.fonte
Implementações de modelo não visíveis.
Modelos não especializados devem ter suas definições visíveis para todas as unidades de tradução que os usam. Isso significa que você não pode separar a definição de um modelo em um arquivo de implementação. Se você deve separar a implementação, a solução alternativa usual é ter um
impl
arquivo que você inclua no final do cabeçalho que declara o modelo. Uma situação comum é:Para corrigir isso, você deve mover a definição de
X::foo
para o arquivo de cabeçalho ou algum lugar visível para a unidade de tradução que o utiliza.Modelos especializados podem ser implementados em um arquivo de implementação e a implementação não precisa estar visível, mas a especialização deve ser declarada anteriormente.
Para mais explicações e outra solução possível (instanciação explícita), consulte esta pergunta e resposta .
fonte
referência indefinida
WinMain@16
ou referência de ponto de entrada 'incomum' semelhantemain()
(especialmente paraestúdio visual)Você pode ter deixado de escolher o tipo certo de projeto com seu IDE real. O IDE pode querer vincular, por exemplo, projetos de aplicativos Windows a essa função de ponto de entrada (conforme especificado na referência ausente acima), em vez da
int main(int argc, char** argv);
assinatura comumente usada .Se o seu IDE suportar Projetos de console simples, convém escolher esse tipo de projeto, em vez de um projeto de aplicativo do Windows.
Aqui estão case1 e caso2 tratadas com mais detalhes a partir de um mundo real problema.
fonte
WinMain
. Programas válidos em C ++ precisam de ummain
.Além disso, se você estiver usando bibliotecas de terceiros, verifique se possui os binários corretos de 32/64 bits
fonte
A Microsoft oferece um
#pragma
para referenciar a biblioteca correta no momento do link;Além do caminho da biblioteca, incluindo o diretório da biblioteca, este deve ser o nome completo da biblioteca.
fonte
O pacote NuGet do Visual Studio precisa ser atualizado para a nova versão do conjunto de ferramentas
Eu apenas tive esse problema ao tentar vincular a libpng ao Visual Studio 2013. O problema é que o arquivo do pacote tinha apenas bibliotecas para o Visual Studio 2010 e 2012.
A solução correta é esperar que o desenvolvedor libere um pacote atualizado e faça a atualização, mas funcionou para mim invadindo uma configuração extra do VS2013, apontando os arquivos da biblioteca do VS2012.
Editei o pacote (na
packages
pasta dentro do diretório da solução) localizandopackagename\build\native\packagename.targets
e dentro desse arquivo, copiando todas asv110
seções. Mudeiv110
para parav120
nos campos de condição, tendo muito cuidado para deixar os caminhos de nome de arquivo todos comov110
. Isso simplesmente permitiu que o Visual Studio 2013 se vinculasse às bibliotecas de 2012 e, nesse caso, funcionou.fonte
Suponha que você tenha um grande projeto escrito em c ++ que possua mil arquivos .cpp e mil arquivos .h. Digamos que o projeto também dependa de dez bibliotecas estáticas. Digamos que estamos no Windows e criamos nosso projeto no Visual Studio 20xx. Quando você pressiona Ctrl + F7 Visual Studio para começar a compilar toda a solução (suponha que tenhamos apenas um projeto na solução)
Qual o significado da compilação?
A segunda etapa da compilação é feita pelo Linker.Linker deve mesclar todo o arquivo de objeto e criar finalmente a saída (que pode ser um executável ou uma biblioteca)
Etapas para vincular um projeto
error LNK2001: unresolved external symbol "void __cdecl foo(void)" (?foo@@YAXXZ)
Observação
Como resolver esse tipo de erro
Erro de tempo do compilador:
Erro de tempo do vinculador
#pragma once
para permitir que o compilador não inclua um cabeçalho, se já estiver incluído no .cpp atual, compiladofonte
Um erro no compilador / IDE
Recentemente, tive esse problema e, no Visual Studio Express 2013, havia um erro. . Eu tive que remover um arquivo de origem do projeto e adicioná-lo novamente para superar o bug.
Etapas para tentar se você acredita que poderia ser um bug no compilador / IDE:
fonte
Use o vinculador para ajudar a diagnosticar o erro
A maioria dos vinculadores modernos inclui uma opção detalhada que é impressa em vários graus;
Para gcc e clang; você normalmente adicionaria
-v -Wl,--verbose
ou-v -Wl,-v
à linha de comando. Mais detalhes podem ser encontrados aqui;Para MSVC,
/VERBOSE
(em particular/VERBOSE:LIB
) é adicionado à linha de comando do link./VERBOSE
opção vinculador .fonte
O arquivo .lib vinculado está associado a uma.dll
Eu tive o mesmo problema. Digamos que eu tenha projetos MyProject e TestProject. Eu efetivamente vinculei o arquivo lib do MyProject ao TestProject. No entanto, esse arquivo lib foi produzido quando a DLL do MyProject foi criada. Além disso, eu não contive o código-fonte para todos os métodos no MyProject, mas apenas acesso aos pontos de entrada da DLL.
Para resolver o problema, criei o MyProject como um LIB e vinculei o TestProject a esse arquivo .lib (copie e cole o arquivo .lib gerado na pasta TestProject). Posso criar novamente o MyProject como uma DLL. Ele está compilando, pois a lib à qual o TestProject está vinculado contém código para todos os métodos nas classes no MyProject.
fonte
Como as pessoas parecem ser direcionadas para essa pergunta quando se trata de erros do vinculador, adicionarei isso aqui.
Um possível motivo para erros do vinculador no GCC 5.2.0 é que uma nova ABI da biblioteca libstdc ++ agora é escolhida por padrão.
Portanto, se você repentinamente receber erros de vinculador ao mudar para um GCC após a 5.1.0, isso é algo a se verificar.
fonte
Um wrapper em torno do GNU ld que não suporta scripts de vinculador
Alguns arquivos .so são na verdade scripts de vinculador GNU ld , por exemplo, o arquivo libtbb.so é um arquivo de texto ASCII com este conteúdo:
Algumas compilações mais complexas podem não suportar isso. Por exemplo, se você incluir -v nas opções do compilador, poderá ver que o mainwin gcc wrapper mwdip descarta os arquivos de comando do script do vinculador na lista de saída detalhada das bibliotecas a serem vinculadas. Uma solução simples é substituir o comando de entrada do script do vinculador arquivo com uma cópia do arquivo (ou um link simbólico), por exemplo
Ou você pode substituir o argumento -l pelo caminho completo do .so, por exemplo, em vez de
-ltbb
fazer/home/foo/tbb-4.3/linux/lib/intel64/gcc4.4/libtbb.so.2
fonte
Sua ligação consome bibliotecas antes dos arquivos de objeto que se referem a eles
libfoo
dependelibbar
, então sua ligação coloca corretamentelibfoo
anteslibbar
.undefined reference to
algo .#include
d e são de fato definido nas bibliotecas que você está ligando.Os exemplos estão em C. Eles poderiam igualmente ser C ++
Um exemplo mínimo que envolve uma biblioteca estática que você construiu
my_lib.c
my_lib.h
eg1.c
Você constrói sua biblioteca estática:
Você compila seu programa:
Você tenta vinculá-lo
libmy_lib.a
e falhar:O mesmo resultado se você compilar e vincular em uma etapa, como:
Um exemplo mínimo que envolve uma biblioteca de sistema compartilhada, a biblioteca de compactação
libz
eg2.c
Compile seu programa:
Tente vincular seu programa
libz
e falhar:Mesmo se você compilar e vincular de uma só vez:
E uma variação no exemplo 2 envolvendo
pkg-config
:O que você está fazendo de errado?
Na sequência de arquivos e bibliotecas de objetos que você deseja vincular para criar seu programa, você está colocando as bibliotecas antes dos arquivos de objetos que se referem a eles. Você precisa colocar as bibliotecas após os arquivos de objeto que se referem a eles.
Vincule o exemplo 1 corretamente:
Sucesso:
Vincule o exemplo 2 corretamente:
Sucesso:
Vincule a
pkg-config
variação do exemplo 2 corretamente:A explicação
A leitura é opcional a partir daqui .
Por padrão, um comando de ligação gerado pelo GCC, na sua distribuição, consome os arquivos na ligação da esquerda para a direita na sequência da linha de comando. Quando descobre que um arquivo se refere a algo e não contém uma definição, procurará por uma definição em arquivos mais à direita. Se, eventualmente, encontrar uma definição, a referência será resolvida. Se alguma referência permanecer sem solução no final, a ligação falhará: o vinculador não pesquisa para trás.
Primeiro, exemplo 1 , com biblioteca estática
my_lib.a
Uma biblioteca estática é um arquivo indexado de arquivos de objetos. Quando o vinculador encontra
-lmy_lib
na sequência de ligação e descobre que isso se refere à biblioteca estática./libmy_lib.a
, ele quer saber se o seu programa precisa de algum dos arquivos de objetolibmy_lib.a
.Existe apenas um arquivo de objeto
libmy_lib.a
, a sabermy_lib.o
, e apenas uma coisa é definidamy_lib.o
, a funçãohw
.O vinculador decidirá que o seu programa precisa
my_lib.o
se, e somente se, ele já souber que o programa se refere ahw
um ou mais dos arquivos de objetos que ele já adicionou ao programa e que nenhum dos arquivos de objetos que já adicionou contém um definição parahw
.Se isso for verdade, o vinculador extrairá uma cópia da
my_lib.o
biblioteca e a adicionará ao seu programa. Em seguida, seu programa contém uma definição parahw
, portanto, suas referênciashw
são resolvidas .Quando você tenta vincular o programa, como:
o vinculador não foi adicionado
eg1.o
ao programa quando ele vê-lmy_lib
. Porque nesse ponto, ele não viueg1.o
. Seu programa ainda não faz qualquer referência ahw
: ainda não faz qualquer referência em tudo , porque todas as referências Faz estão emeg1.o
.Portanto, o vinculador não adiciona
my_lib.o
ao programa e não tem mais uso paralibmy_lib.a
.Em seguida, ele encontra
eg1.o
e o adiciona ao programa. Um arquivo de objeto na sequência de ligação é sempre adicionado ao programa. Agora, o programa faz uma referênciahw
e não contém uma definição dehw
; mas não resta nada na sequência de ligação que possa fornecer a definição ausente. A referência ahw
acaba não resolvida e a ligação falha.Segundo, exemplo 2 , com biblioteca compartilhada
libz
Uma biblioteca compartilhada não é um arquivo de arquivos de objetos ou algo parecido. É muito mais parecido com um programa que não tem uma
main
função e, em vez disso, expõe vários outros símbolos que define, para que outros programas possam usá-los em tempo de execução.Hoje muitos Linux distros configurar sua GCC toolchain para que seus pilotos linguagem (
gcc
,g++
,gfortran
etc.) instruir o vinculador do sistema (ld
) para bibliotecas compartilhadas de link em um conforme a necessidade base. Você tem uma dessas distros.Isso significa que, quando o vinculador encontra
-lz
na sequência de vinculação e descobre que isso se refere à biblioteca compartilhada (por exemplo)/usr/lib/x86_64-linux-gnu/libz.so
, ele quer saber se alguma referência adicionada ao seu programa que ainda não foi definida possui definições que sejam exportado porlibz
Se isso for verdade, o vinculador não copiará nenhum pedaço
libz
e o adicionará ao seu programa; em vez disso, ele apenas medicará o código do seu programa para que: -Em tempo de execução, o carregador de programa do sistema carrega uma cópia
libz
no mesmo processo que o seu programa sempre que ele carrega uma cópia do seu programa, para executá-lo.No tempo de execução, sempre que seu programa se refere a algo definido em
libz
, essa referência usa a definição exportada pela cópialibz
no mesmo processo.Seu programa deseja se referir a apenas uma coisa que possui uma definição exportada
libz
, a saber, a funçãozlibVersion
, a qual é referida apenas uma vez, emeg2.c
. Se o vinculador adicionar essa referência ao seu programa e encontrar a definição exportada porlibz
, a referência será resolvidaMas quando você tenta vincular o programa como:
a ordem dos eventos está incorreta da mesma maneira que no exemplo 1. No momento em que o vinculador encontra
-lz
, não há referências a nada no programa: eles estão todos inseridoseg2.o
, o que ainda não foi visto. Portanto, o vinculador decide que não tem utilidadelibz
. Quando chegaeg2.o
, o adiciona ao programa e, em seguida, tem uma referência indefinidazlibVersion
, a sequência de ligação está concluída; essa referência não foi resolvida e a ligação falha.Por fim, a
pkg-config
variação do exemplo 2 tem uma explicação agora óbvia. Após a expansão do shell:torna-se:
que é apenas o exemplo 2 novamente.
Eu posso reproduzir o problema no exemplo 1, mas não no exemplo 2
A ligação:
funciona muito bem para você!
(Ou: esse link funcionou bem para você, digamos, no Fedora 23, mas falha no Ubuntu 16.04)
Isso ocorre porque a distribuição na qual a ligação funciona é uma das que não configuram sua cadeia de ferramentas GCC para vincular bibliotecas compartilhadas conforme necessário .
Naquela época, era normal que sistemas do tipo Unix vinculassem bibliotecas estáticas e compartilhadas por regras diferentes. As bibliotecas estáticas em uma sequência de ligação foram vinculadas conforme a necessidade explicada no exemplo 1, mas as bibliotecas compartilhadas foram vinculadas incondicionalmente.
Esse comportamento é econômico no momento do link, porque o vinculador não precisa ponderar se uma biblioteca compartilhada é necessária pelo programa: se for uma biblioteca compartilhada, vincule-a. E a maioria das bibliotecas na maioria das ligações são bibliotecas compartilhadas. Mas também existem desvantagens: -
É antieconômico em tempo de execução , porque pode fazer com que as bibliotecas compartilhadas sejam carregadas junto com um programa, mesmo que não seja necessário.
As diferentes regras de vinculação para bibliotecas estáticas e compartilhadas podem ser confusas para programadores inexperientes, que podem não saber se
-lfoo
o vínculo será resolvido com/some/where/libfoo.a
ou para/some/where/libfoo.so
e não entender a diferença entre bibliotecas compartilhadas e estáticas.Esse trade-off levou à situação cismática hoje. Algumas distribuições alteraram suas regras de vinculação do GCC para bibliotecas compartilhadas para que o princípio conforme necessário se aplique a todas as bibliotecas. Algumas distros mantiveram o estilo antigo.
Por que ainda sinto esse problema, mesmo que eu compile e vincule ao mesmo tempo?
Se eu apenas fizer:
certamente o gcc precisa compilar
eg1.c
primeiro e depois vincular o arquivo de objeto resultantelibmy_lib.a
. Então, como ele pode não saber que o arquivo de objeto é necessário ao fazer o link?Porque compilar e vincular com um único comando não altera a ordem da sequência de vinculação.
Quando você executa o comando acima,
gcc
descobre que deseja compilação + ligação. Por trás dos bastidores, ele gera um comando de compilação e o executa, gera um comando de ligação e o executa, como se você tivesse executado os dois comandos:Assim, a ligação falhar tal como acontece se você não executar esses dois comandos. A única diferença que você nota na falha é que o gcc gerou um arquivo de objeto temporário no caso de compilação + link, porque você não está dizendo para usar
eg1.o
. Nós vemos:ao invés de:
Veja também
A ordem na qual as bibliotecas vinculadas interdependentes são especificadas está incorreta
Colocar as bibliotecas interdependentes na ordem errada é apenas uma maneira pela qual você pode obter arquivos que precisam de definições das coisas que virão posteriormente no link do que os arquivos que fornecem as definições. Colocar as bibliotecas antes dos arquivos de objeto que se referem a eles é outra maneira de cometer o mesmo erro.
fonte
Modelos de amizade ...
Dado o trecho de código de um tipo de modelo com um operador amigo (ou função);
O
operator<<
está sendo declarado como uma função não modelo. Para todos os tiposT
usadosFoo
, é necessário que não haja modelosoperator<<
. Por exemplo, se houver um tipoFoo<int>
declarado, deve haver uma implementação do operador da seguinte maneira;Como não está implementado, o vinculador falha ao encontrá-lo e resulta no erro.
Para corrigir isso, você pode declarar um operador de modelo antes do
Foo
tipo e, em seguida, declarar como amigo, a instanciação apropriada. A sintaxe é um pouco estranha, mas tem a seguinte aparência;O código acima limita a amizade do operador à instanciação correspondente de
Foo
, ou seja, aoperator<< <int>
instanciação é limitada para acessar os membros privados da instanciação deFoo<int>
.Alternativas incluem;
Permitindo que a amizade se estenda a todas as instanciações dos modelos, como segue;
Ou, a implementação para o
operator<<
pode ser feita em linha dentro da definição de classe;Observe que , quando a declaração do operador (ou função) aparece apenas na classe, o nome não está disponível para a pesquisa "normal", apenas para a pesquisa dependente de argumento da cppreference ;
Há mais leituras sobre amigos modelo na cppreference e no C ++ FAQ .
Listagem de código mostrando as técnicas acima .
Como uma observação lateral ao exemplo de código com falha; O g ++ alerta sobre isso da seguinte maneira
fonte
Quando seus caminhos de inclusão são diferentes
Erros do vinculador podem ocorrer quando um arquivo de cabeçalho e sua biblioteca compartilhada associada (arquivo .lib) ficam fora de sincronia. Deixe-me explicar.
Como os vinculadores funcionam? O vinculador combina uma declaração de função (declarada no cabeçalho) com sua definição (na biblioteca compartilhada) comparando suas assinaturas. Você pode obter um erro de vinculador se o vinculador não encontrar uma definição de função que corresponda perfeitamente.
Ainda é possível obter um erro de vinculador, mesmo que a declaração e a definição pareçam corresponder? Sim! Eles podem ter a mesma aparência no código fonte, mas isso realmente depende do que o compilador vê. Essencialmente, você pode acabar com uma situação como esta:
Observe como, embora as duas declarações de função pareçam idênticas no código-fonte, mas são realmente diferentes de acordo com o compilador.
Você pode perguntar como alguém acaba em uma situação como essa? Inclua caminhos, é claro! Se, ao compilar a biblioteca compartilhada, o caminho de inclusão levar
header1.h
e você acabar usandoheader2.h
em seu próprio programa, você ficará coçando o cabeçalho, imaginando o que aconteceu (trocadilho intencional).Um exemplo de como isso pode acontecer no mundo real é explicado abaixo.
Elaboração adicional com um exemplo
Eu tenho dois projetos:
graphics.lib
emain.exe
. Ambos os projetos dependemcommon_math.h
. Suponha que a biblioteca exporte a seguinte função:E então você vai em frente e inclui a biblioteca em seu próprio projeto.
Estrondo! Você recebe um erro no vinculador e não tem idéia do porquê está falhando. O motivo é que a biblioteca comum usa versões diferentes da mesma inclusão
common_math.h
(eu tornei óbvio aqui no exemplo, incluindo um caminho diferente, mas nem sempre é tão óbvio. Talvez o caminho da inclusão seja diferente nas configurações do compilador) .Observe neste exemplo, o vinculador diria que não foi possível encontrar
draw()
, quando na realidade você sabe que obviamente está sendo exportado pela biblioteca. Você pode passar horas coçando a cabeça imaginando o que deu errado. O problema é que o vinculador vê uma assinatura diferente porque os tipos de parâmetro são ligeiramente diferentes. No exemplo,vec3
é um tipo diferente nos dois projetos no que diz respeito ao compilador. Isso pode acontecer porque eles vêm de dois arquivos de inclusão ligeiramente diferentes (talvez os arquivos de inclusão venham de duas versões diferentes da biblioteca).Depurando o vinculador
DUMPBIN é seu amigo, se você estiver usando o Visual Studio. Tenho certeza que outros compiladores têm outras ferramentas semelhantes.
O processo é assim:
[1] Por projeto, quero dizer um conjunto de arquivos de origem que são vinculados para produzir uma biblioteca ou um executável.
EDIT 1: Reescreva a primeira seção para ser mais fácil de entender. Comente abaixo para me informar se algo mais precisa ser corrigido. Obrigado!
fonte
UNICODE
Definições inconsistentesUma compilação UNICODE do Windows é criada com
TCHAR
etc. sendo definida comowchar_t
etc. Quando nãoUNICODE
compilada comTCHAR
definida como compilada com definida comochar
etc. EssasUNICODE
e_UNICODE
definições afetam todos os tipos de "T
" strings ;LPTSTR
,LPCTSTR
E seus alces.Construir uma biblioteca com
UNICODE
definido e tentar vinculá-lo em um projeto ondeUNICODE
não está definido resultará em erros do vinculador, pois haverá uma incompatibilidade na definição deTCHAR
;char
vs.wchar_t
.O erro geralmente inclui uma função, um valor com um tipo
char
ouwchar_t
derivado, que pode incluirstd::basic_string<>
etc. também. Ao navegar pela função afetada no código, muitas vezes haverá uma referência aTCHAR
oustd::basic_string<TCHAR>
etc. Esse é um sinal revelador de que o código foi originalmente destinado a uma compilação UNICODE e Caractere de Byte Múltiplo (ou "restrito") .Para corrigir isso, construa todas as bibliotecas e projetos necessários com uma definição consistente de
UNICODE
(e_UNICODE
).Isso pode ser feito com qualquer um deles;
Ou nas configurações do projeto;
Ou na linha de comando;
A alternativa também é aplicável, se o UNICODE não se destina a ser usado, verifique se as definições não estão definidas e / ou a configuração de vários caracteres é usada nos projetos e aplicada de forma consistente.
Não se esqueça de ser consistente entre as versões "Release" e "Debug".
fonte
Limpar e reconstruir
Uma "limpeza" da compilação pode remover a "madeira morta" que pode ser deixada nas compilações anteriores, compilações com falha, compilações incompletas e outros problemas de compilação relacionados ao sistema de compilação.
Em geral, o IDE ou build inclui alguma forma de função "limpa", mas isso pode não estar configurado corretamente (por exemplo, em um arquivo de composição manual) ou pode falhar (por exemplo, os binários intermediários ou resultantes são somente leitura).
Depois que a "limpeza" for concluída, verifique se a "limpeza" foi bem-sucedida e se todo o arquivo intermediário gerado (por exemplo, um makefile automatizado) foi removido com êxito.
Esse processo pode ser visto como um recurso final, mas geralmente é um bom primeiro passo ; especialmente se o código relacionado ao erro foi adicionado recentemente (localmente ou no repositório de origem).
fonte
Falta "externo" nas
const
declarações / definições de variáveis (apenas C ++)Para pessoas provenientes de C, pode ser uma surpresa que em C ++
const
variáveis globais tenham ligação interna (ou estática). Em C, esse não foi o caso, pois todas as variáveis globais estão implicitamenteextern
(ou seja, quando astatic
palavra-chave está ausente).Exemplo:
correto seria usar um arquivo de cabeçalho e incluí-lo em file2.cpp e file1.cpp
Como alternativa, pode-se declarar a
const
variável em file1.cpp com explícitaextern
fonte
Embora essa seja uma pergunta bastante antiga com várias respostas aceitas, gostaria de compartilhar como resolver um erro obscuro de "referência indefinida a".
Versões diferentes das bibliotecas
Eu estava usando um alias para me referir a
std::filesystem::path
: sistema de arquivos está na biblioteca padrão desde C ++ 17, mas meu programa também precisava compilar em C ++ 14, então decidi usar um alias variável:Digamos que eu tenho três arquivos: main.cpp, file.h, file.cpp:
Observe as diferentes bibliotecas usadas em main.cpp e file.h. Desde main.cpp # include'd " file.h " depois < filesystem >, a versão do sistema de arquivos usado houve o C ++ 17 um . Eu costumava compilar o programa com os seguintes comandos:
$
g++ -g -std=c++17 -c main.cpp
-> compila main.cpp para main.o$
g++ -g -std=c++17 -c file.cpp
-> compila file.cpp e file.h para file.o$
g++ -g -std=c++17 -o executable main.o file.o -lstdc++fs
-> links main.o e file.oDesta forma, qualquer função contida em file.o e utilizado em main.o que necessário
path_t
deu erros "referência indefinida" porque main.o referidostd::filesystem::path
mas file.o parastd::experimental::filesystem::path
.Resolução
Para corrigir isso, eu só precisava alterar <experimental :: filesystem> em file.h para <filesystem> .
fonte
Ao vincular contra bibliotecas compartilhadas, verifique se os símbolos usados não estão ocultos.
O comportamento padrão do gcc é que todos os símbolos estão visíveis. No entanto, quando as unidades de conversão são construídas com a opção
-fvisibility=hidden
, apenas as funções / símbolos marcados com__attribute__ ((visibility ("default")))
são externos no objeto compartilhado resultante.Você pode verificar se os símbolos que você está procurando são externos, invocando:
os símbolos ocultos / locais são mostrados
nm
com o tipo de símbolo em minúsculas, por exemplo, emt
vez de `T para a seção de código:Você também pode usar
nm
com a opção-C
para desmembrar os nomes (se C ++ foi usado).Semelhante às dlls do Windows, seria possível marcar funções públicas com um define, por exemplo,
DLL_PUBLIC
definido como:Que corresponde aproximadamente à versão Windows / MSVC:
Mais informações sobre visibilidade podem ser encontradas no wiki do gcc.
Quando uma unidade de conversão é compilada com
-fvisibility=hidden
os símbolos resultantes, ainda possui ligação externa (mostrada com o tipo de letra maiúscula pornm
) e pode ser usada para ligação externa sem problemas se os arquivos de objeto se tornarem parte de bibliotecas estáticas. A ligação se torna local somente quando os arquivos de objeto são vinculados a uma biblioteca compartilhada.Para descobrir quais símbolos em um arquivo de objeto estão ocultos, execute:
fonte
nm -CD
ounm -gCD
para visualizar símbolos externos. Veja também Visibilidade no wiki do GCC.Arquiteturas diferentes
Você pode ver uma mensagem como:
Nesse caso, significa que os símbolos disponíveis são para uma arquitetura diferente daquela para a qual você está compilando.
No Visual Studio, isso ocorre devido à "Plataforma" errada e você precisa selecionar a correta ou instalar a versão correta da biblioteca.
No Linux, isso pode ocorrer devido à pasta da biblioteca errada (usando em
lib
vez de,lib64
por exemplo).No MacOS, existe a opção de enviar as duas arquiteturas no mesmo arquivo. Pode ser que o link espere que ambas as versões estejam lá, mas apenas uma está. Também pode ser um problema com a pasta errada
lib
/lib64
onde a biblioteca é selecionada.fonte