Qual é o efeito do externo "C" em C ++?

1632

O que exatamente a inserção extern "C"no código C ++ faz?

Por exemplo:

extern "C" {
   void foo();
}
Litherum
fonte
82
Gostaria de apresentar a você este artigo: http://www.agner.org/optimize/calling_conventions.pdf Informa muito mais sobre a convenção de chamada e a diferença entre compiladores.
Sam Liao
1
@ Litherum No topo da minha cabeça, está dizendo ao compilador para compilar esse escopo de código usando C, já que você tem um compilador cruzado. Além disso, significa que você possui um arquivo Cpp em que possui essa foo()função.
ha9u63ar
1
@ ha9u63ar Está 'fora do topo da minha cabeça'. O restante do seu comentário também está incorreto. Eu recomendo que você o exclua.
TamaMcGlinn

Respostas:

1559

extern "C" faz com que um nome de função em C ++ tenha ligação 'C' (o compilador não altera o nome) para que o código C do cliente possa vincular (ou seja, usar) sua função usando um arquivo de cabeçalho compatível com 'C' que contenha apenas o declaração de sua função. Sua definição de função está contida em um formato binário (que foi compilado pelo seu compilador C ++) que o vinculador 'C' do cliente vinculará ao uso do nome 'C'.

Como o C ++ possui sobrecarga de nomes de funções e C, o compilador C ++ não pode apenas usar o nome da função como um ID exclusivo para vincular, portanto, gerencia o nome adicionando informações sobre os argumentos. O compilador AC não precisa alterar o nome, pois você não pode sobrecarregar os nomes das funções em C. Quando você declara que uma função possui ligação "C" externa em C ++, o compilador C ++ não adiciona informações de tipo de argumento / parâmetro ao nome usado para ligação.

Só para você saber, é possível especificar explicitamente a ligação "C" para cada declaração / definição individual explicitamente ou usar um bloco para agrupar uma sequência de declarações / definições para ter uma determinada ligação:

extern "C" void foo(int);
extern "C"
{
   void g(char);
   int i;
}

Se você se preocupa com os detalhes técnicos, eles estão listados na seção 7.5 do padrão C ++ 03, aqui está um breve resumo (com ênfase no externo "C"):

  • extern "C" é uma especificação de ligação
  • Todo compilador é necessário para fornecer ligação "C"
  • uma especificação de ligação deve ocorrer apenas no escopo do espaço para nome
  • todos os tipos de funções, nomes de funções e nomes de variáveis ​​têm um vínculo de idioma. Veja o comentário de Richard: Apenas nomes de funções e nomes de variáveis ​​com vínculo externo têm um vínculo de idioma.
  • dois tipos de função com links de idiomas distintos são tipos distintos, mesmo que idênticos
  • ninho de especificações de ligação, o interno determina a ligação final
  • extern "C" é ignorado para os alunos
  • no máximo uma função com um nome específico pode ter ligação "C" (independentemente do espaço para nome)
  • extern "C" força uma função a ter um vínculo externo (não pode torná-la estática). Veja o comentário de Richard: 'estático' dentro de 'extern "C"' é válido; uma entidade assim declarada possui vínculo interno e, portanto, não possui vínculo de idioma
  • A vinculação do C ++ a objetos definidos em outras linguagens e a objetos definidos em C ++ de outras linguagens é definida pela implementação e depende da linguagem. Somente quando as estratégias de layout de objeto de duas implementações de linguagem forem semelhantes o suficiente, essa ligação poderá ser alcançada
Faisal Vali
fonte
22
O compilador C não usa desconfiguração, o que o c ++ faz. Portanto, se você deseja chamar a interface CA de um programa c ++, é necessário declarar claramente que a interface c é "extern c".
Sam Liao
59
@ Faisal: não tente vincular código criado com diferentes compiladores C ++, mesmo que as referências cruzadas sejam todas 'extern "C"'. Geralmente, existem diferenças entre os layouts de classes ou os mecanismos usados ​​para lidar com exceções, ou os mecanismos usados ​​para garantir que as variáveis ​​sejam inicializadas antes do uso, ou outras diferenças, além de que você pode precisar de duas bibliotecas de suporte em tempo de execução C ++ separadas (uma para cada compilador).
Jonathan Leffler
8
'extern "C" força uma função a ter um vínculo externo (não pode torná-lo estático)' está incorreta. 'estático' dentro 'extern "C"' é válido; uma entidade assim declarada possui vínculo interno e, portanto, não possui vínculo de idioma.
Richard Smith
14
'todos os tipos de funções, nomes de funções e nomes de variáveis ​​têm um vínculo de idioma' também está incorreto. Somente nomes de funções e nomes de variáveis ​​com vínculo externo têm vínculo de idioma.
Richard Smith
9
Note que extern "C" { int i; }é uma definição. Pode não ser o que você pretendia, ao lado da não definição de void g(char);. Para torná-lo uma não definição, você precisaria extern "C" { extern int i; }. Por outro lado, a sintaxe de uma declaração sem chaves faz a declaração não-definição: extern "C" int i;é o mesmo queextern "C" { extern int i; }
aschepler
327

Só queria adicionar um pouco de informação, pois ainda não a vi publicada.

Você verá frequentemente o código nos cabeçalhos C da seguinte forma:

#ifdef __cplusplus
extern "C" {
#endif

// all of your legacy C code here

#ifdef __cplusplus
}
#endif

O que isso faz é que ele permite que você use esse arquivo de cabeçalho C com seu código C ++, porque a macro "__cplusplus" será definida. Mas você também pode usá-lo com o código C legado, onde a macro NÃO está definida, para que não veja a construção exclusiva do C ++.

Embora eu também tenha visto código C ++, como:

extern "C" {
#include "legacy_C_header.h"
}

que imagino realiza praticamente a mesma coisa.

Não tenho certeza qual caminho é melhor, mas eu já vi os dois.

UncaAlby
fonte
11
Há uma diferença distinta. No caso do primeiro, se você compilar esse arquivo com o compilador gcc normal, ele gerará um objeto em que o nome da função não é mutilado. Se você vincular objetos C e C ++ ao vinculador, NÃO encontrará as funções. Você precisará incluir esses arquivos de "cabeçalho herdado" com a palavra-chave externa, como no seu segundo bloco de código.
Anne van Rossum
8
@ Anne: O compilador C ++ também procurará nomes não manipulados, porque foi visto extern "C"no cabeçalho). Funciona muito bem, usei essa técnica várias vezes.
Ben Voigt
20
@ Anne: Isso não está certo, o primeiro também está bem. É ignorado pelo compilador C e tem o mesmo efeito que o segundo em C ++. O compilador não poderia se importar menos se encontrar extern "C"antes ou depois de incluir o cabeçalho. Quando chega ao compilador, é apenas um longo fluxo de texto pré-processado.
Ben Voigt
8
@ Anne, não, acho que você foi afetado por algum outro erro na fonte, porque o que você está descrevendo está errado. Nenhuma versão g++errou, para qualquer objetivo, a qualquer momento nos últimos 17 anos, pelo menos. O ponto principal do primeiro exemplo é que não importa se você usa um compilador C ou C ++, nenhuma manipulação de nome será feita para os nomes no extern "C"bloco.
precisa saber é o seguinte
7
"qual é o melhor" - com certeza, a primeira variante é melhor: permite incluir o cabeçalho diretamente, sem nenhum requisito adicional, tanto no código C quanto no C ++. A segunda abordagem é uma solução alternativa para os cabeçalhos C, o autor esqueceu os protetores de C ++ (porém, se forem adicionados posteriormente, as declarações externas "C" aninhadas serão aceitas ...).
Aconcagua
267

Descompile um g++binário gerado para ver o que está acontecendo

main.cpp

void f() {}
void g();

extern "C" {
    void ef() {}
    void eg();
}

/* Prevent g and eg from being optimized away. */
void h() { g(); eg(); }

Compile e desmonte a saída ELF gerada :

g++ -c -std=c++11 -Wall -Wextra -pedantic -o main.o main.cpp
readelf -s main.o

A saída contém:

     8: 0000000000000000     7 FUNC    GLOBAL DEFAULT    1 _Z1fv
     9: 0000000000000007     7 FUNC    GLOBAL DEFAULT    1 ef
    10: 000000000000000e    17 FUNC    GLOBAL DEFAULT    1 _Z1hv
    11: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND _GLOBAL_OFFSET_TABLE_
    12: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND _Z1gv
    13: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND eg

Interpretação

Nós vemos que:

  • efe egforam armazenados em símbolos com o mesmo nome que no código

  • os outros símbolos estavam mutilados. Vamos desmembrá-los:

    $ c++filt _Z1fv
    f()
    $ c++filt _Z1hv
    h()
    $ c++filt _Z1gv
    g()

Conclusão: os dois tipos de símbolos a seguir não foram mutilados:

  • definiram
  • declarado mas indefinido ( Ndx = UND), a ser fornecido no link ou no tempo de execução de outro arquivo de objeto

Então, você precisará de extern "C"ambos ao ligar:

  • C de C ++: diga g++para esperar símbolos não manipulados produzidos porgcc
  • C ++ de C: diga g++para gerar símbolos não manipulados para gccusar

Coisas que não funcionam no externo C

Torna-se óbvio que qualquer recurso do C ++ que exija a troca de nomes não funcionará dentro extern C:

extern "C" {
    // Overloading.
    // error: declaration of C function ‘void f(int)’ conflicts with
    void f();
    void f(int i);

    // Templates.
    // error: template with C linkage
    template <class C> void f(C i) { }
}

Exemplo de C executável mínimo de C ++

Para fins de integridade e novidades, consulte também: Como usar arquivos de origem C em um projeto C ++?

Chamar C a partir de C ++ é bastante fácil: cada função C possui apenas um símbolo não-mutilado possível, portanto, nenhum trabalho extra é necessário.

main.cpp

#include <cassert>

#include "c.h"

int main() {
    assert(f() == 1);
}

CH

#ifndef C_H
#define C_H

/* This ifdef allows the header to be used from both C and C++ 
 * because C does not know what this extern "C" thing is. */
#ifdef __cplusplus
extern "C" {
#endif
int f();
#ifdef __cplusplus
}
#endif

#endif

cc

#include "c.h"

int f(void) { return 1; }

Corre:

g++ -c -o main.o -std=c++98 main.cpp
gcc -c -o c.o -std=c89 c.c
g++ -o main.out main.o c.o
./main.out

Sem extern "C"o link falha com:

main.cpp:6: undefined reference to `f()'

porque g++espera encontrar um mutilado f, que gccnão produziu.

Exemplo no GitHub .

Exemplo de C ++ executável mínimo do exemplo C

Chamar C ++ de C é um pouco mais difícil: precisamos criar manualmente versões não-mutiladas de cada função que queremos expor.

Aqui ilustramos como expor sobrecargas da função C ++ a C.

main.c

#include <assert.h>

#include "cpp.h"

int main(void) {
    assert(f_int(1) == 2);
    assert(f_float(1.0) == 3);
    return 0;
}

cpp.h

#ifndef CPP_H
#define CPP_H

#ifdef __cplusplus
// C cannot see these overloaded prototypes, or else it would get confused.
int f(int i);
int f(float i);
extern "C" {
#endif
int f_int(int i);
int f_float(float i);
#ifdef __cplusplus
}
#endif

#endif

cpp.cpp

#include "cpp.h"

int f(int i) {
    return i + 1;
}

int f(float i) {
    return i + 2;
}

int f_int(int i) {
    return f(i);
}

int f_float(float i) {
    return f(i);
}

Corre:

gcc -c -o main.o -std=c89 -Wextra main.c
g++ -c -o cpp.o -std=c++98 cpp.cpp
g++ -o main.out main.o cpp.o
./main.out

Sem extern "C"ele falha com:

main.c:6: undefined reference to `f_int'
main.c:7: undefined reference to `f_float'

porque g++gerou símbolos mutilados que gccnão conseguem encontrar.

Exemplo no GitHub .

Testado no Ubuntu 18.04.

Ciro Santilli adicionou uma nova foto
fonte
21
Melhor resposta, desde que você 1) mencione explicitamente que extern "C" {ajuda a chamar funções C não manipuladas dentro de programas C ++ , bem como funções C ++ não manipuladas dentro de programas C , que outras respostas não tornam tão óbvias e 2) porque você mostra exemplos distintos de cada. Obrigado!
Gabriel Staples
3
Eu gosto desta resposta muito
selfboot
4
Mãos a melhor resposta, uma vez que mostra como chamar funções sobrecarregadas de c
Gaspa79
1
@JaveneCPPMcGowan, o que faz você pensar que eu tinha um professor de C ++? :-)
Ciro Santilli escreveu:
205

Em todo programa C ++, todas as funções não estáticas são representadas no arquivo binário como símbolos. Esses símbolos são seqüências de texto especiais que identificam exclusivamente uma função no programa.

Em C, o nome do símbolo é igual ao nome da função. Isso é possível porque, em C, duas funções não estáticas podem ter o mesmo nome.

Como o C ++ permite sobrecarga e possui muitos recursos que C - como classes, funções-membro, especificações de exceção - não é possível simplesmente usar o nome da função como o nome do símbolo. Para resolver isso, o C ++ usa o chamado mangling de nome, que transforma o nome da função e todas as informações necessárias (como o número e o tamanho dos argumentos) em uma string de aparência estranha processada apenas pelo compilador e vinculador.

Portanto, se você especificar uma função para ser externa C, o compilador não executará a troca de nome com ela e poderá ser acessado diretamente usando o nome do símbolo como o nome da função.

Isso é útil ao usar dlsym()e dlopen()para chamar essas funções.

sud03r
fonte
o que você quer dizer com útil? o nome do símbolo = nome da função tornaria o nome do símbolo passado para o dlsym conhecido ou outra coisa?
Erro
1
@ Erro: sim. É basicamente impossível, no caso geral, dlopen () uma biblioteca compartilhada em C ++ receber apenas um arquivo de cabeçalho e escolher a função correta a ser carregada. (Em x86, não há uma especificação de desconfiguração nome publicado na forma do Itanium ABI que todos os compiladores x86 Eu sei de uso para mangle C ++ nomes de função, mas nada na língua requer isso.)
Jonathan Tomer
52

C ++ gerencia nomes de funções para criar uma linguagem orientada a objetos a partir de uma linguagem procedural

A maioria das linguagens de programação não é construída sobre as linguagens de programação existentes. O C ++ é construído sobre o C e, além disso, é uma linguagem de programação orientada a objetos, construída a partir de uma linguagem de programação procedural e, por esse motivo, existem expressões C ++ comoextern "C" que fornecem compatibilidade retroativa com C.

Vejamos o seguinte exemplo:

#include <stdio.h>

// Two functions are defined with the same name
// but have different parameters

void printMe(int a) {
  printf("int: %i\n", a);
}

void printMe(char a) {
  printf("char: %c\n", a);
}

int main() {
  printMe("a");
  printMe(1);
  return 0;
}

O compilador CA não compilará o exemplo acima, porque a mesma função printMeé definida duas vezes (mesmo que eles tenham parâmetros diferentes int avs char a).

gcc -o printMe printMe.c && ./printMe;
1 erro. PrintMe é definido mais de uma vez.

Um compilador C ++ compilará o exemplo acima. Não importa se printMeé definido duas vezes.

g ++ -o printMe printMe.c && ./printMe;

Isso ocorre porque um compilador C ++ renomeia implicitamente ( manipula ) funções com base em seus parâmetros. Em C, esse recurso não era suportado. No entanto, quando o C ++ foi construído sobre C, a linguagem foi projetada para ser orientada a objetos e necessária para oferecer suporte à capacidade de criar classes diferentes com métodos (funções) com o mesmo nome e substituir métodos ( substituição de método ) com base em diferentes parâmetros.

extern "C" diz "não altere nomes de funções C"

No entanto, imagine que tenhamos um arquivo C herdado chamado "parent.c", que includeé o nome da função de outros arquivos C herdados, "parent.h", "child.h" etc. Se o arquivo herdado "parent.c" for executado por meio de um compilador C ++, os nomes das funções serão desconfigurados e não corresponderão mais aos nomes de funções especificados em "parent.h", "child.h" etc. - para que os nomes de funções nesses arquivos externos também precisem ser mutilado. A manipulação de nomes de funções em um programa C complexo, com muitas dependências, pode levar ao código quebrado; portanto, pode ser conveniente fornecer uma palavra-chave que diga ao compilador C ++ para não alterar o nome de uma função.

A extern "C"palavra-chave informa ao compilador C ++ para não alterar (renomear) nomes de função C.

Por exemplo:

extern "C" void printMe(int a);

tfmontague
fonte
não podemos usar extern "C"se tivermos apenas um dllarquivo? Quero dizer, se não temos um arquivo de cabeçalho e apenas temos um arquivo de origem (apenas implementações) e o uso de sua função via ponteiro de função. nesse estado, usamos apenas funções (independentemente de seu nome).
BattleTested
@tfmontague, para mim você acertou em cheio! direto na cabeça.
Artanis Zeratul
29

Nenhum cabeçalho C pode ser compatível com C ++ apenas envolto em "C" externo. Quando os identificadores em um cabeçalho C entram em conflito com as palavras-chave C ++, o compilador C ++ se queixa disso.

Por exemplo, vi o seguinte código falhar em um g ++:

extern "C" {
struct method {
    int virtual;
};
}

Meio que faz sentido, mas é algo a ter em mente ao portar código C para C ++.

Sander Mertens
fonte
14
extern "C"significa usar ligação C, conforme descrito por outras respostas. Não significa "compilar o conteúdo como C" ou qualquer coisa. int virtual;é inválido em C ++ e a especificação de ligação diferente não altera isso.
MM
1
... ou no modo geralmente, qualquer código que contenha erro de sintaxe não será compilado.
Valentin Heinitz 1/11
4
@ValentinHeinitz naturalmente, embora usar "virtual" como um identificador em C não seja um erro de sintaxe. Eu só queria salientar que você não pode usar automaticamente nenhum cabeçalho C em C ++ colocando "C" externo em torno dele.
Sander Mertens
28

Ele altera o vínculo de uma função de forma que a função possa ser chamada de C. Na prática, isso significa que o nome da função não é mutilado .

Russo empregado
fonte
3
Mangled é o termo geralmente usado ... Não acredito que eu já tenha visto 'decorado' usado com esse significado.
Matthew Scharley
1
A Microsoft (pelo menos parcialmente) usa decorações decoradas em vez de mutiladas em sua documentação. eles até nomeiam sua ferramenta para decorar um nome undname.
René Nyffenegger 31/01
20

Ele informa o compilador C ++ a procurar os nomes dessas funções no estilo C ao vincular, porque os nomes das funções compiladas em C e C ++ são diferentes durante o estágio de vinculação.

Mark Rushakoff
fonte
12

"C" externo deve ser reconhecido por um compilador C ++ e notificar o compilador de que a função observada é (ou deve ser) compilada no estilo C. Para que, ao vincular, ele vincule à versão correta da função de C.

Flami
fonte
6

Eu usei 'extern "C"' antes para arquivos dll (biblioteca de vínculo dinâmico) tornarem a função main () "exportável" para que possa ser usado posteriormente em outro executável da dll. Talvez um exemplo de onde eu costumava usá-lo possa ser útil.

DLL

#include <string.h>
#include <windows.h>

using namespace std;

#define DLL extern "C" __declspec(dllexport)
//I defined DLL for dllexport function
DLL main ()
{
    MessageBox(NULL,"Hi from DLL","DLL",MB_OK);
}

Exe

#include <string.h>
#include <windows.h>

using namespace std;

typedef LPVOID (WINAPI*Function)();//make a placeholder for function from dll
Function mainDLLFunc;//make a variable for function placeholder

int main()
{
    char winDir[MAX_PATH];//will hold path of above dll
    GetCurrentDirectory(sizeof(winDir),winDir);//dll is in same dir as exe
    strcat(winDir,"\\exmple.dll");//concentrate dll name with path
    HINSTANCE DLL = LoadLibrary(winDir);//load example dll
    if(DLL==NULL)
    {
        FreeLibrary((HMODULE)DLL);//if load fails exit
        return 0;
    }
    mainDLLFunc=(Function)GetProcAddress((HMODULE)DLL, "main");
    //defined variable is used to assign a function from dll
    //GetProcAddress is used to locate function with pre defined extern name "DLL"
    //and matcing function name
    if(mainDLLFunc==NULL)
    {
        FreeLibrary((HMODULE)DLL);//if it fails exit
        return 0;
    }
    mainDLLFunc();//run exported function 
    FreeLibrary((HMODULE)DLL);
}
SturmCoder
fonte
4
Falso. extern "C"e __declspec(dllexport)não são relacionados. O primeiro controla a decoração do símbolo, o segundo é responsável por criar uma entrada de exportação. Você também pode exportar um símbolo usando a decoração de nome C ++. Além de perder completamente o objetivo desta questão, também existem outros erros no exemplo de código. Por um lado, mainexportado da sua DLL não declara um valor de retorno. Ou convocando convenção, para esse assunto. Ao importar, você atribui uma convenção de chamada aleatória ( WINAPI) e usa o símbolo errado para compilações de 32 bits (deve ser _mainou _main@0). Desculpe -1.
11nspectable
1
Isso apenas repetiu, que você não sabe, o que está fazendo, mas fazê-lo dessa maneira parece funcionar para você, para uma lista não divulgada de plataformas de destino. Você não abordou as questões que levantei no meu comentário anterior. Isso ainda é um voto negativo, por estar muito errado (há mais, que não se encaixava em um único comentário).
IInspectable
1
A publicação de uma resposta no Stack Overflow implica que você sabe o que está fazendo. Isso é esperado. Quanto à sua tentativa de "impedir a corrupção da pilha durante a execução" : sua assinatura de função especifica um valor de retorno do tipo void*, mas sua implementação não retorna nada. Isso vai voar muito bem ... #
Inspectable
1
Se você implementa algo que parece funcionar, por pura sorte, claramente você não sabe o que está fazendo (seu exemplo "de trabalho" se enquadra nessa categoria). É um comportamento indefinido e parecer funcionar é uma forma válida de comportamento indefinido. Ainda está indefinido. Eu apreciaria muito se você exercesse mais diligência no futuro. Parte disso pode estar excluindo esta resposta proposta.
IInspectable
1
Você está reinterpretando uma função que não retorna nada como uma função que retorna um ponteiro. É pura sorte que o x86 seja muito indulgente em relação às assinaturas de funções incompatíveis e, em particular, retorne valores do tipo integral. Seu código funciona apenas por coincidência. Se você não concorda, precisa explicar por que seu código funciona de maneira confiável.
IInspectable
5

extern "C"é uma especificação de ligação usada para chamar funções C nos arquivos de origem Cpp . Podemos chamar funções C, escrever variáveis ​​e incluir cabeçalhos . A função é declarada na entidade externa e é definida fora. A sintaxe é

Tipo 1:

extern "language" function-prototype

Tipo 2:

extern "language"
{
     function-prototype
};

por exemplo:

#include<iostream>
using namespace std;

extern "C"
{
     #include<stdio.h>    // Include C Header
     int n;               // Declare a Variable
     void func(int,int);  // Declare a function (function prototype)
}

int main()
{
    func(int a, int b);   // Calling function . . .
    return 0;
}

// Function definition . . .
void func(int m, int n)
{
    //
    //
}
Yogeesh HT
fonte
3

Esta resposta é para os impacientes / com prazos a cumprir, apenas uma explicação parcial / simples está abaixo:

  • em C ++, você pode ter o mesmo nome na classe por sobrecarga (por exemplo, como todos eles têm o mesmo nome e não podem ser exportados como estão na DLL, etc.), a solução para esses problemas é que eles são convertidos em cadeias diferentes (chamadas de símbolos ), os símbolos contabilizam o nome da função, também os argumentos, para que cada uma dessas funções, mesmo com o mesmo nome, possa ser identificada exclusivamente (também chamada de nome desconfigurado)
  • em C, você não tem sobrecarga, o nome da função é único (portanto, uma sequência separada para identificar o nome de uma função exclusivamente não é necessária, portanto, símbolo é o próprio nome da função)

Portanto,
em C ++, com o nome desconectado identifica cada função
em C, mesmo sem o nome desconectado identifica cada função

Para alterar o comportamento do C ++, ou seja, para especificar que o nome incorreto não ocorra para uma função específica, você pode usar "C" externo antes do nome da função, por qualquer motivo, como exportar uma função com um nome específico de uma dll , para uso de seus clientes.

Leia outras respostas, para obter respostas mais detalhadas / corretas.

Manohar Reddy Poreddy
fonte
1

Ao misturar C e C ++ (ou seja, a. Chamando a função C a partir de C ++; e b. Chamando a função C ++ a partir de C), o nome C ++ incorreto causa problemas de vinculação. Tecnicamente falando, esse problema ocorre apenas quando as funções de chamada já foram compiladas no binário (provavelmente, um arquivo de biblioteca * .a) usando o compilador correspondente.

Portanto, precisamos usar extern "C" para desativar o nome desconectado em C ++.

Trombe
fonte
0

Sem entrar em conflito com outras boas respostas, adicionarei um pouco do meu exemplo.

O que exatamente o C ++ Compiler faz: ele gerencia os nomes no processo de compilação; portanto, exigimos que o compilador trate a C implementação especialmente.

Quando estamos criando e adicionando classes C ++ extern "C", estamos dizendo ao nosso compilador C ++ que estamos usando a convenção de chamada em C.

Razão (estamos chamando a implementação C em C ++): queremos chamar a função C em C ++ ou a função C ++ em C (classes C ++ ... etc, não funcionam em C).

Susobhan Das
fonte
Bem-vindo ao Stack Overflow. Se você decidir responder a uma pergunta antiga que tenha respostas bem estabelecidas e corretas, adicionar uma nova resposta no final do dia pode não lhe dar crédito. Se você tiver alguma informação nova e distinta, ou estiver convencido de que as outras respostas estão erradas, adicione uma nova resposta, mas "mais uma resposta" fornecerá a mesma informação básica muito tempo depois que a pergunta for feita normalmente " você ganha muito crédito. Francamente, não acho que exista algo novo nesta resposta.
Jonathan Leffler
Bem, eu deveria ter lembrado o seu ponto - ok
Susobhan Das
-1

Uma função void f () compilada por um compilador C e uma função com o mesmo nome void f () compilada por um compilador C ++ não são a mesma função. Se você escreveu essa função em C e tentou chamá-la de C ++, o vinculador procuraria a função C ++ e não encontraria a função C.

extern "C" informa ao compilador C ++ que você possui uma função que foi compilada pelo compilador C. Depois que você disser que foi compilado pelo compilador C, o compilador C ++ saberá como chamá-lo corretamente.

Ele também permite que o compilador C ++ compile uma função C ++ de forma que o compilador C possa chamá-la. Essa função seria oficialmente uma função C, mas como é compilada pelo compilador C ++, ela pode usar todos os recursos do C ++ e possui todas as palavras-chave do C ++.

gnasher729
fonte
O compilador C ++ pode compilar uma extern "C"função - e (sujeito a algumas restrições) será possível chamar pelo código compilado por um compilador C.
Jonathan Leffler