Hello, and welcome back.
In this lecture, I want to talk about using pointers and structures,
how they're related.
We can have elements inside of a structure be
a pointer, and then we can also make our structures themselves be pointers.
Two very useful concepts.
So structure is important here c allows for pointers two structures.
So when we create a variable of type structure,
we can create it as a pointer.
Pointers to structures are easier to manipulate than structures themselves.
So again, it's sort of a notational thing,
makes things a lot easier and it's also more efficient.
In some older implementations
of the c language,
a structure cannot be passed as an argument to a function,
but we know that a pointer can. So this is another reason why
you want to have a structure be a pointer when you declare
it because then you can pass it around in older versions of c
you can pass it as a argument to a function.
Even if you can pass a structure as an argument,
passing a pointer is more efficient.
We know that that's more efficient. We've talked about this in the past
how passing important can be more efficient.
In this case, it's even more efficient because
if you're passing in a entire structure,
if the structure is large and it contains a lot of elements, you're copying
all the elements in the structure to that parameter.
If you're passing a pointer just copying the address.
So it's even more important with
pointers as arguments that are structured because structures can be large.
So many data representations use structures, containing pointers
to other structures.
You're going to see this a lot in all of your c programs
structures that are pointers and for the reasons we mentioned.
The fact that you can pass them as parameters
on older versions to see and it's also more efficient because you
we do everything by pass by value which is copying
much more efficient to copy an address
than copying all the elements of this large structure.
So declaring a structure as a pointer pretty straightforward.
When you define a variable to be a pointer to struck, you just use that asterisk
after the type of the data.
And we said that structures are really a new type.
In this case, our ever important date structure.
We just say struct date and then use the asterisk date pointer,
and you'll probably want to initialize that to null.
Because remember, when you say struct date pointer date struct date date pointer,
that does not allocate any memory for,
it creates memory to store the actual pointer.
It's not creating memory for the data inside of it.
So you could either
assign to this date point or the address of another struct date
or you could allocate a memory for it
somewhere else or you could initialize it
with some data that way as well.
So the variable date pointer can be assigned just like other pointers,
you can set it to point to today's date
with the assignment statement so if we already had a declaration of today's date.
We would just use the ampersand to get the address of it.
So we create a normal date struct and it's today's date.
So that would create memory for the three elements.
And then we create our pointer and we just assign it
the address of the other date.
Pretty straightforward. We've done that with regular data types,
it's no different when you do it with structures.
You can then indirectly access any of the members of the data structure pointed to by date pointer.
So we would need to do something like this.
If you wanted to access the element
of the day pointer structure you'd have to de-reference it first.
And so it'd probably be useful to put it in parentheses so it's very straightforward.
You have to de-reference the date pointer to get the value
of what today's date is pointing to,
the value of the address of today's date which it's pointing to
in order to set particular values.
So you would de-reference it
and then do dot day equals 21 and that would assign 21
to your day pointer.
Now the above is just saying whatever today's date is pointing to,
we want to access that value and set the day to 21.
In this case, the parentheses are required because the
dot has a higher precedence than the asterisk.
If you didn't put the parentheses in there, it gets screwed up because it would try to do the dot first
before it actually got the pointer to the pointer,
to point it to the data.
So it would try to access data with with a null pointer essentially.
And so by de-referencing you're getting the value.
Now when we use structs a pointers.
If we wanted to test the value of months stored in the date structure pointed to by date pointer,
we could do something like this.
We de-reference the pointer, put in parenthesis, so it takes precedence over the dot
and then we say dot month equals 12.
It's perfectly valid syntax.
But it is kind of a little cumbersome.
So c has its own syntax, so you don't have to de-reference everything
and put it inside a parenthesis for structures.
When you want to access what the structure is pointing to,
you don't have to de-reference it this way.
There's a special operator that exists in c for structures of pointers,
and it's the dash greater than.
It's a dash followed by the greater than sign.
And this allows you to not have to de-reference the pointer.
So instead of doing star x inside parentheses and then dot y,
you can just say x dash greater than y.
Because x is a pointer, you don't have to reference it and you want to access
the element inside that structure whatever you're pointing to.
So you're going to see this syntax all over the place. You're not going to see
the parentheses and the d reference. It's perfectly valid, but you're typing in more characters.
It's much more efficient to just say x dash greater than y.
So the previous if statement up here instead of de-referencing inside the parentheses,
all you have to do is say if date pointer dash greater than month.
And it's somewhat intuitive because the dash greater than is sort of saying it's pointing to something.
And so date dash greater than month.
And so date pointer is a pointer and that's why you use the dash greater than.
Now if that was a nested structure,
you would do the dash greater than to the structure and then you'd have to do a
dot after that to access the elements of the nested structure.
But because day pointer is a pointer you have to use the dash equals
or dash greater than.
So here's an example, we're creating our struct date
we have three elements then we're creating our our pointer date pointer and we're creating
just a regular variable today. We assign
to our pointer the address of today and then to assign
actual elements of the struct, we just use that dash greater than and then we can print it out.
So let's look at this in code blocks to see how this works.
So I'm going to put this example in here.
And typically when you define structures,
you define them outside of a function because then you can use them in any function.
It's kind of like a global. But remember that
when you define the structure you're not actually creating any memory.
So you still create the variables as local variables or pointers,
but the actual definition is outside the function.
So if you want to use it you can use it in any function.
So here at the top, we've created a new struct of type date
or name to date and it contains three integers, we end it with a semicolon.
We then create a couple variables inside the main.
One which is just a normal variable struct date
today and then one that's a pointer. We know that it's a pointer because it has the asterisk before it.
We know that a pointer when you create a pointer it only creates allocates memory
to store that address for that for that value of that pointer.
So we'd have to actually assign it an address which we do with ampersand today
because that's actually create a memory report.
So now date pointer is going to point to whatever
today is with the address of today.
We can then start assigning data to it by using this dash greater than very useful
and just assign a data like anybody else just use the assignment operator.
And then when we print it out,
you'll notice we don't have to de-reference it to print it out.
To print it out,
we can just actually say the data types for the format specifiers here percent higher percent d
same for integers and then we just use dash grader because
that is actually doing the de-referencing remember.
Now we could change this and this would be valid.
We have to put in parentheses and then we have to say the dot month.
Either way is fine, but get used to the dash then greater because it is better syntax.
And now when we build this,
you'll see it prints out the date pointers values
and they're all set to 9 25 and 2015.
So it says today's date is 9 25 15. It was formatted to print out that way.
Pretty straightforward stuff.
Just remember that dash greater than, that's a new
syntax for pointers to structs.
But everything else here is just normal.
We know that pointers have to have at store addresses so we know we have to
either allocate dynamic memory or we have to assign it an existing struct.
Let's go back and talk more about structs and pointers.
Now let's talk about structures that contain pointers.
We know how to create a pointer for a structure.
We could allocate memory for that pointer if we didn't want to
assign an existing address of an existing pointer.
But you can also have elements inside a
structure that's a pointer itself. So a pointer also could be a member of a structure.
We don't have to use that character array.
So we could say struct,
we could name it in pointers and then we could use here in this example we have two integers,
two integer pointers p1 and p2, perfectly valid syntax to do that.
So a structure called in pointers is defined to contain two integer pointers.
First one is called p1 and the second one is called p2.
You can define a variable of type struct in pointers just like normal,
struct, int pointers, pointers.
The variable pointers can now be used just like other structs.
Pointers is not a pointer.
It's int pointers.It contains two elements inside its struck that are pointers,
but pointers itself is not an actual pointer.
So pay attention to that. It's a structure variable that has two pointers as its members,
that's different.
So if we want to access the elements inside
the pointers variable which is in pointers,
we don't use the dash greater than because pointers is not a pointer, it's a struct.
We would use the dot
to access p1 and access p2.
So in this example, we create our structure,
we create a variable. It's not a pointer called pointers.
And then we create two integers, i1
equal to 100 and i2 which is not initialized.
Now p1 and p2 are pointers.
So they have to contain valid memory addresses where the addresses have been
memory allocated.
And that's what into i1 and i2 is doing.
When you declare the integer variables, memory is being allocated for those integers.
And so by assigning the address i1 and i2 to p1 and p2,that's perfectly fine
because i1 and i2 have memory allocated for because they're integers.
Now if the integer pointers were not integer pointers but they were character pointers,
then you'd have to allocate dynamic memory.
So now we just are using integers, so we assign the addresses of the two integers to p1 and p2.
We can now say star pointers.p2 equals 97,
negative 97.
Why do we have to de-reference it there
because pointers.p2 is what we're referencing.
Pointers.pt p2 is a pointer.
We're not accessing an element of a structure.
We're dereferencing all of pointers.p2.
And we're going to assign that the value of that whatever that's pointing to to negative 97.
So notice the difference there.
We're not creating a structure pointer.
We're creating a regular variable pointer,
but it contains two elements that are pointers.
So if we want to assign to those two elements inside of the structure,
we have to de-reference them using the indirection operator,
and that's what we do there.
And then in our two print statements.
Again, we want to print out the values of what p1 is pointing to
so we de-reference it same with p2
and then we want to also print out
again the values what pointers is pointing to.
So in that first one we're saying i1
equals percent I comma pointers.p1
equals percent I.
So the first one inside the double quotes is just printing out that actual text,
it's not de-referencing anything after the comma is where we're doing the de-referencing.
We're first printing out i1 which is 100
and then printing out pointers p1 which would be 100.
I2 is actually going to be
negative 97 after we assign it to p2.
So what about character arrays or character pointers. In the previous,
lecture we talked about how you can create a character array as an element
inside of a pointer. But what about
character pointers,
should we do something like
first and last have character arrays or should we just do something like first
and last with character pointers as elements. What is the better option.
Well, we'll see. It's really again a notational thing but there are advantages to using the pointers.
So when we create pointers to characters inside of our elements,
it's usually a little bit better.
But you need to understand what's going on here when you create array element
char array as an element or a pointer as an element,
both are valid. But there's a big difference.
So if we say struct names veep
or vp and we say Talia and Summers
and then we say struct pointer name. So remember, struct names contains the character arrays.
Struct pointer names contains the pointers.
And we here are initializing the pointers equal to Brad and Fallingjaw.
And then we print them out we're going to print out the first
element, which is a character a and vp
and then the first element of treas, which is a pointer to first.
So the struct names variable veep.
What's happening here is the strings are stored inside the structure.
The structure has allocated a total of 40 bytes to hold the two names,
they're character arrays.
That's what's happening when we create character arrays inside of a pointer.
We're allocating 40 bytes to hold those two names,
20 and 20.
For the pointer names variable treas whether we have two pointers first and last,
these are strings that are stored wherever the compiler stores string constants.
The structure holds the two addresses
because they're pointers. Pointers contain, what, values that are addresses.
So all it's going to do is allocate two pointers inside that structure.
That's 16 bytes for most systems, pointers usually 8 bytes or 4 bytes.
The struct p name structure allocates no space to store the strings
because it's only allocating space for the pointers themselves.
Pointers have values that are addresses.
The address that it's pointing to there's no memory allocated for that.
So when you create those two pointers, there's no memory allocated for those two pointers.
It allocates no space.
So it can be used only with strings that have had space allocated for them elsewhere.
Whenever you have pointers inside of a structure,
you have to use malloc or calloc.
Otherwise, they could be stored wherever string constants is stored.
And that's the big difference. So the pointers and p name structure should be used
only to manage strings that were created and allocated elsewhere in the program.
You do not want to
assign data to that unless you have dynamically allocated it or
that has been allocated somewhere else,
maybe by an existing struct or something. But typically, you're going to want to malloc those pointers.
Again, very different.
You're allocating 40 bytes for these two character arrays inside the name struct.
For the p name struct, you have two pointers.
There's no memory allocated for that when you create a variable of that type.
It's just creating memory for the two character pointers, which is eight bytes each.
To actually allocate memory for what first and last are pointing to, you have to do a malloc.
So one instance in which it does make sense to use a pointer and a structure
is to handle strings
is if you are dynamically allocating that memory.
That's usually when you use it
because other permitted data types are automatically going to allocate the memory for you.
So you want to use a pointer to store the address.
This has the advantage that you can ask malloc to allocate just the amount of space that is needed for
a string. We talked about the advantages of dynamic memory allocation.
When you create things statically at compile time,
you're only going to create a specific number of bytes.
What happens if you need more bytes, you're screwed, your program's not going to work.
You have to go back and recompile. With dynamically allocated strings,
you can readjust, you can create just as much memory as you need,
which makes it much more efficient in the program.
So you're going to use malloc on any pointers created inside of a struct.
So in this case, you have a struct named name count.
You have two pointers, character pointers fname and last name.
And then you have an int, letters.
The two strings inside here are not stored in the structure.
We only create memory for the pointers, which contain addresses.
We want to create memory for fname and lname for what the pointers are pointing to.
So we want to store that in a chunk managed by dynamic memory location, which is using malloc.
The address of the two strings are what's stored, not what they're pointing to.
So addresses are what string handling functions typically work with,
which is good and that's why you want to use pointers in there
because then you don't have to that then you can just pass them around a lot easier.
So here's an example. We have a function called get info.
It takes a pointer.
And we can pass that pointer around very easily.
Now we also are going to
create a another
or for our pointer array that we're being passed to it,
we're going to allocate memory for it.
So you'll see that we create a character a called temp
and then we store some data in that temp.
We ask the user to get something. We're not familiar with the sgets,but it's the same as the gets saved scanf a little.
Then we allocate memory for fname inside that pointer
and we do that just like we allocate memory for any character. We call malloc,
we pass in the number of bytes.
In this case, the str length of 10 plus 1 for the null terminator.
And then remember, malloc turns a void star, so we cast it to a character star.
But this is critical right here. This malloc call is necessary for any structures that contain pointers.
Now we passed this pointer in and the pointer never had a memory allocated
for it.
So we're allocating memory inside here. We can then do our copy very easily.
We can copy temp to fname of our structure pst
that was passed in as a parameter.
And then we can have them enter their last name, we can malloc more memory for the last name
and then stored in the last name struct variable.
This is what you're going to need to do when you have pointers
to characters inside your structures, you have to do the malloc.
Because again, those pointers only store the address.
They allocate enough memory to store an address,
not enough memory to store a string.
So whatever that address is pointing to, you have to allocate memory for that
and you do it just like normal with the malloc,
the only difference is here you use the dash greater than sign
to access the actual element inside that struct, which is a character pointer,
so you can assign it to it and that's doing the de-referencing.
So you'll want to play around with this stuff. This is
very common for character pointers inside structs.
We're going to have a challenge where you're gonna need to do this. But play around with this code,
go inside code blocks, start allocating memory for
strings or character pointers inside of structs
and see how you then can use str copy to actually put data inside there,
but you first have to allocate the memory before you assign.
If you're doing constants like in the previous example who knows where that memory is allocated.
So again, a lot of important concepts presented in this lecture.
This idea of a structure that is a pointer
in this new
syntax where you can use the dash then greater than as opposed to
using the indirection operator, very nice.
And then actually having elements as pointers
and how you have to allocate memory for character pointers. Thank you.