Qual é o efeito de extern “C” em C ++?
-
22-07-2019 - |
Pergunta
O que exatamente faz colocando extern "C"
em código C ++ fazer?
Por exemplo:
extern "C" {
void foo();
}
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 idiomacomentá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
eeg
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 porgcc
- C ++ a partir de C:
g++
tell para gerar símbolos unmangled paragcc
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) { }
}
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.
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.
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 include
s 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 ++.