Pergunta

O que exatamente faz colocando extern "C" em código C ++ fazer?

Por exemplo:

extern "C" {
   void foo();
}
Foi útil?

Solução

externo "C" faz um nome de função em C ++ têm ligação 'C' (compilador não mangle o nome) para que o código do cliente C pode ligar para (ou seja, utilização) a sua função usando um arquivo de cabeçalho compatível 'C' que contém apenas a 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 vinculador o cliente 'C', então, conectar-se a usar o nome 'C'.

Uma vez que C ++ tem a sobrecarga de nomes de funções e C não, o compilador C ++ pode não usar apenas o nome da função como uma identificação única para link para, assim que mutila o nome adicionando informações sobre os argumentos. compilador AC não precisa mangle o nome desde que você não pode sobrecarregar os nomes de função em C. Quando você estado que uma função tem ligação extern "C" em C ++, o compilador C ++ não adiciona informações de tipo argumento / parâmetro para o nome usado para ligação.

Só para você saber, você pode especificar ligação "C" para cada indivíduo declaração / definição explícita ou usar um bloco para agrupar uma sequência de declarações / definições para ter uma certa ligação:

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

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

  • externo "C" é uma ligação-especificação
  • Cada compilador é necessário para fornecer "C" ligação
  • uma especificação de ligação deve ocorrer apenas no âmbito namespace
  • todos os tipos de função, nomes de funções e nomes de variáveis ??têm uma ligação idioma comentário Ver de Richard: Somente nomes de funções e nomes de variáveis ??com ligação externa têm uma linguagem ligação
  • dois tipos de função com vínculos de linguagem distintas são tipos distintos, mesmo que de outra forma idêntica
  • ligação características ninho, um interior determina a ligação final
  • externo "C" é ignorado para os membros da classe
  • no máximo uma função com um nome específico pode ter ligação "C" (independentemente do namespace)
  • forças
  • extern "C" uma função de ter ligação externa (não pode torná-lo estático) comentário Ver de Richard: 'estática' dentro 'extern "C"' é válido; uma entidade assim declarado tem ligação interna, e assim não tem uma ligação idioma
  • Vinculação C ++ para objetos definidos em outros idiomas e objetos definidos no C ++ a partir de outras línguas é definido pela implementação e dependentes do idioma. Apenas onde as estratégias de layout objeto de duas implementações de idioma são ser alcançado semelhante o suficiente lata tal ligação

Outras dicas

Só queria acrescentar um pouco de informação, já que eu não vi isso postado ainda.

Você muitas vezes ver o código nos cabeçalhos C assim:

#ifdef __cplusplus
extern "C" {
#endif

// all of your legacy C code here

#ifdef __cplusplus
}
#endif

O que Isto consegue é que ele permite que você use esse arquivo de cabeçalho C com o seu código C ++, porque a macro "__cplusplus" será definida. Mas você pode também ainda usá-lo com o seu código C legado, onde a macro é não definido, por isso não vai ver a construção excepcionalmente C ++.

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

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

que eu imagino Realiza a mesma coisa.

Não tenho certeza qual é o melhor caminho, mas eu vi ambos.

Em cada programa C ++, todas as funções não-estáticos são representados no arquivo binário como símbolos. Estes símbolos são cadeias de texto especiais que identificam unicamente uma função no programa.

Em C, o nome do símbolo é o mesmo que o nome da função. Isto é possível porque em C há duas funções não-estáticos podem ter o mesmo nome.

Porque C ++ permite sobrecarga e tem muitas características que C não - como classes, funções de 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, C ++ usa o chamado desconfiguração do nome, que transforma o nome da função e todas as informações necessárias (como o número e tamanho dos argumentos) em alguma corda esquisito processado somente pelo compilador e vinculador.

Então, se você especificar uma função para ser extern C, o compilador não executa nome desconfiguração com ele e ele pode ser diretamente acessada usando seu nome de símbolo como o nome da função.

Este vem acessível ao usar dlsym() e dlopen() para chamar essas funções.

Decompile um g++ gerado binário 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(); }

Compilar com GCC 4.8 Linux ELF saída:

g++ -c main.cpp

Decompile tabela de símbolos:

readelf -s main.o

A saída contém:

Num:    Value          Size Type    Bind   Vis      Ndx Name
  8: 0000000000000000     6 FUNC    GLOBAL DEFAULT    1 _Z1fv
  9: 0000000000000006     6 FUNC    GLOBAL DEFAULT    1 ef
 10: 000000000000000c    16 FUNC    GLOBAL DEFAULT    1 _Z1hv
 11: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND _Z1gv
 12: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND eg

Interpretação

Vemos que:

  • ef e eg foram armazenados em símbolos com o mesmo nome no código

  • os outros símbolos foram mutilados. Vamos unmangle eles:

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

Conclusão: ambos os seguintes tipos de símbolo foram não mutilado:

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

Então você vai precisar extern "C" tanto ao chamar:

  • C do C ++: g++ Diga aos espera símbolos unmangled produzidos por gcc
  • C ++ a partir de C: g++ tell para gerar símbolos unmangled para gcc para uso

Coisas que não funcionam em extern C

Torna-se óbvio que qualquer recurso de C ++ que requer desconfiguração do nome não vai 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) { }
}

Minimal C executável do exemplo C ++

Por uma questão de exaustividade e para os newbs lá fora, ver também: Como usar arquivos de origem C em um projeto de C ++?

Chamar C do C ++ é muito fácil:. Cada função C tem apenas símbolo de uma possível não-mutilado, o trabalho de forma que nenhum extra é necessário

main.cpp

#include <cassert>

#include "c.h"

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

c.h

#ifndef C_H
#define C_H

/* This ifdef allows the header to be used from both C and C++. */
#ifdef __cplusplus
extern "C" {
#endif
int f();
#ifdef __cplusplus
}
#endif

#endif

C.C.

#include "c.h"

int f(void) { return 1; }

Executar:

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 falhar com:

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

porque espera g++ para encontrar um f mutilado, que gcc não produziu.

Exemplo no GitHub .

Minimal C ++ executável a partir de C exemplo

Chamar C ++ a partir de C é um pouco mais difícil:. Temos que criar manualmente versões não mutilados de cada função que deseja expor

Aqui ilustramos como expor C ++ sobrecargas de função para 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);
}

Executar:

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++ gerado símbolos mutilados que gcc não consegue encontrar.

Exemplo no GitHub .

Testado no Ubuntu 18.04.

C ++ mangles função nomes para criar uma linguagem orientada a objetos a partir de uma linguagem procedural

A maioria das linguagens de programação não são construídos no topo das linguagens de programação existentes. C ++ é construído no topo do C, e, além disso, é uma linguagem de programação orientada a objetos construídos a partir de uma linguagem de programação procedural, e por essa razão há expressões C ++ como extern "C" que fornecem a compatibilidade com C.

Vamos olhar o exemplo a seguir:

#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;
}

compilador A C não irá compilar o exemplo acima, porque o mesmo printMe função é definida por duas vezes (embora eles têm diferentes parâmetros int a vs char a).

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

A C ++ compilador compilar o exemplo acima. Ele não se importa que printMe é definida duas vezes.

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

Isto porque um compilador C ++ implicitamente renomeações ( mangles ) funções com base em seus parâmetros. Em C, esta característica não foi apoiado. No entanto, quando C ++ foi construída sobre C, a língua foi concebido para ser orientado para o objecto, e necessário para suportar a capacidade de criar diferentes classes com métodos (funções) com o mesmo nome, e para substituir os métodos ( método de substituição ) com base em parâmetros diferentes

extern "C" diz: "Não mangle C nomes de função"

No entanto, imaginar que temos um arquivo legado C chamado "parent.c" que includes funcionar nomes de outros arquivos legado C "parent.h", "child.h", etc. Se o legado "parent.c" arquivo é executado através de um compilador C ++, em seguida, os nomes das funções será mutilado, e eles já não irá corresponder os nomes das funções especificadas no "parent.h", "child.h", etc - para que os nomes das funções nesses arquivos externos faria também precisam ser mutilado. Desconfiguração nomes de função através de um programa em C complexo, aqueles com lotes de dependências, pode levar à quebrado código; por isso pode ser conveniente para fornecer uma palavra-chave que pode dizer ao compilador C ++ não mangle um nome de função.

A palavra-chave extern "C" diz um compilador C ++ nomes não mangle (renomear) C função. Exemplo de uso: extern "C" void printMe(int a);

Ele muda a ligação de uma função de tal forma a que a função é exigível a partir da prática C. Em que significa que o nome da função não é mutilado .

Não é qualquer C-header pode ser feita compatível com C ++ meramente envolvimento em extern "C". Quando identificadores em um conflito C-cabeçalho com C ++ palavras-chave do compilador C ++ vai reclamar sobre isso.

Por exemplo, eu vi o código a seguir falha em um g ++:

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

isso faz sentido, mas é algo a ter em mente quando se portar C-código para C ++.

Informa o compilador C ++ para procurar os nomes dessas funções em um C-estilo ao ligar, porque os nomes de funções compiladas em C e C ++ são diferentes durante a fase de ligação.

externo "C" é para ser reconhecido por um compilador C ++ e para notificar o compilador que a função observado é (ou ser) compilados em estilo C. De modo que ao ligar, ele relação para a versão correta da função de C.

Eu usei 'extern 'C'' antes de arquivos de DLL (dynamic link library) para fazer etc. main () função "exportáveis" para que ele possa ser usado mais tarde em outro executável a partir de dll. Talvez um exemplo de onde eu costumava usá-lo pode 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);
}

extern "C" é uma especificação de ligação que é usado para funções chamada C nos arquivos código CPP . Podemos funções chamada C, Variáveis ??de escrita, e incluir cabeçalhos . Função é declarada em entidade externa e é definida fora. 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)
{
    //
    //
}

Esta resposta é para os impacientes / tem prazos a cumprir para, apenas uma parte / explicação simples é abaixo:

  • em C ++, você pode ter mesmo nome na classe através de sobrecarga (por exemplo, uma vez que todos eles são mesmo nome não pode ser exportado como-é de dll, etc.) solução para estes problemas é que eles são convertidos em cadeias diferentes (chamados de símbolos), símbolos representa o nome da função, também os argumentos, de modo que cada uma destas funções, mesmo com o mesmo nome, pode ser identificada exclusivamente (também chamado, desconfiguração do nome)
  • em C, você não tem a sobrecarga, o nome da função é única (assim, uma seqüência separada para identificar o nome de uma função exclusivamente não é necessária, de modo símbolo é o próprio nome da função)

Assim
em C ++, com o nome desconfiguração exclusivamente identidades cada função
em C, mesmo sem nome desconfiguração exclusivamente identidades cada função

Para alterar o comportamento de C ++, isto é, para especificar que desconfiguração do nome não deve acontecer para uma determinada função, você pode usar externo "C" antes da função nome, por qualquer motivo, como exportar uma função com um nome específico a partir de uma dll, para uso por seus clientes.

Leia outras respostas, por mais detalhado / respostas mais corretas.

Ao misturar C e C ++ (isto é, uma função de chamada C a partir de C ++;.. E b chamando função C ++ a partir de C), o nome C ++ calandrar causas que ligam problemas. Tecnicamente falando, este problema só acontece quando as funções callee já foram compilados em binário (muito provavelmente, a * .a arquivo de biblioteca) usando o compilador correspondente.

Então, precisamos usar extern "C" para desativar o nome desconfiguração em C ++.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top