Подтвердить что ты не робот

С++ аргумент ссылки и ссылка C

Я столкнулся с работой (с компиляторами XLC8 и MSFT9), содержащей файл С++ с функцией, определенной с помощью 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-связью? Я не смог найти информацию об этом.

4b9b3361

Ответ 1

Моя копия n3000.pdf(из здесь), это можно сказать в разделе 7.5. Спецификации Linkage:

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

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

Усиление - это примечание 5 в том же разделе (внимание мое):

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

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

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

Ответ 2

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

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

void real_f(int& n) {
  n++;
}
extern "C" void f(int* p) { // called from C code
  real_f(*p);
}

Ответ 3

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

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

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

Ответ 4

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

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

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

class A {};

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

Ответ 5

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

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

Например:

 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 не нужно хранить в памяти (возможно, он существует только в регистре, возможно, он исчезает в инструкциях).