Ссылочный аргумент C ++ и связь C - PullRequest
13 голосов
/ 15 декабря 2009

Я обнаружил работающий (с компиляторами XLC8 и MSFT9) фрагмент кода, содержащий файл C ++ с функцией, определенной с помощью связи C, и ссылочный аргумент. Это беспокоит меня, так как ссылки только на C ++. Рассматриваемая функция вызывается из кода C, где она объявляется как принимающая аргумент указателя на тот же тип вместо ссылочного аргумента.

Упрощенный пример :

Файл C ++ :

extern "C" void f(int &i)
{
    i++;
}

Файл C :

void f(int *);

int main()
{
    int a = 2;
    f(&a);
    printf("%d\n", a);  /* Prints 3 */
}

Теперь, на улице говорят, что большинство компиляторов C ++ реализуют ссылки так же, как указатель. Так ли это и просто везение, почему этот код работает, или он говорит где-то в спецификации C ++, каков результат, когда вы определяете функцию со ссылочным аргументом и связью C? Я не смог найти никакой информации по этому вопросу.

Ответы [ 5 ]

11 голосов
/ 15 декабря 2009

Во многих случаях, но не во всех, ссылка может быть реализована с помощью указателя с автоматической разыменовкой. То, что какой-либо конкретный компилятор обрабатывает функцию со связью C и ссылочным параметром таким способом, не гарантируется в стандарте C ++, и вы должны рассматривать это как деталь реализации.

Нетрудно написать функцию пересылки, которая принимает указатель и вызывает вашу функцию, если вам нужно сделать это, не полагаясь на детали реализации:

void real_f(int& n) {
  n++;
}
extern "C" void f(int* p) { // called from C code
  real_f(*p);
}
5 голосов
/ 15 декабря 2009

Моя копия n3000.pdf (из здесь ), о которой говорится в разделе 7.5 - Характеристики сцепления :

9 . Связь с C ++ с объектами, определенными в другие языки и определенные объекты в C ++ из других языков есть определяется реализацией и зависит от языка. Только где стратегии размещения объектов двух языковые реализации похожи достаточно, чтобы такая связь была достигнута.

Поскольку C и C ++ являются разными языками, это означает, что вы не можете полагаться на эту «особенность» обычных компиляторов.

Сильнее примечание 5 в том же разделе (выделено мое):

Если два объявления объявляют функции с тем же именем и список параметров типа (8.3.5) члены одного и того же пространства имен или объявлять объекты с тем же именем быть членами одного и того же пространства имен и декларации дают имена различные языковые связи, программа плохо сформирована; без диагностики требуется, если объявления появляются в разных единицах перевода .

Итак, я бы сказал, что то, что вы сделали, не гарантирует работу в соответствии со стандартом, и компилятору не требуется печатать диагностику для примера, который вы привели, потому что объявления находятся в разных единицах перевода .

К вашему сведению, он "работает для меня" с gcc и g ++ версии 4.2.1 на Snow Leopard.

3 голосов
/ 15 декабря 2009

A ссылка - альтернативное имя для объекта. Технически, в C нет ничего, что можно было бы напрямую сопоставить со ссылкой на C ++.

Очевидная реализация ссылки - это (постоянный) указатель, который разыменовывается при каждом его использовании. Таким образом, в зависимости от того, как ваш компилятор реализует ссылки, ваш код может работать. Но это не правильно.

Решение состоит в том, чтобы написать функцию C, которая получает либо реальный объект, либо указатель на этот объект, и вызвать функцию C ++ из этого.

1 голос
/ 19 ноября 2010

Я думаю, что вы уже получили несколько хороших ответов, поэтому просто укажите что-то еще на свой вопрос.

Мое скромное понимание состоит в том, что extern просто создает символ C.

Таким образом, ваш пример все еще «работает» (gcc / g ++), даже когда я добавляю объект класса - мне пришлось попытаться верить этому:

class A {};

extern "C" void f(int &i, A a) {
    i++;
}
0 голосов
/ 15 декабря 2009

Вот что говорит Бьярн Страуструп о ссылках:

" Большинство ссылок являются инструментами, реализованными с использованием переменной указателя; эта ссылка обычно занимает одно слово в памяти. Однако ссылка, которая используется исключительно локально, может - и часто исключается - оптимизатором ».

Например:

 struct S 
 { 
    int a;
    int b[100]; 
 };  // just an example

 void do_something(const vector<S>& v)
 {
    for (int i=0; i<v.size(); ++i)
    {
        int(&p)[100] = v[i].b;
        for (int j=0; j<100; ++j) 
           cout <<p[j];
    }
 }

В этом случае p не нужно хранить в памяти (может быть, он просто существует в регистре, может быть, он исчезает в инструкциях).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...