structs and pointers?

Could someone help me understand what is going on in the following code?


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
struct Abc     // this I understand
{
    Abc() 
	{

    }
    int num;
};


void update(Abc& abc, int x) // I do not understand: Abc& abc   dereferencing
                             // the abc pointer?
{
	abc.num = 3;         // why abc.num here?

}


int main()
{
	int var = 5;
	Abc* abc = new Abc();// creating a new instance of struct Abc being  
	                     // pointed to by abc? why Abc* abc?

	abc->num = 2;        // why abc->num here?
      
	update(*abc, var);   // passing pointer to struct Abc?

	return 0;
}
There are a few ways you can pass data to a function. One of them is by reference, which is accessing the actual memory location of the data. This is different then passing by value, which creates a copy of the object on the call stack, but deletes this object when the function returns.

In your function update, which takes an Abc reference, the symbol "&" denotes to the compiler that the data should be passed by reference, and any time you access public methods or members of classes passed by reference, you use the "." operator.

The other method is by pointer. A pointer is a 4 byte memory location that holds the memory location of something else. The syntax to define a pointer is:

type* [identifier]

This is not the object, but rather an address of the object. When you use the "new" keyword, it allocates and creates an object of the type you specify on the free store, which is memory located outside of your running program. The syntax to access this data is the "->". The "*" is used to either designate a declaration of a pointer, or to "dereference" the pointer, which tells the compiler to return the memory location that the pointer indicates.

in your call to update, you use the dereference operator(*) to call the memory location of Abc, so that the compiler can pass it by value. Just before that you used the "->" operator to access the member num so that you can set its data to the value of 2.
Thank you for your quick reply. I have spent time going over your response and pass by value, pass by reference and pass by address.

I still don't understand why you would use:

Abc* abc = new Abc();
update(*abc, 5);

isn't it similar to:

Abc def;
Abc *ghi = &def;
update(*ghi, 6);

Also, when to use "abc.num" vs "abc->num" is not clear.
Abc* abc = new Abc();
This creates an object on the heap, and requires manual deletion.

1
2
Abc def;
Abc *ghi = &def;

This creates an object on the stack, and then creates a pointer to that object. Objects on the stack do not require manual deletion, and in fact you can't.
To answer the simplest question first:

If abc is a struct, then use "abc.num". If abc is a pointer to a struct, use "abc->num". "abc->num" is an easier way of writing (*abc).num.

As for your main question, there are two independent issues here.

1) When to dynamically allocate memory on the heap, and when to declare variables on the stack. There are a number of reasons why you might choose to dynamically allocate variables. Among them are:

- You don't know at compile-time how much space you need to allocate. This might be the case for an array, for example; you might need to calculate the number of elements from some other information before allocating the space for them.

- You want complete control of the lifetime of the data stored in the memory. For example, you may want to create the structure inside a function, and then have the code that calls your function use it too. Sure, you can copy the entire structure back and forth, but that's inefficient and can be problematic; it's easier to simply dynamically allocate the memory and hand the pointer to it around instead. Then, your code can free up the memory when it decides it no longer wants it. If you just declare a local variable, then the moment you leave the scope in which it was declared, it's gone.

2) When writing a function, whether to pass in an argument as a pointer or a reference. Passing by reference can have advantages, such as:

- inside your function, you don't have to worry about whether the pointer is pointing to memory that has been properly allocated. (References aren't 100% safe, but they're safer than pointers.)

- inside your function, you can use the semantics of the actual data type, rather than of a pointer. This can make the code easier to read.

M321 wrote:
1
2
Abc* abc = new Abc();
update(*abc, 5);


isn't it similar to:

1
2
3
Abc def;
Abc *ghi = &def;
update(*ghi, 6);



The first bit leaks memory, and if a delete abc; is added to fix that, it remains exception-unsafe. It looks a lot like a typical mistake of a beginner who is familiar with Java.

The second bit is safe, although redundant: it can be simply written as
1
2
Abc def;
update(def, 6);
Last edited on
Thank you all for your replies. I now have a much clearer understanding of the code.
Topic archived. No new replies allowed.