Function prototype and defining functions

Apr 8, 2010 at 11:45pm
I'm trying to write a function prototype, and define and call the function. The function is called distance and should return a float and accept two float parameters(rate and time).

Here's what I have, but I don't think its correct. Would appreciate any input!

function prototype:
float distance(float, float);

define function:
float distance(float rate, float time)
{
cout << "Distance is " << rate * time) << endl;
}

function call:
distance(rate, time);
Apr 8, 2010 at 11:52pm
Your function needs to return the values, not cout them.

Read: http://www.cplusplus.com/doc/tutorial/functions/
Apr 9, 2010 at 3:56am
So it should be this?

function prototype:
float distance(float, float); <---should I include rate and time after the floats in the ( )?

define function:
float distance(float rate, float time)
{
float distance;
distance=rate * time;
return(distance);
}

function call:
distance(rate, time); <---does this look like a proper call or should I leave out the (rate, time)?
Apr 9, 2010 at 4:03am
closed account (jwC5fSEw)
For a simple function like this, it's more efficient to remove the local variable entirely:

1
2
3
float distance (float rate, float time) {
    return (rate * time);
}


And it would be called like this:

1
2
3
float r = 10;
float l = 5;
float dist = distance(r, l)
Apr 9, 2010 at 5:18am
For a simple function like this, it's more efficient to remove the local variable entirely

I m not really sure about this... I believe it is exactly the same. I mean in both cases only one object is created. Check this out:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#include <iostream>
using namespace std;

class MyFloat
{
    public:
    MyFloat(){cout << "construction of MyFloat at " << this << endl;}
    MyFloat(float f):value(f){cout << "construction of MyFloat at " << this << endl;}
    ~MyFloat(){cout << "destruction of MyFloat at " << this << endl;}
    
    MyFloat & operator=(float f){value=f; return *this;}
    MyFloat & operator=(const MyFloat & mf){value=mf.value; return *this;}
    
    friend ostream & operator<<(ostream & os, const MyFloat & mf) {os<<mf.value; return os;}
    
    private:
    float value;
};

MyFloat distance1(float rate, float time)
{   
    //here we explicitely create the object 
    MyFloat dist;
    dist=rate*time;
    return dist;
}

MyFloat distance2(float rate, float time)
{
    //here we let the compiler create it for us
    //but still, it is created...
    return rate*time;
}

struct AfterMain
{
    ~AfterMain(){cout << "\nhit enter to continue..."; cin.get();}
};

AfterMain after_main;

int main()
{
    cout << "creating my_float..." << endl;
    MyFloat my_float;
    
    cout << "\nentering distance1..." << endl;
    my_float=distance1(1.0,2.0);
    cout << "after distance1..." << endl;
    
    cout << "distance is: " << my_float << endl;
    
    cout << "\nentering distance2..." << endl;
    my_float=distance2(3.0,4.0);
    cout << "after distance2..." << endl;
    
    cout << "distance is: " << my_float << endl;
    
    cout << "\ndestroying my_float..." << endl;
    
    return 0;
}

At least that's how user defined types are handled, but I believe standard types are handled the same way.
Last edited on Apr 9, 2010 at 5:28am
Apr 9, 2010 at 5:28am
closed account (jwC5fSEw)
At least with g++, it is definitely more efficient. I checked the assembly output for this program:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
float dist1 (float a, float b) {
    float ret;
    ret = a * b;
    return ret;
}

float dist2 (float a, float b) {
    return (a * b);
}

int main() {
    float x = 10;
    float y = 50.34;
    float z = dist1(x, y);
    z = dist2(x, y);
    return 0;
}


Here's the first function (with the added local variable):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
__Z5dist1ff:
LFB0:
	pushl	%ebp
LCFI0:
	movl	%esp, %ebp
LCFI1:
	subl	$20, %esp
LCFI2:
	flds	8(%ebp)
	fmuls	12(%ebp)
	fstps	-4(%ebp)
	movl	-4(%ebp), %eax
	movl	%eax, -20(%ebp)
	flds	-20(%ebp)
	leave
	ret


And here's the second:

1
2
3
4
5
6
7
8
9
10
__Z5dist2ff:
LFB1:
	pushl	%ebp
LCFI3:
	movl	%esp, %ebp
LCFI4:
	flds	8(%ebp)
	fmuls	12(%ebp)
	leave
	ret


As you can see, it's much shorter without the local variable.
Apr 9, 2010 at 5:30am
oh, that's... interesting... I guess you are right...
Apr 9, 2010 at 5:36am
is this subl $20, %esp allocating space on the stack for the local var?
Apr 9, 2010 at 5:46am
closed account (jwC5fSEw)
I really don't know, actually. I'm not intimately familiar with assembly; all I can do is scan some simple code (like the above) and get the basic meaning.

Also, when one snippet is significantly shorter than the other, that's pretty significant hint. ;D
Apr 9, 2010 at 5:46am
shepp2670, you were asking how to write a function prototype with a definition and call. This is how it is done:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

#include <iostream>
using namespace std;

// Function Prototype.  This declares Distance to be a function that needs
// two float variables as arguments.
float Distance(float, float);


// Main function
int main()
{

	float rate = 5;  // Declare a first float variable
	float time = 6;  // And a second float variable
	
        // This is the function call.  The arguements can be ANY name so long as they
        // the name of float variables.
	float dist = Distance(rate, time);
	

	cout << "Distance is " << dist << endl;
	
	return 0;
	
}

// Function Definition.  This function will return a float. 
float Distance(float rate, float time)
{

	float value = rate * time;
	
	return value;  // Return the value of the variable 'value'
	
}

Apr 9, 2010 at 5:46am
ah, I think I get it. these two commands are necessary for the argument passing:

1
2
pushl     %ebp
movl      %esp,     %ebp

and then you load a register with the first argument:
flds 8(%ebp)

and multiply that register with the second argument:
fmuls 12(%ebp)

In this case no temporary float object is created because the result is stored in a register! So I guess for standard types this is faster, because they can be stored directly in a register. And I suppose this means that for user defined types there is no difference. But would you like to check this too please? So we are sure about what happens.
Last edited on Apr 9, 2010 at 5:49am
Apr 9, 2010 at 6:12am
closed account (jwC5fSEw)
Yeah, turns out you're right about that. I guess it has to call the constructor no matter what. Here's my code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class MyFloat {
  public:
    float x;
    MyFloat() {}
    MyFloat(float a) : x(a) {}
};

MyFloat dist1 (MyFloat a, MyFloat b) {
    MyFloat ret;
    ret.x = a.x * b.x;
    return ret;
}

MyFloat dist2 (MyFloat a, MyFloat b) {
    return MyFloat(a.x * b.x);
}


First function's output:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
__Z5dist17MyFloatS_:
LFB6:
	pushl	%ebp
LCFI4:
	movl	%esp, %ebp
LCFI5:
	subl	$24, %esp
LCFI6:
	leal	-4(%ebp), %eax
	movl	%eax, (%esp)
	call	__ZN7MyFloatC1Ev
	flds	8(%ebp)
	flds	12(%ebp)
	fmulp	%st, %st(1)
	fstps	-4(%ebp)
	movl	-4(%ebp), %eax
	movl	%eax, -20(%ebp)
	flds	-20(%ebp)
	leave
	ret


Second function's output:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
__Z5dist27MyFloatS_:
LFB7:
	pushl	%ebp
LCFI7:
	movl	%esp, %ebp
LCFI8:
	subl	$28, %esp
LCFI9:
	flds	8(%ebp)
	flds	12(%ebp)
	fmulp	%st, %st(1)
	fstps	4(%esp)
	leal	-4(%ebp), %eax
	movl	%eax, (%esp)
	call	__ZN7MyFloatC1Ef
	movl	-4(%ebp), %eax
	movl	%eax, -20(%ebp)
	flds	-20(%ebp)
	leave
	ret


It's just in a different order.
Apr 9, 2010 at 6:15am
Neat!!! I guess now we can say that we are both right! You for the standard types and me for the user defined ones! hahaha :D
Apr 9, 2010 at 12:49pm
1
2
pushl  %ebp
movl   %esp, %ebp


is standard code that does two things:

1) Sets up the stack frame so that the debugger can give you a stack backtrace;
2) Sets ebp so that the function can access its parameters

I would recommend trying to compile the above examples with -O3 on gcc.

(Not that I'm recommending the three-line version over the one-liner; I'd take the
one-liner any day.)
Apr 9, 2010 at 1:36pm
Thanks!
Topic archived. No new replies allowed.