Function prototype and defining functions

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);
Your function needs to return the values, not cout them.

Read: http://www.cplusplus.com/doc/tutorial/functions/
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)?
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)
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
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.
oh, that's... interesting... I guess you are right...
is this subl $20, %esp allocating space on the stack for the local var?
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
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'
	
}

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
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.
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
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.)
Thanks!
Topic archived. No new replies allowed.