C++ by-reference argument and C linkage

2020-03-09 08:37发布

I have encountered a working (with XLC8 and MSFT9 compilers) piece of code, containing a C++ file with a function defined with C linkage and a reference argument. This bugs me, as references are C++ only. The function in question is called from C code, where it is declared as taking a pointer argument to the same type in place of the reference argument.

Simplified example:

C++ file:

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

C file:

void f(int *);

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

Now, the word on the street is that most C++ compilers, under the hood, implement references just like a pointer. Is it like that and just pure luck the reason this code works or does it say somewhere in the C++ specification what the result is when you define a function with a reference argument and C linkage? I haven't been able to find any information on this.

5条回答
Rolldiameter
2楼-- · 2020-03-09 09:02

A reference is an alternate name for an object. Technically, there is nothing in C that could map directly to a C++ reference.

The obvious implementation of a reference is as a (constant) pointer that is dereferenced each time it is used. So, depending on how your compiler implements references, your code might work. But it is not correct.

The solution is to write a C function that receives either a real object or a pointer to that object and call the C++ function from this.

查看更多
劳资没心,怎么记你
3楼-- · 2020-03-09 09:13

That's what Bjarne Stroustrup has to say about references:

"Most references are implements are implemented using a pointer variable; that it a reference usually takes up one word of memory. However, a reference that is used purely locally can - and often is - eliminated by the optimizer".

For example:

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

In this case, p needs not be stored in memory (maybe it just exists in a register, maybe it disappears into the instructions).

查看更多
干净又极端
4楼-- · 2020-03-09 09:14

I think you already got some good answers so just pointing out something extra on your question.

My humble understanding is that extern merely creates a C symbol.

So your example still "seems to work" (gcc/g++) even when I add a class object - I had to try ir to believe it:

class A {};

extern "C" void f(int &i, A a) {
    i++;
}
查看更多
三岁会撩人
5楼-- · 2020-03-09 09:15

My copy of n3000.pdf (from here), has this to say in section 7.5—Linkage specifications:

9. Linkage from C++ to objects defined in other languages and to objects defined in C++ from other languages is implementation-defined and language-dependent. Only where the object layout strategies of two language implementations are similar enough can such linkage be achieved.

Since C and C++ are different languages, this means that you can't rely on this "feature" of common compilers.

Stronger is note 5 in the same section (emphasis mine):

If two declarations declare functions with the same name and parameter-type-list (8.3.5) to be members of the same namespace or declare objects with the same name to be members of the same namespace and the declarations give the names different language linkages, the program is ill-formed; no diagnostic is required if the declarations appear in different translation units.

So, I would say that what you did is not guaranteed to work according to the standard, and the compiler is not required to print a diagnostic for the example you have given because the declarations are in different translation units.

FYI, it "works for me" with gcc and g++ version 4.2.1 on Snow Leopard.

查看更多
孤傲高冷的网名
6楼-- · 2020-03-09 09:22

In many cases, but not all, a reference can be implemented with an 'auto-dereferenced' pointer. That any particular compiler treats a function with C linkage and a reference parameter this way is not guaranteed in the C++ standard, and you should treat it as an implementation detail.

It isn't hard to write a forwarding function that takes a pointer and calls your function, if you need to do this without relying on implementation details:

void real_f(int& n) {
  n++;
}
extern "C" void f(int* p) { // called from C code
  real_f(*p);
}
查看更多
登录 后发表回答