Seeking explanation re: trailing &'s and *'s

I am very new to C++ and attempting to learn via "C++ Primer" (5th edition).

I am encountering trailing &'s and *'s with variable definitions that the book I am using does not explain. Most notably char* and char& etc.

Would someone please explain these constructs, hopefully with specific definitions and perhaps some online references.

Thank you,
Ortndal
does char *someVar and char &someVar make sense to you?
My (limited) understanding is that the 1st is a pointer, and the second is a reference.
correct. char& someVar and char* someVar is the same thing, but it will only refer to the first. ie:
1
2
char* var1, var2; // var1 is a pointer and var2 is a normal char
char *var3, * var4; // all are pointers 
Would someone please explain these constructs, hopefully with specific definitions and perhaps some online references.


Since you said you're new to C++ and in the previous post you said your limited understanding, I'm assuming you do not full comprehend pointers and references.

A pointer contains the address of an object in memory.

A reference is the same thing, although the way you access the pointed to object in memory are different and their declarations are different.

1
2
3
4
5
6
char obj1 = 'a';
char obj2= 'b';
char* p = &obj1; // pointer
// Note the & before obj, that is the address of operator,
// should not be confused with the declaration of a reference
char& ref = obj; // reference 


To access and change the value of an object using a pointer you have to dereference it:

 
*p = 'w'; // obj1 and ref is now 'w' 


You can change the what the pointer is pointing to some other object:

1
2
// Note there is not * before p = ...
p = &obj2; // p now points to obj2, it contains obj2's address 


To access the value of a reference you just do:

 
ref = 't'; // obj1 now equals 't' 


But with references you cannot make it refer (or point) to another object.

The sizeof() a pointer and a reference is smaller than the sizeof() the object it holds, because all it contains is an address. That means you can call a function and pass it's arguments by value instead of by copy. Imagine you have a std::vector<int> with 100 000 elements, it would be very inefficient to pass it to a function by copy.

The advantage of using references is it makes it easier to read and write code. Compare the following to functions:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void f1(const std::vector<int>& arg_ref)
{
	for(int i = 0; i < arg_ref.size(); ++i)
	{
		std::cout << arg_ref[i] << '\n';
	}
}

void f2(const std::vector<int>* arg_ref)
{
	for(int i = 0; i < *arg_ref.size(); ++i)
	{
		std::cout << *arg_ref[i] << '\n';
	}
}


So how do you chose between pass by value, pass by reference, and pass by pointer? First of all there is no need to pass by pointer since it is the same thing as passing by reference.

The general rule of thumb is:
1). Use pass by value for small objects
2). Use pass by const reference when you don't need to modify what your passing, when the object is too big and it is inefficient to copy it.
3). Use pass by reference when you want to modify the object.

Why should avoid pass by reference and prefer pass by value:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
int incremet1(int arg)
{
	return ++arg;
}

void increment2(int& arg)
{
	++arg;
}

int main()
{
	int x = 0;
	
	increment1(x); // no harm done x is still 0

	increment2(x); // x now equals 1
	// anyone reading this will not see that x has been modified

	x = increment1(x); // x now equals 2
	// anyone reading this will see that we are modifying x
}


I usually pass by const value, it is better than const reference, because you can remove the const qualifier using const_cast<>. Removing the const from a pass by value won't do any harm to the object the caller passed, but removing the const from a pass by reference object can do a whole lot of harm to the unsuspecting caller.

When choosing how you want to pass an argument, have in mind that modern computers are really powerful. You shouldn't be worried about efficiency. People that worry about efficiency too much usually write messy and error-prone code, messy code is hard to understand and difficult to maintain.

So what your probably thinking now is, "So if pointers and references are kinda the same thing, why not use a reference, because it's easier to read and write code?" Pointers are used to implement data structures such as std::vector (which is a dynamic array), binary search trees, linked lists, and for allocating memory on the free store, but don't worry about that for now.

Word of advice: Never return a reference to a local variable in a function. Since the object was destroyed at the end of the function you are returning an address that does not contain a valid object and it is possible another program or the operating system might be using that address now.

The most confusing thing about pointers and references is there syntax, it's very similar, which makes it easy to get confused. Pointers and references were maybe the most difficult thing for in my early days. It is confusing for you now, but it will all become clear at some point.
Last edited on
benbalach wrote:
A pointer contains the address of an object in memory.

A reference is the same thing, although the way you access the pointed to object in memory are different and their declarations are different.


A reference is not the same thing as a pointer. A reference may be implemented under the hood as a pointer, but it is definitely not the same as a pointer in C++.

A reference is simply an alias (another name) for the variable it refers to. There are all kinds of things one can do with a pointer that one cannot do with a reference. For instance, pointer arithmetic is a useful thing we can do with pointers, but there is no equivalent reference arithmetic. Taking the sizeof a pointer will give you the size of a pointer, whereas taking the sizeof a reference will give you the size of the type the reference is aliasing. Taking the address of a pointer will give you the address of the pointer, whereas taking the address of a reference will give you the address of the object the reference is aliasing.


benbalach wrote:
The sizeof() a pointer and a reference is smaller than the sizeof() the object it holds, because all it contains is an address.

As noted above, this is not correct.


benbalach wrote:
So how do you chose between pass by value, pass by reference, and pass by pointer? First of all there is no need to pass by pointer since it is the same thing as passing by reference.

There is pass by value and pass by reference. What you're calling "pass by pointer" is passing a pointer by value. Of course, we can also pass a pointer by reference.


benbalach wrote:
I usually pass by const value, it is better than const reference, because you can remove the const qualifier using const_cast<>. Removing the const from a pass by value won't do any harm to the object the caller passed, but removing the const from a pass by reference object can do a whole lot of harm to the unsuspecting caller.

You pass by value. The phrase "pass by const value" doesn't have any additional meaning. When you pass by value, you pass a copy of the original object, thus the original object will not be modified by any change to the passed value, rendering the const superfluous (compilers ignore top level const for parameters in function declarations: int func(const int) declares the same function as int func(int))

Design your code so that you aren't using const_cast except in situations where it is absolutely unavoidable.



Last edited on
@cire

Thank you for pointing out my mistakes, I guess I shouldn't try and answer a question unless I fully understand the topic. Have in mind I did not say I was an expert, I am still learning each day.

@Ortndal

Sorry for giving you misleading information.
Last edited on
Thanks to all for contributing your knowledge and patience.

I do have a code example to which I am trying to apply the info above.
Perhaps I am overlooking something, or I don't yet grasp the subtleties.

Here goes:

.
.
.
friend bool operator==(const Sales_item&, const Sales_item&);
.
.
.

Where Sales_item is a class.

Any ideas? This is from the Lippman, Lajoie, Moo book.

Thanks,
Ortndal

Just like you could have a prototype:
bool func(int, int);
You could have:
bool func(int&, const int&);

Does that make sense?
I must say that the "int&, int&" appears to be different; they don't look like
either a reference or address-of usage.

I have also encountered "char* " without a variable name which conflicts with
the requirements stated above for a pointer variable.
Without a variable name, "char*" is the name of the type "pointer to char".

You can use it as an argument to sizeof: std::cout << sizeof(char*) << '\n';, or to new: char** p = new char*; or to declare a variable: char* x;, or in a few other ways.
Last edited on
also as zhuge pointer out you can do it with prototypes, such as:
someDataType Foo(someDataType*="SomeValue");
Dear Plussers -

Thanks for all the thoughtful replies and insight given freely.
I hope to someday provide some answers to your ?'s, if possible.

Best Regards,
Ortndal
Topic archived. No new replies allowed.