2

For example, suppose I have the following files: hello.cpp, hello.h, and main.c

In hello.cpp say I have the following:

#include "hello.h"
extern "C" void test_func(int &a, int b){
   some stuff   
}

In hello.h I have the following:

#ifdef __cplusplus
extern "C" {

#endif
void test_func(int& a, int b);

#ifdef __cplusplus
}
#endif

Here is where I am confused. If I have the following in main.c:

#include "hello.h"
extern void test_func(int*, int);

then this will not compile properly. It tells me that I have errors in my hello.h file, I assume this is because C does not support pass by reference? I noticed that if I change my hello.h file to read "void test_func(int*a, int b)" then this will compile properly.

However, If I do not have #include "hello.h" in my main.c file, then it will also compile properly. And, I am able to call test_func from main.c even without including hello.h. Is declaring the function prototype enough? And why? If I wanted to include hello.h would that mean I have to make it C compatible and not have any functions that pass by reference?

Very new to all this so thanks in advance for anyone's help.

5
  • That's how C works, you can read up on it. Undeclared functions are assumed to return int, and have no argument prototypes-- meaning you can pass them anything without error checking. Nice and minimal. Commented Mar 5, 2014 at 23:43
  • @alexis: C89 worked that way, but modern C does not allow it. Commented Mar 5, 2014 at 23:45
  • Ok so why was there no error when the OP omitted the header? Commented Mar 5, 2014 at 23:52
  • The OP did include a prototype (manually), and @aschepler is wrong, current editions of the standard (open-std.org/jtc1/sc22/wg14/www/standards.html) include "If the expression that denotes the called function has a type that does not include a prototype,..." with what appear to be exactly the same promotion rules as are in C89. Commented Mar 6, 2014 at 0:43
  • C (even C11) allows you to call a function without a function declaration, but it then becomes the responsibility of the programmer to ensure that the function is called with the correct arguments (and argument types). Also the compiler will assume that the function returns an int. If these conditions are not met (and there may be function implementations that are not possible to call correctly without a prototype) then undefined behavior will be the result. One of the possible behaviors of UB is the appearance that things work as you expect. But that's a fragile result. Commented Mar 6, 2014 at 1:10

2 Answers 2

2

Declaring the same C function with two different prototypes is undefined behavior. Don't do it that way. (This particular case was likely to "work" anyway because a typical compiler passes a pointer type by putting a memory address on the stack, and passes a reference type by putting a memory address on the stack.)

So yes, your current hello.h cannot be used from C code at all. If you want a function to cross the C-C++ "boundary", it should have a C-like declaration. (Except it can be in a namespace from the C++ side; this namespace is ignored on the C side.)

Instead, you could create a C wrapper around your C++ function:

// hello.h
#ifndef HELLO_H_GUARD_
#define HELLO_H_GUARD_

#ifdef __cplusplus
void test_func(int &a, int b);

extern "C"
#endif
void test_func_c(int *a, int b);

#endif

// hello.cpp
void test_func(int &a, int b) {
    //...
}

extern "C"
void test_func_c(int *a, int b) {
    test_func(*a, b);
}

By the way, you mentioned a "main.c" file. When mixing C and C++, the main function should be in a C++ source file. If you want, that can be just a wrapper that calls your "real" C main-like function.

Sign up to request clarification or add additional context in comments.

5 Comments

At least the OP didn't have hello.h, hello.cpp, and hello.c. That would have caused some fun confusion...
Thanks. So I see that the header file does need to be C compatible. However, I'm still a little confused about how omitting the header file in main.c allows me to still use the function. Is this because of the "extern" in front of the prototype in main.c?
The line extern void test_func(int*, int); tells the C compiler: There's a function named test_func with argument types int* and int, defined elsewhere. Trouble is, that's a lie, and neither the compiler nor linker happened to notice the problem.
Oh okay I see. Thank you so much! That makes a lot of sense now. So when the compiler is told, this function is defined "elsewhere", where exactly does it look?
At the linking stage, it scans through all the object files and libraries you pass into the linker, looking for that symbol.
0

Calling a function in one object file from another object file is strictly an issue of whether the linker can find an object of the correct name in the other object file. Normally, C++ compilers do name mangling to make sure that you don't call the wrong version of a function -- for example, if I compile:

void test_func(int &a, int b) {
         return;
}

and then call nm, I get something like:

0000000000000000 T _Z9test_funcRii

However, 'extern "C"' tells the compiler to not do name mangling, so I get:

0000000000000000 T test_func

A C program is perfectly free to call test_func with whatever arguments it wants to, and it's up to the coder to make sure the arguments he passes are correct. In your case, the only reason it worked is because your C++ compiler apparently implements pass-by-reference using pointers (which is unsurprising).

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.