Function Parameters

CS 301 Lecture, Dr. Lawlor

Here's some C++ code that calls an external function "bar".  Note that this code gives a link error when you try to run it in NetRun, because "bar" is never defined. The "extern "C"" tells C++ to just look for a C-style plain function "bar", instead of a fancy overloaded C++ function "bar(int,int,int)".
extern "C" int bar(int a,int b,int c);

int foo(void) {
return bar(0xA0B1C2D3, 0xE0E1E2E3, 0xF0F1F2F3);
}

(executable NetRun link)

We can actually write this "bar" function in assembly, like this:
global bar
bar:
mov eax,edi
ret

(Try this in NetRun now!)

When we get called, our first parameter is sitting in register edi as usual.

The "global" keyword in assembly tells the assembler to make a symbol, in this case bar, visible from outside the file.

The "Link With:" box tells NetRun to link together two different projects, in this case one in C++ and the other in assembly.

Argument Passing

C++ is kinda asymmetrical, because "int" or "long" parameters are placed directly in registers (like edi or rdi), while arrays are always passed via pointer (rdi contains the *address* of the array, not the actual integers in the array).  C/C++ do this because you don't have enough registers to store the entire array, plus it'd be expensive to copy the array into and out of registers.

Fortran, curiously, passes *everything* via pointer--if a Fortran function takes an int parameter, what gets pushed on the stack is a *pointer* to an int, not the int itself!

To summarize:
When passing...
In C++, you...
In Fortran, you...
an int
put the int into edi
put a pointer to the integer into rdi
a long
put the long into rdi
put a pointer to the long into rdi
an array
put a pointer to the first element of the array into rdi
put a pointer to the first element of the array into rdi (same as C++)
a char
put an int containing the character's value into edi
put a pointer to the character into rdi

Fortran 1D arrays are indexed using round brackets, like "myarr(i)".  And the index of the first array element in Fortran is "myarr(1)", not "myarr[0]" like C/C++.  But beyond those small differences, arrays work exactly the same in Fortran as in C++, and in fact it's not always possible from looking at the generated assembly code whether the original code was written in plain C, C++, Fortran, or Assembly!

Name Mangling, plain C, and extern "C"

C++ "mangles" the linker names of its functions to include the data types of the function arguments. This is good, because it lets you overload function names; but it's bad, because plain C and assembly don't do anything special to the linker names of functions.

In plain C or assembly, a function "foo" shows up as just plain "foo" in the linker. In C++, a function foo shows up as "foo()" or "foo(int,double,void *)". (Check out the disassembly to be sure how your linker names are coming out.)

So if you call C or assembly code from C++, you have to turn off C++'s name mangling by declaring the C or assembly routines 'extern "C"', like this:

extern "C" void some_assembly_routine(int param1,char *param2);
or wrapped in curly braces like this:
extern "C" {
void one_assembly_routine(int x);
void another_assembly_routine(char c);
}

In fact, it's common to provide a "magic" header file for C code that automatically provides 'extern "C"' prototypes for C++, but just works normally in plain C:

#ifdef __cplusplus /* only defined in C++ code */
extern "C" {
#endif
void one_assembly_routine(int x);
void another_assembly_routine(char c);
#ifdef __cplusplus /* only defined in C++ code */
}
#endif
Definitely try these things out yourself:

Plain C bar routine:
int bar(int i,int j) {
printf("bar(%d,%d)\n",i,j);
return i;
}
(executable NetRun link)

C++ foo routine that calls bar:
extern "C" int bar(int i,int j);
int foo(void)
{
return bar(2,3);
}
(executable NetRun link)

Try:
Code written in
With name
Has linker name
C++
int bar(int a,int b)
bar(int,int)    <- But "mangled" to be alphanumeric...
C++
extern "C" int bar(int a,int b) bar
C
int bar(int a,int b)
bar
Assembly
global bar
bar:
bar
Fortran
SUBROUTINE bar()
bar_, BAR, BAR_, bar__, or some such.  Disassemble to be sure...

Bottom line: to call code written in anything else (C, Assembly, Fortran) from C++, or to call C++ from anything else, add extern "C" to the C++ code!

Fun With Fortran!

CS 301 isn't a computer languages course, but I think it's pretty interesting to look at old-school Fortran, a language from 1956.  Note how this little function returns 10, like you'd expect.  And the assembly code is pretty much exactly what you'd get from C/C++!
       function foo()
INTEGER foo

i = 7;
foo = i + 3;

end function

(executable Net Run link)

Here's a "do loop" (the Fortran equivalent of C/C++ "for"):
       function foo()
INTEGER foo

do i=1,10
CALL print_int(i)
end do
foo = i + 3;

end function

(executable NetRun link)

Note that "print_int" is defined in NetRun's "inc.c" as:
    CDECL void print_int__(int *i) {print_int(*i);}
Here,

This sort of Fortran/C/C++ interfacing is really common in big projects, mostly because engineers like Fortran, hardcore assembly hackers prefer plain C, and GUI designers and general coders tend to use C++.