pass by ref vs pass by pointer

D

Dan Cernat

Frank said:
Andy, I know what the c++ standard says about the fact that that all
evaluation of function arguments takes place before execution of any
expressions of any expressions or statements in the the function body.
This is very similar to way the Scheme interpreter evaluator
functions(see Structure and Interpretaion of Computer Program by Harold
Abelson and Gerald Sussman). But as "almost anonymous" says, what the
C++ standard and the C++ compiler do are actually two different things.
One should code by the standard not by what compiler
says/generates/behaves. This way when one changes the compiler,
everything works as expected.
"The failure occurs inside the TestFunction and not at the call to
TestFunction. This is with the Metrowerks compiler but I'm sure it's
true for all compilers.
This is the result of UB. Why is it so hard for you to understand? As
soon as the invalid pointer is dereferenced you step into UB land.
Anything is possible, *including* crash inside the function.
The dereference does not occur at the call site because it's not
deferenced at the point. Instead, because it's a reference parameter,
the address is pushed onto the stack.
that is how the code is compiled by your compiler on your platform
If the parameter was not a reference parameter then you would be
right... the dereference would occur at the call."

So, in reality , the argument addresses are pushed on the stack without
being deferenced. I guess the best way to resolve our difference is to
look at assembly code that the C++ compiler generates.
Even if every single C++ compiler would generate the same machine code
(as your compiler), that doesn't make the code right, does it?

/dan
 
J

Jojo

Victor said:
Rolf said:
Victor Bazarov wrote:

Yes, a valid program shouldn't "crash". And I bet there are programs
that
just do not crash. They report critical situations, work their way
out of
them, and so on.

Never happens and is the reason why people invent

"safe" languages like Java et al.


No trolling here, please.



I don't see why this would be trolling. One reason why Java [...]


That's what I call trolling. Java is OFF-f#@*ng-TOPIC, yet you just
continued a discussion on it. He successfully trolled you into this.

LOL, when did the discussion on Java start? I could have put anything
there. I made no comparisons to Java. I said nothing positive or
negative about anything. I'm not a Java programmer nor advocate.
Sheesh, this is why I don't typically hang out on USENET, too many
insane individuals (not that the rest of the 'Net is much different ;) )

and on top of all that no one could solve my problem that I came here
for anyway. I think I need to find some real experts and not just
talkers. Buh-bye!

Jo
 
S

Stuart MacMartin

As you can see, this question always generates a flame war.

The answer is one of philosophy and mindset.
Passing reference uses the mindset: "I think in terms of objects.
This function requires an object. Caller is responsible for properly
providing an object. The compiler will make sure it's the right type
and valid unless you circumvent that. We only need to test a pointer
at the place where we dereference it, before calling the functions that
take a reference."

If you are of this mindset with passing references, then passing
pointers means "You can give me a pointer to nothing as well" although
some people confuse const with pointer and mean "I can edit your
value".

If you are of the mindset that references are evil, then pass by
pointer means (I'm not entirely facetious here): I don't understand
objects and I think in terms of pointers. I might or might not be
allowing you to pass 0, and I might or might not be expecting you to
check that the pointer is non-zero because I might or might not check
it before I dereference, depending on whim. If I am careful about
checking pointers against 0, I insist this be done in every routine no
matter how small, so typically I prefer encapsulation problems to tiny
routines. Since I know you can dereference a pointer to 0, I prefer to
work with pointers and love to pontificate about the horrors of
references. However, although I get on my high horse about references
not really being guaranteed to be objects when you lie to the compiler,
I will conveniently ignore the equivalent problem that passing a
non-zero pointer doesn't guarantee that it's a pointer to something
legitimate, and that dereferencing a pointer can actually cause a crash
if you've lied to the compiler and passed a pointer to a deleted object
or arranged for the pointer to be to some arbitrary number.

In other words, as far as the compiler is concerned it's about the same
thing. As far as you are concerned, passing by reference or not helps
clarify your code and make it clear who is supposed to check that
pointers are valid. Generally code that passes references are easier
to maintain, but that depends on the people involved.

Stuart
 
D

Default User

Frank said:
Andy, please read post 9 in this thread. Thank you.


Most of us aren't reading this from Google. There are no numbers on the
posts. Google sticks those on.




Brian
 
S

Stuart MacMartin

Frank said:
NULL references are unbelievably easy in C++...

someClass* instance = SomeOperation();
Test(*instance );

I don't understand this argument.
What's the difference between this and someone inadvertantly doing:

Test(someClass* p)
{
p->doSomething();
}
....
someClass* instance = SomeOperation();
Test(instance);

or even worse:

Test(SomeOperation()); // We assume Test will check that that pointer
is ok

Both cases dereference a pointer that's never been checked for
non-zero. Are you saying that people are careless when dereferencing a
pointer using * but not careless when using a pointer via ->? That's
not consistent with my observations of people's code. I've seen much
sloppier handling of pointers in code that takes pointers than in code
calling routines taking references.

Stuart
 
A

AndyRB

Frank said:
Andy, I know what the c++ standard says about the fact that that all
evaluation of function arguments takes place before execution of any
expressions of any expressions or statements in the the function body.
This is very similar to way the Scheme interpreter evaluator
functions(see Structure and Interpretaion of Computer Program by Harold
Abelson and Gerald Sussman). But as "almost anonymous" says, what the
C++ standard and the C++ compiler do are actually two different things.
And these are cases where the compiler is non-conforming.
"The failure occurs inside the TestFunction and not at the call to
TestFunction. This is with the Metrowerks compiler but I'm sure it's
true for all compilers.

The dereference does not occur at the call site because it's not
deferenced at the point. Instead, because it's a reference parameter,
the address is pushed onto the stack.

If the parameter was not a reference parameter then you would be
right... the dereference would occur at the call."

So, in reality , the argument addresses are pushed on the stack without
being deferenced.

Is this guaranteed?
I guess the best way to resolve our difference is to
look at assembly code that the C++ compiler generates.

You say "the" c++ compiler as though there is only one. I guess the
fact there are many c++ compilers is the reason we have the c++
standard and it is only this which actually defines the language.

The point I am trying to make is that the code that caontains the
undefined behaviour is the calling code, it is the calling code that is
erroneous not the function body. The function can assume the reference
refers to a valid object because anything else would mean that you have
already eneter the realm of undefined beahviour and at that point what
happens within the function body is pointless to discuss because it is
*undefined*. Where a program crashes (assuming it crashes) is not
necessarily the point where the program first became undefined and it
is this point which is important (from a coding perspective).
 
F

Frank Chang

Stuart, I agree with what you just said. This is one of those
"religious wars" that can go on and on. I've learned a lot by listening
to everybody's viewpoints and that is what really counts.
 
F

Frank Chang

Dan, Your points are well stated. However, you state

This is true, but I never claimed the code wouldn't crash inside the
function.

Your last point,

is completely true. I was just stating to AndyRB that it would be
interesting to see the assembly code from a from a well-known C++
compiler like MS Visual Studio, to see what goes on behind the scenes.
By the ways , "almost anonymous" looked at the assembly code generated
by the Metrowerks C++ compiler and made the observation about the
arguments being pushed on the stack.
 
R

Rolf Magnus

Victor said:
Rolf said:
Victor Bazarov wrote:

Yes, a valid program shouldn't "crash". And I bet there are programs
that
just do not crash. They report critical situations, work their way out
of them, and so on.

Never happens and is the reason why people invent

"safe" languages like Java et al.

No trolling here, please.


I don't see why this would be trolling. One reason why Java [...]

That's what I call trolling. Java is OFF-f#@*ng-TOPIC,

What's your problem, man?
yet you just continued a discussion on it. He successfully trolled you
into this.

Actually it was you. If you hadn't called him a troll, I wouldn't have
answered to it. My point was that I don't consider just the bare mentioning
of the word "Java" as trolling. He didn't write something like "Java is
better than C++" or "C++ is better than Java". He only used it as an
argument for his point that in the real world, errors happen, and so it can
happen that an invalid reference is passed to a function. And so, passing a
reference instead of a pointer is in no way safer. The function has to rely
on the caller providing valid data in both cases.
OK, here you go

#include <iostream>

int main() {
std::cout << 'H' << 'e' << 'l' << 'l' << 'o'
<< ' ' << 'w' << 'o' << 'r' << 'l' << 'd' << '\n';
}

Better now?

Yup ;-)
 
F

Frank Chang

Andy, You are right. There is not only one C++ compiler. By the way ,
it was "almost_anonymous" who made the observation about the Metrowerks
C++ compiler pushing arguments on the stack. The point of my post
directed to you was that one would need to look at the assembly code
generated by other well known compilers, before we could draw any
conclusions. Your final point is what really counts

Thank you for bringing your viewpoints to this discussion. I learned a
lot from reading your posts.
 
E

Earl Purple

Passing by pointer comes from the C attitude:

void useAnInt( int x )
{
do_something_with_x( x );
}

void setAnInt( int * x_address )
{
*x_address = someValue();
}

Now from the code that calls the function it looks clear
whether or not the function is likely to modify (set) your value.

void callingFunc()
{
int x;
setAnInt( &x ); /* will be setting this value to something */
useAnInt( x ); /* x will not be changed by this function */
}

Those who are taught "C first" will probably learn this even before
they use larger structs, which they might
want to pass as a pointer even if it is not going to be modified for
efficiency.

When they move over to C++ and learn about references, there is a
feeling of discomfort.
You can pass a function thus useAnInt( x ); and you won't know if
useAnInt() might modify the x without
looking at the function, whereas setAnInt( &x ) is likely to because
you passed it by address.

Therefore they stick with pointers and avoid references. (Apart from
the fact they are sticking with the familiar).

By the way, Java does not use references in the style of C++. What they
call references are really pointers, albeit
more like smart-pointers. But let's end that discussion here because we
are not here to discuss Java.
 
P

peter.koch.larsen

Stuart MacMartin skrev:
I don't understand this argument.
What's the difference between this and someone inadvertantly doing:

Test(someClass* p)
{
p->doSomething();
}
...
someClass* instance = SomeOperation();
Test(instance);

or even worse:

Test(SomeOperation()); // We assume Test will check that that pointer
is ok

Both cases dereference a pointer that's never been checked for
non-zero. Are you saying that people are careless when dereferencing a
pointer using * but not careless when using a pointer via ->? That's
not consistent with my observations of people's code. I've seen much
sloppier handling of pointers in code that takes pointers than in code
calling routines taking references.

One difference is that when programmers see a signature involving a
reference, they have the obligation to check their pointer before
passing it on. Secondly, they should assume that a function that
accepts a pointer will allow passing 0 (unless specifically stated in
the docs, but this requires more documentation). Thus using a reference
is a valuable point of documentation.
Another is that by not using (raw) pointers you reduce the option of
inadvertently passing one. And pointerusage is very heavily reduced in
C++ program compared to the equivalent C-code.
Unfortunately it appears that there are loads of "C++"-programmers
using naked pointers all the time, at least judging by this newsgroup.
I would not have let those in to my development team without a heavy
brainwash.

/Peter
 
R

Ram

Whats the difference b/w pass by ref and pass by pointer in C++ when
ur passing objects as args..

Apart from accessing the object's member's with dot (.) notation, it
delegates the responsibility of passing a valid object to the caller.
When one passes a pointer, that may not or may be a NULL (in which case
you aren't allowed to de-reference it). But with a reference and in a
valid sequence you are guaranteed that you have a valid object. That
can't be said for a pointer argument. The NULL pointer itself may be a
valid argument.

Ramashish
 
R

Ram

Andy, I know what the c++ standard says about the fact that that all
evaluation of function arguments takes place before execution of any
expressions of any expressions or statements in the the function body.
This is very similar to way the Scheme interpreter evaluator
functions(see Structure and Interpretaion of Computer Program by Harold
Abelson and Gerald Sussman). But as "almost anonymous" says, what the
C++ standard and the C++ compiler do are actually two different things.
Frank, if you program according to the standard i.e. by assuming that
"all evaluation of function arguments takes place before execution of
any expressions of any expressions or statements in the the function
body" this question wouldn't arise.
"The failure occurs inside the TestFunction and not at the call to
TestFunction. This is with the Metrowerks compiler but I'm sure it's
true for all compilers.

The dereference does not occur at the call site because it's not
deferenced at the point. Instead, because it's a reference parameter,
the address is pushed onto the stack.

If the parameter was not a reference parameter then you would be
right... the dereference would occur at the call."

That is just the way your compiler implements a reference. May not be
true for all compilers and even if it is I should not care.
So, in reality , the argument addresses are pushed on the stack without
being deferenced. I guess the best way to resolve our difference is to
look at assembly code that the C++ compiler generates.

Again, whether or not the compiler de-reference it before passing on is
upto it. Looking at the assembly code is no good way to resolve this
issue. If one is keen to abuse the language, then little can be done
about it.

If I were to design a compiler that throws an exception on having an
invalid reference object then I need to de-reference the pointer (from
which the object is being constructed) whether I use the reference
variable or not in the function. Why, the standard C++ already does it
for dynamic_cast..

#include <iostream>

using namespace std;

class A
{
public:
virtual ~A() {}
};


class B: public A
{
public:
};

int main()
{
A *a = new A;

try {
B &b = dynamic_cast<B&>(*a);
}
catch(...)
{
cerr << "Invalid cast\n";
}

delete a;

return 0;
}

Ramashish
 
R

Ram

Earl said:
Passing by pointer comes from the C attitude:

void useAnInt( int x )
{
do_something_with_x( x );
}

void setAnInt( int * x_address )
{
*x_address = someValue();
}

Now from the code that calls the function it looks clear
whether or not the function is likely to modify (set) your value.

void callingFunc()
{
int x;
setAnInt( &x ); /* will be setting this value to something */
useAnInt( x ); /* x will not be changed by this function */
}

Those who are taught "C first" will probably learn this even before
they use larger structs, which they might
want to pass as a pointer even if it is not going to be modified for
efficiency.

When they move over to C++ and learn about references, there is a
feeling of discomfort.
You can pass a function thus useAnInt( x ); and you won't know if
useAnInt() might modify the x without
looking at the function, whereas setAnInt( &x ) is likely to because
you passed it by address.

I don't agree here. If I am using a function, I definitely know in what
way it uses its arguments. If it doesn't modify its arguments the
reference would be const and I don't need to bother. And if its
expected to modify and I don't know about it then I am already doomed.
 
R

Ram

A reference always refers to a valid object, according to language rules.
The difference is that, when inside a function, if the argument is a reference,
you can rely upon it being a real object.
cat main.cc
#include <iostream>

void f(const int& r) {
std::cout << "r = " << r << std::endl;
}

int main(int argc, char* argv[]) {

int* p = 0;
int& r = *p;

The behavior of the program becomes undefined at this step itself. The
problem has nothing to do with reference but to your de-referencing a
NULL pointer.

f has every right to assume that it is passed a valid and real object.
If you mess up before calling f, then there's nothing that can be done
about it in f.

Ramashish
 
R

Rolf Magnus

Ram said:
Apart from accessing the object's member's with dot (.) notation, it
delegates the responsibility of passing a valid object to the caller.

I disagree. If I call a function that takes a pointer, I never pass a null
pointer unless the documentation of the function explicitly says that it
accepts a null pointer _and_ that it handles it the way I want.
When one passes a pointer, that may not or may be a NULL (in which case
you aren't allowed to de-reference it). But with a reference and in a
valid sequence you are guaranteed that you have a valid object. That
can't be said for a pointer argument. The NULL pointer itself may be a
valid argument.

It may be, or it may be not.
 
K

Kev

I disagree. If I call a function that takes a pointer, I never pass a
null pointer unless the documentation of the function explicitly says
that it accepts a null pointer _and_ that it handles it the way I
want.

I am still getting the hang of references but they do seem to be less error
prone at least for me... more so in catching my errors while coding than
anything else. That in itself helps quite a lot. I find pointers to be a
bit of voodoo but then Im newish. And if anyone put assembly in front of me
I would think I was looking at the matrix, just without the pretty green
waterfall effect.

Its a bit difficult to keep track when using both in different places. I am
trying to stick with references wherever possible. But a couple of times I
didnt see a way to get them to work, whereas things such as pointers to
pointers worked just fine.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,483
Members
44,901
Latest member
Noble71S45

Latest Threads

Top