>> [MUSIC PLAYING]
>> DOUG LLOYD: OK.
We've worked with integers,
we've worked with characters,
we've worked floats,
doubles, strings, and bools.
We've exhausted pretty much all
of the [INAUDIBLE] types that
have been available to us all along.
But now we want to do something more.
How do we do that?
How do we create different data types?
We can do so by using structures.
So structures allow us to unify
variables of different types
into a single, new variable type,
which we can assign its own type name.
This is a really strong
thing to be able to do,
because we can now group
elements of different data types
together that have a logical connection.
We've been able to do this
with arrays sort of, right?
We can group variables
of the same data type
together in a large unit
of memory, an array.
>> But we haven't been able to mix
up different data types together.
We can't, say, pair an integer,
and a character, and a double all
in the same thing and
call that a single unit.
But with structures, or
frequently referred to as structs,
we actually can.
So a structure is sort
of like a super variable.
It's a variable that contains
other variables inside of it.
So here's an example of
a very simple structure.
This is what the syntax would look
like to create a structure for a car.
Now, let's go through the syntax here.
Struct, that's the
keyword that indicates
that I'm creating a new data type here.
In particular, the data type's name is
going to be struct car, as we'll see.
But this is the sort of tip
off to the compiler that this
as a group of variables that is going
to be considered part of the same type
in a minute.
>> Cars, just the name of the structure.
Again, the data type here is going
to be struct car, not just car.
But if you have different--
if you create multiple structs
in the same program, you need to
distinguish between struct and struct.
So struct car, I might also have
struct student, for example,
in the same program.
Inside of the curly braces are
all of the so-called fields,
or members of the structure.
So what are some of the things
that are inherent in a car?
Well, it usually has a year, has
a model name, and a license plate,
an odometer that usually has
some number of miles on it,
and maybe an engine size.
And as you can see, I'm mixing up
integers and characters and doubles.
They're all going to be
part of this new data type.
>> Lastly, the final thing I need to do,
don't forget this little semicolon
at the end.
After we finish defining the structure,
we need to put a semicolon at the end.
It's a very common syntactical
mistake, because with a function,
for example, you would just have
open curly brace, close curly brace.
You don't put a semicolon at the
end of a function definition.
This looks like a function
definition, but it's not,
and so the semicolon there
is just a reminder that you
need to put it there, because
the compiler will otherwise not
know what to do with it.
It's a very common error
to accidentally make
when you're first defining structures.
>> OK.
So we usually define our structures
at the very top of our programs
because they're probably going
to be used by multiple functions.
We don't want to define a
struct inside of a function,
because then we can only-- the
scope of the structure really
only exists inside of that function.
We'd probably want to define a structure
so we can use it in multiple functions,
or perhaps in multiple
files that are tied together
to create our single program.
Sometimes also instead
of defining the structure
at the very top where you put your
pound includes and your pound defines,
for example, you might put them in
separate dot h files, which you then
pound include yourself.
>> So we have structures, but now
we need to get inside of them.
How do we get inside of
a structure to access
those sub-variables, those variables
that exist inside the structure?
Well, we have something called
the dot operator, which allows us
to access the fields of the structure.
So for example, let's say I've declared
my structure data type somewhere
at the top of my program, or perhaps in
a dot h file that I've pound included.
If I then want to create a new
variable of that data type, I can say,
struct car, my car, semicolon.
Just like I could say int
x, or string name semicolon.
>> The data type here is struct car,
the name of the variable is my car,
and then I can use the dot operator to
access the various fields of my car.
So I can say my car
dot year equals 2011.
That's perfectly fine.
Year, if you recall, was defined as an
integer field inside of this struct car
data type.
So any variable of the struct car data
type, such as my car, I can say my car
dot year equals and then assign
it some integer value, 2011.
My car dot plate equals CS50.
My card dot odometer
equals 50505 semicolon.
All of those are
perfectly fine and that's
how we access the
fields of the structure.
Structures, though, do not need
to be created on the stack.
Just like any other variable, we
can dynamically allocate them.
If we have a program that might
be generating many structures,
we don't know how many
we're going to need,
then we need to dynamically
allocate those structures
as our program is running.
And so if we're going to access the
fields of a structure in that context,
recall that we first need to dereference
the pointer to the structure,
and then once we dereference the
pointer, then we can access the fields.
If we only have a
pointer to the structure,
we can't just say pointer dot field
name and get what we're looking for.
There's the extra step of dereferencing.
So let's say that
instead of the previous--
just like the previous example,
instead of declaring it
on the stack, struct car, my
car, semicolon, I say struct car,
star, a pointer to a
struct car called my car,
equals malloc size of struct car.
Size of we'll figure out how many
bytes your new data type takes up.
You don't necessarily only need to use
size of, width, int, or char, or any
of the built-in data types.
The compiler is smart enough
to figure out how many bytes
are required by your new structure.
So I malloc myself a unit of memory
big enough to hold a struct car,
and I get a pointer back
to that block of memory,
and that pointer is assigned to my car.
>> Now, if I want to access
the fields of my car,
I first dereference my car using
the dereference operator, star
that we've seen from the pointers
videos, and then after I dereference,
then I can use the dot operator to
access the various fields of my car.
Star my car dot year equals 2011.
That would have the effect
we want in this case,
because we've dynamically
allocated my car.
>> That's kind of annoying, though, right?
There's a 2-step process now.
Now we have to dereference--
we have a star operator,
and we have a dot operator.
And as you might expect, because
C programmers love shorter ways
to do things, there is a
shorter way to do this.
There is another operator called arrow,
which makes this process a lot easier.
The way arrow works is
it first dereferences
the pointer on the left
side of the operator,
and then, after having dereferenced
the pointer on the left,
it accesses the field on the right.
And so previously we had this sort
of star my car dot all this stuff,
like there was a lot going on there.
But what we can instead do is this--
my car arrow year equals 2011.
>> Again, what's happening here?
First, I'm dereferencing my car.
Which again, is a pointer here.
Then, after having
dereferenced my car, I
can then access the fields
year, plate, and odometer
just as I could before having first
used star to dereference my car,
and dot to access the field.
So you can have structures, you
can have pointers to structures,
and you have ways to access
the fields of those structures,
whether you have pointers to
them or the variables themselves.
Dot or arrow, depending on
how the variable was declared.
I'm Doug Lloyd, this is CS50.
