Pointers vs References

Pages: 12
Would it be better to use pointers or references to pass varibles in and out of a function?

Thanks!
Last edited on
As far as simplicity, references would be better than pointers. No using stuff like having to assign pointers to addresses.

However, I guess that a reference is rather like a constant pointer, meaning that
int & x = bob;
is basically
const int * x = &bob;
so it doesn't really matter, as far as I know.
I have always preferred pointers due to their explicitness.
If you look at a pointer you can know it's one because it uses a different syntax than variables and objects. References, however, don't.

What the hell? This is valid:
1
2
3
4
5
6
7
8
9
10
11
#include <iostream>

int main(){
	int *a=new int;
	*a=4;
	const int *b=a;
	b++; //???
	std::cout <<a<<", "<<*a<<std::endl;
	std::cout <<b<<", "<<*b<<std::endl;
	return 0;
}
If I remember the syntax for const pointers properly, I think it is valid because b is a pointer to a const int; b itself is not a constant pointer. Therefore, I see no reason why you could not increment the pointer's value.
I normally use references to pass variables with functions but I'd say it depended on the rest of your code and what you were doing. Might as well learn to do both since they have there uses.

Example:
1
2
ostream& operator<<(ostream& o) // reference to an ostream object
void fn(char* name) //Pointer to characters 
No no no no no.

ALWAYS use references when possible. NEVER use pointers unless
absolutely forced. Simple.
For three reasons:

1) QWERTYman is partially correct. References are pointers, but
non-const references are non-const pointers and const references
are const pointers. It is REALLY HARD to make a NULL reference
but VERY EASY to make a NULL pointer. By declaring parameters to
functions as pointers, you FORCE yourself to write defensive code
inside the function to check the pointer for NULL before accessing.
And then you FORCE yourself to figure out how to handle the error
case.

2) When doing generic programming it is easy to write a template
function that works on variables passed by value or by reference
since the syntax for accessing either is the same.

1
2
3
4
5
6
7
8
      vector< int >     vec;
      vector< int >&   rvec( vec );
      vector< int >*   pvec( &vec );

      vec.push_back( 4 );       // Note use of "." to access member
      rvec.push_back( 4 );     //  Note again use of "." to access member
      pvec->push_back( 4 );  // Note use of "->" to access member
      


3) Pointers and operators don't mix well. For example, given a pointer
to a std::map< int, int >, how to you call operator[]?

1
2
3
4
5
6
7
8
      std::map< int, int >*  aMap = new std::map<int, int>();
      
      // This way is ugly:
      (*aMap)[ 4 ] = 5;

      // This is even uglier:
      aMap->operator[]( 4 ) = 5;
      


It may be ugly, but it's explicit. You can tell by simply looking at it that aMap is a pointer.

While it is true that null pointers may be passed as function parameters, this has positive aspects. For example, suppose you have an openFile() function, and you want that when a file name is not passed to it, to open a certain file by default. Since you can't pass a null reference, you are FORCED to pass it an empty string of some kind, which is of course slower, particularly if using std::strings.
And of course, you can also return null pointers, making it easy to distinguish special situations (see strpbrk() for an example)

In the above code, this also worked: int const *b=a;
EDIT: Hmm... The compiler complains "increment of read-only location" if I use (*b)++ is b is both 'const int *' and 'int const *'. This is why I never use consts.
Last edited on
I agree that there are times when pointers are preferred, and the two cases you mentioned are perfect examples.

The problem I find with a lot of production code that uses pointers is that while the NULL pointer error conditions might be handled by checking for NULL, they are almost never handled correctly in a large complex system.

It's also said that 75% of all code written is handling error cases, so I try to minimize the number of possible errors. While it is possible to write a function that returns an error code in the event of an erroneous NULL pointer, I find that a lot of programmers either ignore the return code altogether or they don't give enough thought to how to recover from the error condition with the justification that it "should never happen".


@QWERTYMan & jsmith

First, references are NOT pointers. A reference is just "like" a const pointer meaning once a reference is initialized it can not be reseated.
A reference is an alias for the referrant object, meaning, underneath it represents the address of referred object, but has no "separate" identify nor is a "sepearate entity". If you check the address of reference and referrant object, both return same, unlike a pointer.
A pointer is a separate entity which has its own address and stores/represents another memory address.

And, I suppose you mean,

int & x = bob;
is basically
int * const x = &bob; // not same as, const int * x = &bob;
(Read from right-to-left)

int * const x; // here x is a constant pointer pointing to an integer object, meaning the pointer can not change its address once set
int const * x; // here x is a pointer pointing to a constant integer, meaning the pointed-to object can not change it's value once set


And, coming to the difference,
int a = 10; // assume it is stored address 0x123
int &b = a; // b is just an alias for a, and not a separate entity, meaning, it represents same 0x123

int *p; // p is a poiter and has its own identity and is a seperate entity at 0x126
p = &a; // points-to another memory location, which is of a

cout << "&a " << &a << endl; // prints 0x123
cout << "&b " << &b << endl; // prints same 0x123

cout << "&p " << &p << endl; // prints 0x126, definitely not 0x123

Check the given above example and you find the difference yourself.

Good luck :)
And passing a reference to a function, unless it is clearly defined and documented in the code, I would not, in a designer perspective, recommend to do so.

When you write a library function and would not provide the library (with the .h code) to the client or other team, the function call in the caller program would not show that the function is called "by reference" hiding the fact that the referrant arguments passed-in could be "modified".

But if it is a pointer, it clearly shows/requires to pass-in an address for each pointed-to arguments/parament so that the caller function tells you that it is called by reference and could be modified within the called function.
In the first reference case, it would not be a user or developer friendly as it causes confusion in code development/debugging process.

Check it out. Good luck :)


EDIT: Yes, I forgot that we provide .h file with prototypes etc so they show which one is what, by reference, constant, or value. So this is out of context :)
Last edited on
Dear sir:
I reject your argument about the nature of references as compared to pointers on the grounds that you are a noob.

-Helios
@helios

Are you pointing at my post?
If so please check the given example and let me know where if I am missing.
You would know what I am talking about if you are an experienced C++ dev, then you would know who is noob :)

Good luck :)
Last edited on
Uh, I agree with helios. satm2008, you are a noob. Or else all those years programming in C++ were wasted because you obviously haven't learned much.

When you provide a library to someone you give them the header files. The header files contain the declarations of the functions. The declarations of the functions show the type of each parameter. Either the parameter is a reference or it isn't. When you accept a parameter by non-const reference you are telling the caller that they should not rely on the value of the actual parameter being the same post-call. When you accept a parameter by const reference you are telling the caller that they can rely on the value of the actual parameter being preserved across calls. Same goes for pointer and pointer-to-const.
Which one are you pointing to, the diff between pointer and reference or other my second note, reference in functions.

On the second note, yes, I agree with you as the prototypes show what is what. I missed that.

Just out of curiousity, when would you say some one noob???
Last edited on
I was referring to the first post.
References are pointers, it's just that, once they are initialized, the language hides that fact from you by immediately returning the value they point to (without copy, of course) without any extra syntax. References are just pointers minus an asterisk, and if this wasn't the case it would be impossible to pass parameters by reference using references.
@helios,
Nope, I dont agree with you that they are same. They act as same, with a difference in reference and dereference, they are not same.
As given in the example, a pointer is a separate variable holding an address of other while allowing to be changed, where as, a reference is not a separate entity (ie. just like an alias) and can not be reseated.
A reference acts like a constant pointer, but it is not a pointer.

A variable/identifier is a name for a memory location cotaining a regular value, the same is, reference.
A pointer is a variable, holds the address of a memory location and allows to be manipulated (including pointer arithmatics).

For your confirmation, check the assembly or compiler-generated code for a reference given in a program.

Agree or not, that is the fact.

Good luck :)
Last edited on
Okay, I just did. Here's my C++ code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void fp(int *a){
	(*a)++;
}

void fr(int &a){
	a++;
}

int main(){
	int a=0;
	int &b=a;
	int *c=&a;
	fp(c);
	fr(b);
	b++;
	return 0;
}

And here's the disassembly (a quick summary at the bottom):
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
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
void fp(int *a){
004113A0  push        ebp  
004113A1  mov         ebp,esp 
004113A3  sub         esp,0C0h 
004113A9  push        ebx  
004113AA  push        esi  
004113AB  push        edi  
004113AC  lea         edi,[ebp-0C0h] 
004113B2  mov         ecx,30h 
004113B7  mov         eax,0CCCCCCCCh 
004113BC  rep stos    dword ptr es:[edi] 
	(*a)++;
004113BE  mov         eax,dword ptr [a] 
004113C1  mov         ecx,dword ptr [eax] 
004113C3  add         ecx,1 
004113C6  mov         edx,dword ptr [a] 
004113C9  mov         dword ptr [edx],ecx 
}
004113CB  pop         edi  
004113CC  pop         esi  
004113CD  pop         ebx  
004113CE  mov         esp,ebp 
004113D0  pop         ebp  
004113D1  ret

//...

void fr(int &a){
004113E0  push        ebp  
004113E1  mov         ebp,esp 
004113E3  sub         esp,0C0h 
004113E9  push        ebx  
004113EA  push        esi  
004113EB  push        edi  
004113EC  lea         edi,[ebp-0C0h] 
004113F2  mov         ecx,30h 
004113F7  mov         eax,0CCCCCCCCh 
004113FC  rep stos    dword ptr es:[edi] 
	a++;
004113FE  mov         eax,dword ptr [a] 
00411401  mov         ecx,dword ptr [eax] 
00411403  add         ecx,1 
00411406  mov         edx,dword ptr [a] 
00411409  mov         dword ptr [edx],ecx 
}
0041140B  pop         edi  
0041140C  pop         esi  
0041140D  pop         ebx  
0041140E  mov         esp,ebp 
00411410  pop         ebp  
00411411  ret

//...

int main(){
00413040  push        ebp  
00413041  mov         ebp,esp 
00413043  sub         esp,0E4h 
00413049  push        ebx  
0041304A  push        esi  
0041304B  push        edi  
0041304C  lea         edi,[ebp-0E4h] 
00413052  mov         ecx,39h 
00413057  mov         eax,0CCCCCCCCh 
0041305C  rep stos    dword ptr es:[edi] 
	int a=0;
0041305E  mov         dword ptr [a],0 
	int &b=a;
00413065  lea         eax,[a] 
00413068  mov         dword ptr [b],eax 
	int *c=&a;
0041306B  lea         eax,[a] 
0041306E  mov         dword ptr [c],eax 
	fp(c);
00413071  mov         eax,dword ptr [c] 
00413074  push        eax  
00413075  call        fp (4111D1h) 
0041307A  add         esp,4 
	fr(b);
0041307D  mov         eax,dword ptr [b] 
00413080  push        eax  
00413081  call        fr (4111D6h) 
00413086  add         esp,4 
	b++;
00413089  mov         eax,dword ptr [b] 
0041308C  mov         ecx,dword ptr [eax] 
0041308E  add         ecx,1 
00413091  mov         edx,dword ptr [b] 
00413094  mov         dword ptr [edx],ecx 
	return 0;
00413096  xor         eax,eax 
}
00413098  push        edx  
00413099  mov         ecx,ebp 
0041309B  push        eax  
0041309C  lea         edx,[ (4130C0h)] 
004130A2  call        @ILT+130(@_RTC_CheckStackVars@8) (411087h) 
004130A7  pop         eax  
004130A8  pop         edx  
004130A9  pop         edi  
004130AA  pop         esi  
004130AB  pop         ebx  
004130AC  add         esp,0E4h 
004130B2  cmp         ebp,esp 
004130B4  call        @ILT+310(__RTC_CheckEsp) (41113Bh) 
004130B9  mov         esp,ebp 
004130BB  pop         ebp  
004130BC  ret

Now, if you take a close look at lines 12-17 and 39-44 in the disassembly, where the dereference is performed, you'll notice that those blocks are identical.
You can also see in lines 68-70 and 71-73 that the initializations of b and c are identical, as well.

Now, then. Are references aliases, or obscured pointers?
Last edited on
I m not familiar with an assembly language, so, I can not justify it by seeing the attached code.
What compiler are you using?
Did you check the example I gave?
VC++

I m not familiar with an assembly language, so, I can not justify it by seeing the attached code.
*Twitch*
This discussion is over, then.
Last edited on
Ok, satm2008...


First, references are NOT pointers. A reference is just "like" a const pointer meaning once a reference is initialized it can not be reseated.


Wrong for two reasons, at least as I interpret your statement. The following works just fine:

int x;
int y;
int& ref = x;
ref = y; // "Reseating" a reference.

I do not believe that the C++ standard mandates how compilers must handle or implement references, however as helios says and I can also confirm, gcc implements references by using pointers. That is, internally:

1
2
3
4
int x;
int y;
int& ref = x;
int* ptr = &x;


If you were to dump the contents of the memory locations occupied by ref and ptr you would find they are identical.

A non-const reference works the same as a pointer-to-non-const. A const reference works the same as a pointer-to-const.


A reference is an alias for the referrant object, meaning, underneath it represents the address of referred object, but has no "separate" identify nor is a "sepearate entity". If you check the address of reference and referrant object, both return same, unlike a pointer.


Ok, I partially agree with this. Given

1
2
3
4
5
int x;
int& ref = x;
int* ptr = &x;

cout << (&ref) << ptr << endl;


Outputs the same value. This is because the syntax of accessing variables via references is the same as accessing variables directly. That mans that in the above code &ref is actually the same as &x. That is, it is not possible to take the address of a reference variable. However, that does not mean that references are free. They essentially consume the same amount of memory as a pointer.


A pointer is a separate entity which has its own address and stores/represents another memory address.


Hence I don't agree with your statement that pointers are separate entities and references are not. Internally they are clearly both separate entities.


And, coming to the difference,
int a = 10; // assume it is stored address 0x123
int &b = a; // b is just an alias for a, and not a separate entity, meaning, it represents same 0x123

int *p; // p is a poiter and has its own identity and is a seperate entity at 0x126
p = &a; // points-to another memory location, which is of a

cout << "&a " << &a << endl; // prints 0x123
cout << "&b " << &b << endl; // prints same 0x123

cout << "&p " << &p << endl; // prints 0x126, definitely not 0x123

Check the given above example and you find the difference yourself.


Yes, you are seeing this because the language does not allow you to take the address of a reference variable. &b is NOT the address of b. b indeed does have an address.

1
2
3
int x;             // Assume address of x is 0x1000 and ints and pointers are 32-bit
int* ptr = &x;  // ptr will be located at address 0x1004
int& ref = x;   // ref will occupy 4 bytes of memory beginning at address 0x1008 

Pages: 12