Structs Flashcards

(27 cards)

1
Q

Thinking about Structs

A
  • A struct is like a class in Java
    – It aggregates multiple fields, each with its own name and type
    – Unlike Java, a struct only contains data, no methods
    – Unlike Java, all the fields of a struct are accessible to any code (it’s as if they are all public)
    struct Person {
    char name[ 10 ]; -> fields in struct(you ca’t initialize them here)
    double height;
    int age;
    }; -> its easy to forget the semicolon here
    Person -> Structure name
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
2
Q

Using Structs

A
  • You can create variables with your new
    structure type.
  • But, defining a new struct doesn’t
    automatically introduce a new type name.
    Person p1;
  • It introduces a structure name …
    – but you have to use struct keyword to talk about the structure’s type:
    struct Person p1;
  • Once you’ve made an instance, you can access
    its fields using the dot operator (just like in
    Java)
    p1.height = 1.75; ->You can store values in the fields.
    p1.age = 24;
    printf( “%d\n”, p1.age ); -> You can get values from the fields.
    double x = p1.height;
    p1.name = “Mary”; -> But you can’t do this. It’s not a struct problem; you just can’t
    assign between arrays like this.
  • So, you can’t assign to a string field like this:
    p1.name = “Mary”;
  • How could you initialize that name field?
    – You could copy the string yourself, one character at a time.
    – You could read something into it, using scanf()
    – You could use a library routine, like strcpy()
    – Or …
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
3
Q

Structure Initialization

A
  • You can initialize a struct instance’s fields
    when you declare it:
    struct Person p2 = { “William”, 1.85, 27 };
    In the same order as the struct definition.
    “William” -> This will do a deep copy of
    this string.
  • C99 Introduced a new, more explicit syntax:
    struct Person p3 = { .age = 33,
    .name = “Agatha”,
    .height = 1.7 };
  • As with arrays, storage class determines how a
    structs fields will be initialized.
  • If you use initialization syntax, any fields you
    don’t specify get initialized to zero.
    struct Person p4;
    printf( “age: %d\n”, p4.age ); I wonder what this will print. Nobody knows.
    struct Person p4 = { .height = 1.234 };
    printf( “age: %d\n”, p4.age );
    struct Person p5 = { “abc” };
    struct Person p6 = {};
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
4
Q

Combined Definition and Declaration

A
  • You can declare struct variables along with the struct definition.
    struct Grade {
    double minimum;
    char letter;
    } aGrade = { 90.0, ‘A’ }; -> See. That’s why we need this semicolon.
  • You don’t even need to give the structure a
    name, if you’ll never use it again.
    struct {
    double latitude;
    double longitude;
    } raleigh = { 35.7806, 78.6389 };
    Look at doc for visualization
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
5
Q

Struct Layout

A
  • But, your struct fields may not be laid out exactly like you expect.
    – They will be in the order you specify
    – … but, the compiler may introduce padding between some fields.
    – Why? Performance, and the hardware may have alignment requirements.
    struct Person {
    char name[ 10 ];
    double height;
    int age;
    };
    struct Person p1 = { … };
  • 2-byte alignment
    – On some systems, anything larger than a byte (short, int, etc.)
    may be stored starting at an even memory address.
  • 4-byte alignment
    – On some systems, anything larger than two bytes (int, float, etc.) may be stored starting at an address that’s a multiple of 4.
  • Natural alignment
    – Some systems store any primitive type of size n at an address that’s a multiple of n.
    short a;
    double b;
    int c;
    printf( “a: %p\n”, &a );
    printf( “b: %p\n”, &b );
    printf( “c: %p\n”, &c )
    a: 0x7ff7bfcbf8fe -> Look. I’m a
    multiple of 2.
    b: 0x7ff7bfcbf8f0 -> I’m a multiple of 8.
    c: 0x7ff7bfcbf8ec -> I’m a multiple of 4.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
6
Q

GCC Struct Layout

A
  • GCC aligns the start of the struct with a
    multiple of the largest primitive type it
    contains.
  • GCC naturally aligns each primitive type
    inside.
    Example on doc and sizeOf would return 32
  • The compiler won’t change the order of our
    fields, or their types.
  • But we can make these changes ourselves.
  • Choosing types can help to layout a struct
    without as much padding.
  • Or, arranging fields can help.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
7
Q

Structs and Value Semantics

A
  • Structs support value semantics
    – You can assign from one instance to another and you get a deep copy
    – You can pass and return by value when you call a function.
    struct Event {
    char name[ 10 ];
    int hour;
    int minute;
    };
    struct Event e1 = { “Wake Up”, 6, 30 };
    struct Event e2;
    e2 = e1;
    if ( e2 == e1 ) -> you can’t do this
    printf( “They’re the same!\n” );
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
8
Q

Structs and Pass-By-Value (Code Example)

A
  • So, we can define a function like this to print
    an event:
    void printEvent( struct Event e )
    {
    printf( “%s %d:%02d\n”, e.name,
    e.hour, e.minute );
    }
  • We can call it, passing in a copy of the event struct.
    printEvent( e1 );
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
9
Q

Structs and Pass By Reference

A
  • We can use pointers to pass the struct by
    reference instead.
    void printEvent( struct Event const e )
    {
    printf( “%s %d:%02d\n”, (
    e).name,
    (e).hour, (e).minute );
    }
  • We just need to pass the
    struct’s address.
    printEvent( &e1 );
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
10
Q

Field via Pointer

A
  • We usually end up passing around pointers to struct much more often than passing copies of struct instances.
    – So, we end up writing syntax like the following a lot:
    (*ptr).field
    – Or, we would if we didn’t have a shorthand:
    ptr->field
  • This would let us write the printEvent function like:
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
11
Q

Structs and Precedence

A
  • Our two new operators are in the highest
    precedence category.
    Operator Description
    ++ –
    ()
    []
    .
    ->
    Postincrement, postdecrement : e.g., a++
    Function call : e.g., f( 5 )
    Array index : e.g., a[ 5 ]
    Field from struct value: e.g., b.x
    Field from struct pointer: e.g., bptr->x
    ++ –
    + -
    !
    (type)
    *
    &
    sizeof
  • / % Multiply, divide, mod
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
12
Q

Returning Structs

A
  • Functions can return structs as their return
    values.
    e2 = readEvent();
    struct Event readEvent()
    {
    struct Event e;
    printf( “Event Name: “ );
    scanf( “%s”, e.name );
    printf( “Time: “ );
    scanf(“%d:%d”, &e.hour, &e.minute);
    return e;
    }
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
13
Q

Modifying Reference Parameters

A
  • Or, we could pass the struct by address and let the function modify it directly.
    readEvent( &e2 );
    void readEvent( struct Event *e )
    {
    printf( “Event Name: “ );
    scanf( “%s”, e->name );
    printf( “Time: “ );
    scanf( “%d:%d”, &e->hour, &e->minute );
    }
    readEvent(&e2);
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
14
Q

Bad Ideas

A
  • We can pass by value.
    – But it’s a bad idea to do that here. The function is
    just changing a copy of the caller’s struct.
    void readEvent( struct Event e )
    {
    printf( “Event Name: “ );
    scanf( “%s”, e.name );
    printf( “Time: “ );
    scanf( “%d:%d”, &e.hour, &e.minute );
    }
    readEvent( e2 );
  • We can return by address.
  • We can return by address.
    – But it’s a bad idea to do that here. We’re returning the address of a variable that’s going away.
    struct Event *readEvent()
    {
    struct Event e;
    printf( “Event Name: “ );
    scanf( “%s”, e.name );
    printf( “Time: “ );
    scanf( “%d:%d”, &e.hour, &e.minute );
    return &e;
    }
    struct Event *e3 = readEvent();
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
15
Q

Dynamically Allicating Structs

A
  • A struct instance occupies a contiguous block
    of memory.
    – That memory can come from any of our three storage regions, static, stack … or heap.
  • We just need to ask for the right amount.
  • Here’s how:
    struct Event *e =
    (struct Event *)malloc( sizeof( struct Event ) );
    Returning a New Struct
  • With dynamic allocation, we can fix return-byaddress.
    struct Event *e3 = readEvent();
    struct Event *readEvent()
    {
    struct Event *e = (struct Event *)
    malloc( sizeof( struct Event ) );
    printf( “Event Name: “ );
    scanf( “%s”, e->name );
    printf( “Time: “ );
    scanf( “%d:%d”, &(e->hour),
    &(e->minute) );
    return e;
    }
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
16
Q

Literal Struct Values

A
  • Notice, initialization syntax is special.
  • This means make me an array that contains
    exactly this string:
    char str1[] = “abc123”;
  • But this means just take a pointer to a literal
    string.
    char *str2 = “abc123”;
  • During declaration, we have a syntax for
    specifying initial array and struct values.
    int list[] = { 12, 15, 35 };
    struct Event evt = { “Concert”, 8, 0 };
  • But, outside initialization, this syntax is
    meaningless (well, at least it doesn’t mean
    what we want).
    f( { 12, 15, 35 } );
    e2 = { “Concert”, 8, 0 };
  • C99 gives us a way to use this syntax.
  • It looks like a type cast … but it’s not.
  • You’ve already seen this for arrays.
    f( ( int [] ) { 12, 15, 35 } ); -> This is a pointer
  • We can use this for structs also.
  • This is stack-allocated storage. You can take its address but it goes away when you leave the current scope.
17
Q

Nesting Structures and Arrays

A
  • You’ve seen, we can nest arrays inside structures.
  • Really, you have two choices
  • Put an array inside a structure
    – Array stored along with structure fields
    – Array length must be a constant expression (so, all instances have the same array size)
    struct Event {
    char name[ 10 ];
    int hour;
    int minute;
    };
    char letter = e.name[ 0 ];
  • Or, store a pointer inside a structure
    – Pointing to the array contents
    – You can still index using the same syntax
    – Array size can vary from instance to instance
    – But, you have to find memory elsewhere to
    store the array.
    – This is more like how Java does it.
18
Q

Arrays of Structs

A

Arrays of Structs
* You can store structures as array
elements.
struct Event schedule[] = {
{ “Wake up”, 6, 30 },
{ “Breakfast”, 7, 0 },
{ “OS Class”, 9, 35 },
{ .hour = 11, .minute = 0, .name = “Meeting” },
[ 5 ] = { “C Class”, 11, 45 }
};
* To get to an instance, we index into the
array
* To get to one of its fields, we use dot.
char *ename = schedule[2].name;
double etime = schedule[3].hour +
schedule[3].minute/60.0 ;

19
Q

Arrays of Struct Instances

A
  • We’ve been allocating these arrays on
    the stack (or, statically).
  • Alternatively, we could dynamically
    allocate them
    struct Event *schedule =
    (struct Event *) malloc(6 * sizeof(struct Event));
    Pointer to the start of the array.
    Number of items times size of each item.
    This memory will be uninitialized.
20
Q

Arrays of Pointers to Structs

A
  • We can make an array of pointers to structs
    struct Event **schedule = (struct Event **) malloc(6 * sizeof( struct Event *));
    struct Event **schedule -> Array of (uninitialized) pointers.
    malloc(6 * sizeof( struct Event *)); -> Number of pointers times size of each pointer.
  • Allocate space for each instance.
    struct Event **schedule = (struct Event **) malloc(6 * sizeof( struct Event *));
    schedule[ 0 ] = (struct Event *) malloc( sizeof( struct Event ) );
  • Fill in pointers as you make more instances.
21
Q

Nesting Structs(Different Structs)

A
  • You can put a struct inside another struct.
    struct Time {
    int hour;
    int minute;
    };
    struct Event {
    char name[ 10 ];
    struct Time start;
    struct Time end;
    };
    struct Event evt = { “Breakfast”,
    { 7, 30 },
    { 7, 50 }
    };
    evt.end.minute = 5
    A nested structure initialization.
    To get to an element, you need a field inside a field.
22
Q

Arrays of Pointers to Structs(fields)

A
  • Fill in the fields of your struct.
    struct Event **schedule = (struct Event **) malloc(6 * sizeof( struct Event *));
    schedule[ 0 ] = (struct Event *) malloc( sizeof( struct Event ) );
    schedule[ 0 ]->hour = 2;
    schedule[ 0 ]->minute = 30;
    strcpy( schedule[ 0 ]->name, “take nap” );
  • Just need a little more memory for each struct instance.
    schedule[ len ] = (struct Event *) malloc( sizeof( struct Event ) );
    *schedule[ len ] = (struct Event){ ”take walk”, 4, 20 };
    len++;
23
Q

Meet a New Friend, typedef

A
  • See, we can make long, complicated type names in C … and understand them.
  • But we don’t always want to; sometimes its nice to have a short name
  • And, we can, using typedef
    – Use typedef just before something that looks like a variable declaration.
    – You’re not declaring a variable; you’re introducing a new type name.
    typedef int Table[ 10 ][ 10 ];
    Table tbl1, tbl2;
24
Q

Why use typedef?

A
  • Typedef lets us isolate platform dependencies.
    typedef long int64;
  • We could put this in one header:
  • Everywhere else, we can just use our type name:
    int64 counter = 0;
  • If we change platforms, we only need to update
    the header to reflect platform differences.
  • Typedef lets us simplify naming and thinking
    about complex types.
  • Here’s a pointer to a function.
    typedef bool (*TestFunctionPtr)( int );
  • Here’s an array of those pointers.
    TestFunctionPtr testList[ 10 ];
  • This can help to reduce the zig-zagging when
    reading a type name.
25
Typedef and 2D Arrays
* This can also simplify describing multi- dimensional arrays. typedef int Row[ 20 ]; Row *table; I’m a pointer to a Row, so you can use me like an array of Rows. table = (Row *) malloc( 50 * sizeof( Row ) ); -> Now, I’m an array of 50 rows. * We could do without this typedef, but it make the types harder to describe. int (*table)[ 20 ]; table = (int (*)[20]) malloc( 50 * 20* sizoef( int ) );
26
Typedef and Structs
* Typedef lets us create type names for structure types. struct EventStruct { char name[ 10 ]; int hour, minute; }; typedef struct EventStruct Event; Event evt1 = { “Nap”, 2, 5 } It’s OK to declare two fields at once Now, I can use Event like a regular type name * This is a common practice for structs.. * You can even give a type name for an otherwise anonymous struct. typedef struct { char name[ 10 ]; int hour, minute; } Event; Event evt1 = { “Nap”, 2, 5 }; * This has some disadvantages, so it’s less common.
27
Type def changes Nothing
* Typedef doesn’t create a new type * It just gives you a new name for a type. * This is good. typedef struct EventStruct { char name[ 10 ]; int hour, minute; } Event; struct EventStruct evt1 = { “Study”, 2, 5 }; Event evt2 = { “Nap”, 2, 5 }; evt2 = evt1;